CVE-2012-1876
目录
分析环境
| 名称 | 版本 | |
|---|---|---|
| 操作系统 | Windows7 32位 | 简体中文版 |
| 虚拟机 | VMware Workstations 15 pro | 15.0.2 build-10952284 |
| 反汇编器 | IDA Pro | 6.8 |
| 漏洞软件 | Internet Explorer8 | 8.0.7601.17514 |
| 调试器 | WinDbg | 6.12 |
背景知识
漏洞简述
- 这是一个IE浏览器的漏洞,成功利用可实现远程代码执行,错误出在
mshtml.dll模块中的CTableLayout::CalculateMinMax函数里,程序在执行时会以HTML代码中的<col>元素的span属性作为循环控制次数向堆空间写入数据,如果此span值设置的不当,那么就会引发堆溢出问题。 mshtml.dll模块是IE中的重要组件,它用来解析页面中的HTML和CSS,后续的分析也主要集中在此模块中;如下列出了IE中的主要组件,进一步参考:

漏洞PoC
将通过如下的PoC代码来进行梳理:
1 | <html> |
- 上述代码功能:最开始创建时
span的属性值为6,而后通过js中的over_trigger()函数将其动态更新为666(这个可以随意选,只要能保证溢出就可以了)。另外,width的属性值和写入堆空间的内容有关,后面还会再说。 - 首先将PoC保存为html文件并用IE打开,会弹出阻止提示,此时用WinDbg附加IE进程,附加列表中会有两个IE进程,一个是broker进程,一个是页面内容进程,选择后面的也就是当前选项卡对应的子进程;

- 通过
.reload /f命令强制加载符号命令,lm命令查看加载结果,并且设置三个断点;CTableLayout::CalculateMinMax函数中,所以这个地方肯定要下个断点;- 因为是堆溢出,所以
_HeapRealloc函数也来个断点; - 最后的
CTableCol::GetAAspan函数是用来获取span属性值的,1和2两个断点目前暂时禁用;

- g命令跑起来,在IE中允许阻止的内容,弹出警告直接确定,回到WinDbg可以看到程序第一次在
CTableLayout::CalculateMinMax函数入口断下来了,这是处理最开始创建时span值为6的情况,查看调用栈;

- 再IDA中
CTableLayout::CalculateMinMax函数的声明:
1 | void __thiscall CTableLayout::CalculateMinMax(CTableLayout *theTableLayoutObj, LPVOID lpUnknownStackBuffer); |
- 重点关注CTableLayout这个变量,它是一个指针,由上面的kb命令可知其值为
05db7ea8;

可见参数1引用的是CTableLayout对象,也就是<table>标签在内存中的对象;
- 程序申请了堆空间用于保存column的样式信息,每个样式信息占0x1C(28个)字节,有多少个样式信息由span属性值来确定,因此这里申请的堆空间大小为
0x1C*6=0xA8,即_HeapRealloc函数入口断下后ecx寄存器的值,函数调用时的入参如果用到寄存器的话一般都是ecx,返回参数一般保存在eax中,同时注意随后分配的初始地址会保存到esi寄存器对应的地址处,可以看到此时的值由NULL变为0x049c7f58。

- 继续运行程序会在
CTableCol::GetAAspan处断下来,也就是获取span值作为写入样式信息时循环的控制次数,函数返回结果保存在eax中,此时的值为6。

- 再来看下程序向申请的堆空间写入样式信息的过程,我们在起始地址处下个写入断点。

- 从PoC中可以看到此时对应的width属性值为41,
0x049c7f58处写入的内容就为width值41*100=0x00001004,事实上程序断下来的时候0x1C个字节的样式信息都已写入完成。我们再单步往下跟一下。

- 可以看到出现了inc+cmp组合,可以猜想这应该就是控制堆空间写入样式信息的循环了,为了验证这个猜想多跟几次这个过程,可以发现事实确是如此。
- 这几条汇编指令的意思就是ebp-14h对应的值每次加1,即每次循环后递增;
- 然后ebp-24h对应的值每次加0x1C,即每次加一个样式信息的字节数;
- 最后当前的循环次数和ebp+10h对应的值比较,即span属性值;
- 接来看下程序通过
js脚本动态更新span属性值后,也就是span值变为666时程序第二次在CTableLayout::CalculateMinMax函数入口断下后是个什么情形,理论上是要重新分配堆空间的,毕竟要多写入660个样式信息,而后再获取此时的span值作为循环控制次数,最后才向堆空间写入样式信息。我们来到程序此时断下来的地方,顺便看下之前确实是写入了6个样式信息。

- 如果程序继续往下应该是要分配堆空间了,但我们却发现程序跳过了分配堆空间的过程,错误认为之前分配的空间已经足够而转去直接获取控制循环次数的span属性值eax,即
0x29a=666。

- 接下来和前面一样是写入样式信息的过程,不过这次是对只能容纳6个样式信息的堆空间写入了666个样式信息,从而引发了堆溢出错误。


- 可以看到
ebp+10h对应此时的span属性值0x29a,所以程序最终将会执行666次循环。堆溢出发生后程序继续运行会造成内存访问违规,从而导致IE浏览器的崩溃。

- 崩溃点确认,向edi指向的内存里拷贝值导致crash,同时看到edi来源于esi,但是esi的处理代码并不在这个函数中,所以回溯到上一个函数

漏洞利用
未完待续