0x01 2012 Pwn2Own 黑客大赛
- Pwn2Own 是世界上最著名的黑客大赛,意在激励白帽黑客们进行顶尖的安全研究。在 2012 年 Pwn2Own 大赛上,来自法国著名的安全团队 Vupen 利用两个 0day 漏洞,成功攻破了 Window7 下的 IE9 浏览器,其中一个是绕过 IE 安全沙盘的漏洞,另外一个就是编号为 CVE-2012-1876 的堆溢出漏洞
- 一般 Pwn2Own 大赛用于攻破厂商的 0day 漏洞质量都是非常高的,而且漏洞利用也构造的极为精妙,具有很大的学习价值。CVE-2012-1876 漏洞的成因是 MSHTML.DLL 中处理 table 元素的 CaculateMinMax 函数没有对 span 属性大小做限制,导致以 span 的大小作为循环计数,能够将相对可控的字节写到堆空间之外,在开启堆页的情况下会导致异常
- 这个就是触发漏洞的 POC 样本,看起来非常简单,首先 span 的值为 1,之后调用过 over_trigger 函数之后 span 的值变为 1000,造成溢出
0x02 分析环境
虚拟机:Windows7
漏洞软件:IE8
分析工具:Windbg + IDA
POC 样本:exploit.html + poc.html(提取码:wof6)
0x03 调试分析漏洞成因
- 在 Debugger Tools for Windows 目录下使用 gflag.exe -i IExplore.exe +hpa -htc 开启堆页保护,目的是在堆溢出时能够断下,便于调试
- 开启 windbg 后使用 .childdbg 1 命令开启多线程调试(因为在 IE 读取样本 POC 时会多开一个线程去处理),载入 IE 运行后拖入 POC,之后选择允许执行控件之后会断下,这个是一个很简单的汇编指令,将 ecx 中的内容复制到 edi 的地址,很显然 0x0a902018 这个地址触发了堆页保护异常
- 通过查看栈信息,可以看出在 mshtml!CTableLayout::CaculateMinMax 在相对偏移 0x52f 的地方调用了 mshtml!CTableColCalc::AdjustForCal 函数,而刚才触发的异常就在这个函数里面
- 根据栈溯源在结合 IDA 分析可以很清楚看出(在 Windows7 非管理员账户启动 IDA 分析系统 dll,需要以管理员账户启动 IDA)
- 漏洞点异常的原因知道了,那么函数是怎么调用的呢,经过多次调试分析,在 IE8 加载 POC 之后的流程图是这样的
- 选择控件之后会第二次进入 CaculatcMinMax 函数,下面来调试看看
- 首先这个取传入 CaculateMinMax 的第一个参数放入 ebx,第一个参数是 CTableLayout::vftable 对象也就是 table 元素在内存中的对象
- 继续向下调试,对象偏移 54h 保存着 table 元素的大小
- 之后取 CAryKeyValues::vftalbe 的对象存放在 [ebp-84h] 中,继续向下调试
- 这里对 EnsureSizeWorker 函数下断点,运行两次后到此处。EnsureSizeWorker 这个函数的功能是分配堆空间供函数使用,并且将分配过的堆空间首地址存放在 edi+c 中,此时分配的堆空间首地址为 0x0c4b9f90
- 分配完堆空间之后,会取 span 属性的大小放入 eax 中,此时还没有调用 over_trigger 函数,所以此时 span 的值为 1
- 断下 GetAAspan 函数,运行后再次到达这个函数,此时 span 属性的值已经变为 1000(3e8),和 3e8 作比较说明 span 的值最大不能超过 1000
- 之后调用 GetPixelWidth 函数取 width ,经计算后将值的地址存储在 eax 中(这里会将 width * 100 + 9)
- 之后执行 AdjustForCol,可以看到 esi 保存着分配的堆空间的首地址,而 0x098d6f80 中储存着计算过的 width 的值(42765 * 100 + 9)
- 进入异常触发点,此时 edi 的值就为 0x0c4b9f90 + 18h = 0x0c4b9fa8
- 这时查询一下位于 0c4b9fa8 的堆空间信息,发现大小为 70h
- 在向下调试后发现 ebp-20h 为循环计数,初始化为 0,每次循环加 1Ch,ebp+10h 储存着 span 的值(1000),只要 ebp-20h 不大于 ebp+10h,循环就会继续
- 之后堆空间首地址会在每次循环后加上 1Ch,有点复杂,所以直接看 IDA。在每次循环结束后,循环计数 var_20 每次会加 1Ch,而 esi 控制着堆空间的指针,而 esi 由 eax 传入,eax 为堆空间的首地址,ecx 为循环计数 var_20。所以也就是为什么堆空间的指针会每次循环后加上 1Ch
- 所以在第四次次循环后,将堆指针偏移 18 + 4*1C = 88h 的大小,就超出了 70h 的堆空间大小,导致堆页访问异常(0x0c4b9f90 + 88h = 0x0c4ba018)
0x04 漏洞利用
- 首先别忘了关堆页保护,不然就卡在那了
- 这个就是 exploit.html 的样本 ,循环构造了 3 个 0x100 大小的结构 + 1 个 button 对象
- 之后构造了 132 个 table 元素,span 的值为 9,width 的值为 41
- 如下图所示,释放 EEEE… 堆块就是为了能将漏洞堆块插入,然后控制 span 的大小修改 button 对象的头部信息,等再次调用 button 对象时,便能够控制虚表指针,执行任意位置的代码
- 但漏洞堆块真的这么巧的插入精心构造的堆块当中吗,答案是肯定的,而且在 Windows7+IE8 的环境下成功几率几乎为 100%,下面来验证一下
- 已知 javascript 调用 CollectGarbage() 释放堆空间时会调用 ntdll.dll 中的 RtlFreeHeap 函数,而刚刚创建漏洞堆块调用的函数是 EnsureSizeWorker,所以下这两个断点,第一个断点是释放堆块的首地址 free heap,第二个断点是漏洞堆块的首地址 vulheap
>>> bu ntdll!RtlFreeHeap ".echo free heap;db poi(esp+c) l10;g"
>>> bu mshtml!CTableLayout::CalculateMinMax+0x16d ".echo vulheap;dd poi(ebx+9c) l4;g" - 断下这两个断点之后,使用 .openlog + log存放地址 命令将打印出来的值存放在文件中
注:这两个断点的意思是,在到达指定位置后,打印出当前的信息,比如 esp+c,就是打印出 esp+c 的值
- 运行完后,查看 log 信息,首先查找对后一个 vulheap,复制地址往上搜索
- 成功找到释放的堆块,表明漏洞堆块确实已经插入了进入精心构造的堆空间当中
- 之后会将第 132 个 table 元素的 span 值改为 19,照成第一次溢出,目的之通过信息泄露获取 mshtml 的基址来构造 ROP(返回导向编程),目的是绕过 ASLR 和 DEP 保护,由于篇幅原因不再多述
这里为什么要更改第 132 个 table 元素的值,而且上面为什么构造 132 个 table 元素呢,很简单这里是为了加大插入的概率,也就是说前面的 131 个 table 元素只是给第 132 个 table 元素加大插入概率的
- 这个就是用于构造 ROP 的指令
- 以及利用 Heap Spray 将 shellcode 喷射到指定地址
- 最后将第 132 个 table 元素的 span 的值改为 29 并且将 width 的值改为 0x615145,造成第二次溢出,这次溢出会覆盖掉 button 对象的头部,目的是控制虚表指针
- 接着上面查询 vulheap 堆信息
- 可以看到 button 对象的头 4 个字节的信息已经更改为计算过后的 width 的值
注:值得注意的是这个计算过的 width 值与没有计算过的 width 值是 125 倍的关系,就是 615145 * 125 = 0x04954bc5,与上面的 width *100+9 不同
- 之后再调用 button 对象时就会触发虚表指针,那么 eax 就是 button 对象的头 4 个字节 0x04954bc5
- 因为数据太多,所以无法在调用虚表指针时断下,所以下这两个断点记录 eax 和 ecx 的值
bu mshtml!NotifyElement+0x35 “.echo EAX;r eax ;g”
bu mshtml!NotifyElement+0x35 ".echo ECX;r ecx ;g" - 重新运行之后,可以看到 eax 的值确实是 0x04954bc5,之后就会 call [0x04954bc5 + 8],最后只需要将 shellcode 喷射到此地址便可执行任意代码,也达到了完美利用漏洞的目的
0x05 后续…
- CVE-2012-1876 作为能攻下 Windows7 下 IE8 的漏洞,质量果然是非常的高,无论是漏洞分析还是漏洞利用,都体现出 Vupen 安全团队的顶尖技术水准;由于 IE8 的版本不一样可能导致一些参数也不一样,本文利用到的 exploit.html 也是做了一些修改才能达到精准的溢出效果
对 CVE-2012-1876 的分析到此结束,如有错误,欢迎指正(天哪终于分析完了)