0x00 前述
最初是在360安全客的这篇文章中了解到这个漏洞,没有打算深入进去。今天看到安全客做的漏洞分析,感觉蛮有意思,就找到Github上的PoC,简单搭了个环境准备试一试。成功弹出了计算器,正好来学习一下Windows下的漏洞调试。
注:本文参照安全客的文章,记录自己的调试过程。感谢安全客。
0x01 漏洞背景
漏洞出在EQNEDT32.exe上。
“据悉,这个组件是由Design Science Inc.开发的,后来由微软收购。该组件于2001年编译嵌入Office,之后没有任何进一步的修改。所以该漏洞已存在17年之久。影响现阶段流行的所有Office版本。”
“EQNEDT32.EXE用于在文档中插入和编辑方程式。插入到文档中的任何公式都是OLE对象。该组件是在OLE技术规范下设计的。首发于Microsoft Office 2000和Microsoft 2003。从Microsoft Office 2007套件开始,显示和编辑方程的方法发生了变化,虽然EQNEDT32.EXE变得过时,不过,为了保持版本兼容性,它并没有从Office套件中删除。”
“EQNEDT32.EXE为OLE实现了一组标准的COM接口。
IOleObject
IDataObject
IOleInPlaceObject
IOleInPlaceActiveObject
IpersistStorage
而问题就在于IpersistStorage:Load这个位置。因为历史久远,该组件开发的时候并没有例如ASLR这样的漏洞缓解措施。利用起来更加的方便。”
以上引号内部分摘自360安全客。
我们看一下本次实验环境下EQNEDT32.exe的程序详细信息:

这里顺带了解一下Windows下的漏洞缓解措施(一直很好奇):
DEP
关于查看及修改DEP设置,可以参考这篇文章。
查看:任务管理器 -> 查看 -> 选择列 -> 勾选数据执行保护
修改&查看全局设置:控制面板 -> 系统 -> 高级系统设置 -> 设置 -> 数据执行保护
ASLR
Vista以后的Windows系统才有ASLR。参考这篇文章来看如何关闭系统全局ASLR(未测试过):
// 关闭
[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Memory Management]
"MoveImages"=dword:00000000
// 开启
[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Memory Management]
"MoveImages"=-
使用PE Explorer可以查看程序本身是否有ASLR,即查看Dll Characteristics项中有没有0x40:

可以看到本次将要调试的EQNEDT32.exe确实没有开启ASLR。
2017/11/24更新:检查PE文件防御机制的工具
在读这篇文章时,发现了一款与Linux下用于检查ELF文件漏洞缓解机制的checksec.sh功能相似的用于检查Windows下PE文件漏洞缓解机制的PESecurity,可以到作者的Github上获得。有趣的是,checksec.sh使用bash脚本语言编写,PESecurity使用PowerShell编写。
我们以EQNEDT32.exe为对象做一下测试:
# in cmd
git clone https://github.com/NetSPI/PESecurity.git
cd PESecurity
powershell
# now in powershell
PS > Import-Module .\Get-PESecurity.psm1
PS >

可以发现所有的防护措施都没有打开。
PESecurity可以用于研究学习。当然,其准确度也有待考证。
0x02 环境搭建
%System Info%
msinfo32
得到系统信息如下:

Office信息如下:

0x03 PoC复现
从这里下载PoC,运行example/exploit.rtf,弹出计算器:

0x04 漏洞调试
① 搜索C盘找到EQNEDT.exe
C:\Program Files\Common Files\Microsoft Shared\EQUATION\EQUATION32.exe
② 使用IDA Pro
找到sub_41160F函数,果然在strcpy时没有检查长度(在大量的函数中寻找可能的漏洞点,猜测漏洞发现者应该是使用Fuzzing或者对敏感函数批量搜索等方法找到的):

③ 使用Ollydbg跟到sub_41160F函数
Ctrl + g输入0x41160f,转到地址后F4执行到这里。根据函数参数压栈规则,第一个参数a1(使用IDA Pro中函数参数名,下同)最后被压栈。所以在进入sub_41160F时栈上返回地址再往上一个空间存储的即为a1参数,是一个指针,为0018FAE0。右键在数据窗口中转到这个地方,可以看到宋体字样。所以这个参数的含义应该是字体名。参照下图:

④ 使用rtfobj
pip install oletools
使用rtfobj工具从PoCexploit.rtf中提取ole对象:

可以看到,成功提取:

2017/11/24更新
用HxD看一下提取出来的东西,可以大概找到溢出弹计算器的位置:

基本上shellcode就是
"cmd.exe /c calc.exe " + "AAAAAAAAAAAAAAAAAAAAAAAA" + 0x430C12
前面我们知道了溢出发生在sub_41160F函数的字体名参数。现在的问题是,我们怎么知道shellcode在OLE对象中的偏移?换句话说,如果我们想用自己的shellcode,应该放到哪里?为什么这个PoC里是在0x926这个位置?这应该和ole对象的数据结构有关系。
之前安装的oletools中有olebrowser工具,它依赖python-tkinter库。Windows下不太好操作,我们到Linux下看一下:

在查资料时有印象,ole对象是按stream来存储的。安全客上提到:
“EQNEDT32.EXE为OLE实现了一组标准的COM接口,问题就在于IpersistStorage:Load这个位置。”
查了一下IpersistStorage:Load,似乎是涉及到stream的加载。那么对于EQNEDT32.exe来说就是加载图中的Equation Native流了。进入看一下:

果然这就是shellcode的位置。不过为什么字体名参数在那个位置?也许这与EQNEDT32.exe自己定义的数据结构有关?暂未找到更多资料,希望知道的朋友教我一下。
上面shellcode中的A...是为了让0x430C12覆盖返回地址而做的填充。那么0x430C12是什么?

是WinExec!
那么当sub_41160F结束准备返回时,其返回地址已经变成了WinExec。同时此时栈上的参数恰好是shellcode字符串地址,因为进入sub_41160F时字体名参数刚好是最后一个压入栈的!这个溢出很有意思。
0x05 前辈的分析
11月26日在安全客上看到银雁冰前辈的“【漏洞分析】CVE-2017-11882漏洞分析、利用及动态检测”,写得真好。我还是要虚心学习。