RSS Feed
更好更安全的互联网

CVE-2015-1641 Word 利用样本分析

2017-07-10

作者:知道创宇404实验室

0 引子

本文我们将通过一个恶意文档的分析来理解漏洞 CVE-2015-1641(MS15-033)的具体利用过程,以此还原它在现实攻击中的应用。就目前来看,虽然该 Office 漏洞早被修复,但由于其受影响版本多且稳定性良好,相关利用在坊间依旧比较常见,因此作为案例来学习还是很不错的。

1 样本信息

分析中用到的样本信息如下:

和大多数情形一样,漏洞的利用是借助嵌入OLE对象来实现的,我们可由 oletools 工具包中的rtfobj.py进行查看:

图0  借助 rtfobj.py 分析样本

这里我们先对这些嵌入对象做个简要介绍,详细的分析见后文。其中otkloadr.WRAssembly.1为ProgID,用于加载 OTKLOADR.DLL 模块,从而引入 MSVCR71.DLL 模块来绕过 ASLR 保护。而剩下的3个对象均为 Word
文档,我们可分别对它们进行提取,id为1的文档用来进行堆喷布局,id 为2的文档用来触发漏洞利用,id 为3的文档作用未知,样本中余下的数据为异或加密后的 shellcode、恶意程序以及最终呈现给用户的 Word 文档。

此外,由于 rtf 文档在格式上组织起来比较简单,有时为了调试的方便,我们可以仅抽取样本中的部分对象数据进行分析。若无特殊说明,文中的分析环境均为 Win7 x86+Office 2007(wwlib.dll的版本号为12.0.4518.1014)。

2 漏洞原理分析

下面我们来大致看下漏洞的原理,通过rtfobj.py提取上述 id 为2的 Word 文档,将其后缀改为 zip 后解压,可在document.xml文件中找到如下的 XML 片段,红色标注部分即样本实现利用的关键所在:

图1  引起类型混淆的 smartTag 标签

简单来说,此漏洞是由于 wwlib.dll 模块在处理标签内容时存在的类型混淆错误而造成的任意内存写,即用于处理 customXml 标签的代码没有进行严格的类型检查,导致其错误处理了 smartTag 标签中的内容。

我们来具体跟下,首先将样本中id为2的这部分内容手动抽取(非rtfobj.py提取)出来另存为一个rtf文档,然后作为winword.exe的打开参数载入 WinDbg,直接运行可以看到程序在如下位置处崩溃了,注意此时
ecx 寄存器的值对应第一个 smartTag 标签中的 element 值:

图2  程序的崩溃点

我们在上述崩溃点下条件断点,同时将 id 为 0 的内容也添加到该 rtf 文档中,重新载入 WinDbg。单步往下跟可以来到如下计算待写入内存地址的函数,可以看到该内存地址是根据 smartTag 标签中的 element 值计算出来的:

图3  计算待写入的内存地址

而后程序会调用 memcpy 函数向待写入内存进行数据拷贝,拷贝的内容即为 moveFromRange* 标签的 id 值,因此通过控制上述 smartTag 标签的两个特定值能实现任意内存地址写入,样本中的这几个值都是精心构造的:

图4  向待写入内存地址写入特定数据

针对该漏洞的补丁如下图所示,为了尽可能减少不相关因素的影响,这里比对的 wwlib.dll 版本号分别为12.0.6718.5000 和 12.0.6720.5000。可以看出,在处理 customXml 标签的代码中多了一个条件判断:

图5  补丁前后的比对结果

如果存在类型混淆的情况,那么该条件是不会满足的,即相应的处理函数不一致,也就不会对样本中的
smartTag 标签内容进行处理了:

图6  补丁后原漏洞点的执行流程

3 漏洞利用分析

3.1 执行流控制

接着我们看下样本如何实现程序执行流的控制,首先需要绕过 ASLR 保护,可以知道id为0的OLE对象其 CLSID
如下:

图7  otkloadr.WRAssembly.1 对应的 CLSID

我们在 ole32 模块的 CoCreateInstance 函数上下断,此函数的作用是初始化 OLE 对象,可以看到程序会加载 OTKLOADR.DLL 模块,而 OTKLOADR.DLL 模块又引用了 MSVCR71.DLL 模块中导出的接口函数,所以该模块也会被加载:

图8  OTKLOADR.DLL 模块的加载

而 MSVCR71.DLL 模块并未启用 ASLR 保护,样本将借此绕过 ASLR 保护:

图9  MSVCR71.DLL 模块未启用 ASLR 保护

对于仅抽取样本中id为0和2这两部分对象内容的 rtf 文档来说,最终会触发程序的内存访问违规,从函数的调用栈可以看出其上层应为虚函数调用,这种情况一般通过进程的栈空间来查找函数返回地址,以此分析调用关系。这里显然不能通过目前的 esp 进行查找,我们回溯几条指令后下断并重新执行:

图10  程序出现内存访问违规

此时再查看栈空间中的符号信息如下:

图11  查看栈空间中的符号信息

进一步分析可知,下述红色标识的指令即为相应的虚函数调用指令,其中,跳转的目的地址为 0x7c376fc3,同时压入的参数为 0x09000808,我们注意到这两个值就是 smartTag 标签中 moveFromRange* 的id值:

图12  相应的上层虚函数调用

这与样本借助此漏洞实现的内存写入操作正好是相对应的,因此,通过覆盖 MSVCR71.DLL 模块中的虚表指针,样本获得了 eip 控制权,另一方面,覆盖后的入参则是与下小节讨论的堆喷布局有关:

图13  利用此漏洞实现的内存写入操作

当然,根据Office分析环境的不同,上述获取 eip 的流程会存在差异,应该是样本出于兼容性方面的考虑。

3.2 shellcode

再接着我们来看一下 shellcode,此样本中有两部分 shellcode,第一部分会由堆喷布局到内存中。Office的堆喷一般通过 activeX 控件来实现,我们借助rtfobj.py提取样本中id为1的Word文档,解压后可在
activeX 目录得到如下文件列表,其中布局数据保存在 activeX.bin 文件中,更多相关讨论可参考此blog

图14  用于实现堆喷的文件列表

堆喷后进程空间的分布情况如下:

图15  堆喷后的进程空间

因此,程序通过堆喷能将 activeX.bin 文件中的数据精确布局到内存空间上,其中包含了 ROP 链和
shellcode。而样本在获得 eip 后会进行栈转移操作,也就是将前面的入参 0x09000808 赋给 esp,从而将其引到 ROP 链上执行:

图16  activeX.bin 文件中的布局数据

不用想ROP链的作用肯定就是调用 VirtualProtect 函数来改变内存页的属性,使之拥有执行权限以绕过DEP保护,不过分析环境中的 Word 2007 并未启用此保护:

图17  Word 2007 进程未启用 DEP

这里提及的栈转移和 ROP 链操作我们就不再赘述了,接下去把重点放到 shellcode 的理解上,其实方法无它,单步跟即可。对于第一部分 shellcode,它首先会通过查找 LDR 链的方式来获取 kernel32 模块的基址,因为后面会用到此模块导出的接口函数:

图18  获取 kernel32 模块的基址

而对于 kernel32 模块中导出函数的查找过程实际上就是PE文件结构中导出表的解析过程,如下为PE头的解析:


图19  解析 kernel32 模块的导出信息

目标函数名将以 hash 值的方式给出,如下就是查找相应目标函数名的过程,而在找到目标函数名后,将会从
AddressOfNameOrdinals 数组中取出对应的值,以此作为 AddressOfFunctions 数组中的索引,再加上模块基址就得到了此目标函数的导出地址:

图20  查找 kernel32 模块中的目标函数名

第一部分 shellcode 的作用是为了引出第二部分 shellcode,由于这部分数据是加密后保存在样本文件中的,因此首先需要获取打开的样本文件句柄,在 shellcode 中会遍历进程中打开的文件句柄,并通过调用
GetFileSize 找出其中符合条件的句柄进行下一步的判断:


图21  查找符合条件大小的文件句柄

随后会通过调用 CreateFileMapping 和 MapViewOfFile 函数将此特定大小的文件映射到内存中,如果前4个字节为 “{\rt”,即表示内存中映射的为目标样本文件,之后通过字符串 “FEFEFEFEFEFEFEFEFFFFFFFF” 定位到第二部分 shellcode 的起始位置:

图22  定位 rtf 文件中的第二部分 shellcode

而后将接下去的 0x1000 字节,即第二部分 shellcode,拷贝到函数 VirtualAlloc 申请的具有可执行权限的内存中,最后跳转过去执行。在第二部分 shellcode 开头会先对偏移 0x2e 开始的 0x3cc 字节数据进行异或解密:

图23  解密 shellcode 数据

这里也要用到相关的导出接口函数,其查找方法和第一部分 shellcode 相同:

图24  使用到的相关接口函数

此部分 shellcode 将用于释放恶意 payload 程序以及最终展现给用户的 Word 文档。恶意 payload 的数据保存在样本文件中,shellcode 会通过字符串 “BABABABABABABA” 进行起始字节的定位,之后再经过简单的异或解密即可得到此payload:

图25  定位并解密恶意的 payload 数据

接着会在临时目录的上一级创建名为 svchost.exe 的恶意 payload 文件,并通过 WinExec 函数来执行:

图26  创建恶意 payload 文件并执行

我们可以在对应目录找到此恶意 payload 文件,它的作用主要是进行信息的窃取:


图27  释放的恶意 payload 文件

此外,为了迷惑受害者,在恶意 payload 执行后样本会将一个正常的 Word 文档呈现给用户。这部分数据也保存在样本文件中,通过字符串“BBBBBBBBBBBBBB”定位后还需要进行异或解密操作,由于这部分内容的字节数必然小于样本文件字节数,为了构造相同大小的文件,剩下部分将用零来填充:

图28  定位并解密要呈现给用户的 Word 文档

之后用上一步得到的数据重写该恶意文档,并将其作为winword.exe的参数再次打开:

图29  用解密后的 Word 文档数据重写当前的样本文件

4 结语

总体来看样本的利用过程并不复杂,都是按固定套路走的,不过实际测试中发现这种基于堆喷的漏洞利用在性能和稳定性上确实需要提升,如何改进还是值得我们思考的。另外,分析有误之处还望各位加以斧正:P

5 参考

[1] CVE-2015-1641(ms15-033)漏洞分析与利用
https://weiyiling.cn/one/cve_2015_1641_ms15-033
[2] Word类型混淆漏洞(CVE-2015-1641)分析
http://www.freebuf.com/vuls/81868.html
[3] MS OFFICE EXPLOIT ANALYSIS – CVE-2015-1641
http://www.sekoia.fr/blog/ms-office-exploit-analysis-cve-2015-1641/
[4] Ongoing analysis of unknown exploit targeting Office 2007-2013 UTAI MS15-022
https://blog.ropchain.com/2015/08/16/analysis-of-exploit-targeting-office-2007-2013-ms15-022/
[5] The Curious Case Of The Document Exploiting An Unknown Vulnerability
https://blog.fortinet.com/2015/08/20/the-curious-case-of-the-document-exploiting-an-unknown-vulnerability-part-1

作者:dawu | Categories:安全研究技术分享 | Tags:

发表评论