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.EXEOLE实现了一组标准的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.EXEOLE实现了一组标准的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漏洞分析、利用及动态检测”,写得真好。我还是要虚心学习。

0x06 参考