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漏洞分析、利用及动态检测”,写得真好。我还是要虚心学习。