目录

  1. 分析环境
  2. 背景知识
  3. 漏洞简述
  4. 漏洞PoC
  5. 漏洞利用

分析环境

名称 版本
操作系统 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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<html>  
<body>
<table style="table-layout:fixed" >
<col id="132" width="41" span="6" > </col>
</table>

<script>

function over_trigger() {
var obj_col = document.getElementById("132");
obj_col.width = "42765";
obj_col.span = 666;
}

setTimeout("over_trigger();",1);

</script>
</body>
</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属性值为410x049c7f58处写入的内容就为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的处理代码并不在这个函数中,所以回溯到上一个函数

漏洞利用

未完待续