关联漏洞
标题:Microsoft Office 安全漏洞 (CVE-2017-0261)Description:Microsoft Office是美国微软(Microsoft)公司开发的一款办公软件套件产品。常用组件有Word、Excel、Access、Powerpoint、FrontPage等。 Microsoft Office中存在远程代码执行漏洞。远程攻击者可通过构建特制的EPS文件利用该漏洞在受影响的应用程序上下文中执行任意代码或造成拒绝服务。以下版本受到影响:Microsoft Office 2010 SP2,Office 2013 SP1,Office 2016。
Description
CVE-2017-8570 Exp及利用样本分析
介绍
# CVE-2017-0261及利用样本分析
## 0x01 漏洞描述
- 成因:打开Office文档时,FLTLDR.EXE将被用于渲染包含该漏洞的嵌入式EPS文件。该文件是由PostScript语言编写而成,可以被攻击者通过"save-restore"操作利用,其本质为一UAF漏洞。 当用户打开包含格式错误的图形图像的文件时,或者当用户将格式错误的图形图像插入到 Office 文件时,该漏洞可能会受到利用。
- 影响版本:Microsoft Office 2010 Service Pack 2、Microsoft Office 2013 Service Pack 1、Microsoft Office 2016
- POC:[kcufId's Github](https://github.com/kcufId/eps-CVE-2017-0261)
## 0x02 POC分析
> 笔者在网上寻找许久,并未找到包含EPSIMP32.FLT的Office安装包。幸而kcufId师傅提供了一LoadEps.exe用以加载EPS文件,感谢kcufId师傅。
`LoadEps.exe`先是加载`EPSIMP32.FLT`:

之后调用`ImportGr`开始加载EPS文件:

于此处直接F7跟进,然后就可以成功断在EPSIMP32.FLT内所设断点。
---
在进入正题之前,先来铺陈下Postscript对象结构。
```
// PostScript Object
struct PostScript object
{
dword type;
dword attr;
dword value1;
dword value2; // if array, point to userdict where store the array object
}ps_obj;
```
其中不同`type`对应数值如下:
```
0x0 nulltype
0x3 integertype
0x5 realtype
0x8 booleantype
0x10 operatortype
0x20 marktype
0x40 savetype
0x300 nametype
0x500 stringtype
0x900 filetype
0x30000 arraytype
0x0B0000 packedarraytype
0x70000 packedarraytype
0x110000 dicttype
0x210000 gstatetype
```
以字符串为例,阐述其存储结构。对`forall`函数设置断点,便可以进一步查看其如何处理字符串(如何定位`forall`函数,可参阅https://paper.seebug.org/368/)。

1号图片对应`ps_obj`,其`value2`项指向索引列表中所对应项(2号图片);索引项指向一大小为0x30的结构,该结构0x24位置保存一指向大小为0x28结构(5号图片)的指针的指针,0x2C位置保存字符串大小(3号图片);5号图片中结构0x4位置存储该结构于索引列表中对应项的地址(即4号图片的0x01DB5E94),0x20位置指向字符串最终存储位置(6号图片),0x24位置为实际所占内存大小——字符串大小+1。
大小为0x30结构:
```
+0x0 dword
+0x4 dword
+0x8 dword
+0xc dword
+0x10 dword
+0x14 dword
+0x18 dword
+0x1c dword
+0x20 dword
+0x24 dword pp_struct //指向大小为0x28结构的指针的指针
+0x28 dword
+0x2c dword size //字符串实际大小
```
大小为0x28结构(换作数组,该结构大小为0x2C,且0x28位置指向数组元素,每一元素都是`ps_obj`):
```
+0x0 dword
+0x4 dword //存储该结构于索引列表中对应项的地址
+0x8 dword
+0xc dword
+0x10 dword
+0x14 dword
+0x18 dword
+0x1c dword
+0x20 dword ptr_object //指向字符串最终存储位置
+0x24 dword size //实际所占内存大小,字符串实际大小+1
```
---
漏洞第一次触发:

首先是将VM状态保存在`l62`变量内,之后对于`l63`变量内每一字符调用`l61`——>>`l59`——>>`l56`处理过程;`l62 restore`恢复之前的状态,如此一来,`/l62 save def `语句后面`l63`申请的内存空间会被释放,从而成为悬挂指针。

`l95-l99`变量决定了后续流程,其值均为0(即32位):

漏洞第二次触发,首先是申请0x27大小(实际会占用0x28)的内存空间存储`l63`:

之后`l62 restore`恢复之前的状态,导致`l63`申请的内存空间被释放,从而成为悬挂指针;接下来执行`l100`,之前`l63`所占用内存空间会用来存储`l102`(即`l136`)字符串的0x28结构(这就解释了`l63`为何会申请0x27大小内存空间):

分别获取该结构0x4、0x20、0x24位置的数值:

最后修改`l136`字符串内容(图中仅展示了部分修改之处):

这些修改内容是精心构造的,会于第三次触发漏洞时用到。
漏洞第三次触发,申请包含0x37个元素的数组,之后在循环到第0x34个元素时执行`l62 restore`:

执行完`restore`之后,数组的0x30结构被`l193`字符串内容覆盖:

如此一来,最后一次(0x36)`forall`过程所执行的对象便成为上图中0x30结构,而获取其第0x36个元素便会来到第二次漏洞触发时所精心构造的字符串处:

而其获取到的数组元素是一大小为4的数组,该数组首元素是一起始地址为0,大小为0x7FFFFFFF的字符串:

该数组会存储在`l159`变量中,其首元素——起始地址为0,大小为0x7FFFFFFF的字符串会存储在`l201`变量中,之后便可通过`l201`变量获取任意地址的值。
获取kernel32.dll基址:


如此一来,`l314`变量内存储的便是EPSIMP32.FLT基址。

注:`search `命令语法如下:

查找指定gadget:


构造`file`类型结构:
```
l199 l201 get_dword
/l487 exch def
l487 l201 get_dword
/l488 exch def
l488 36 my_add l201 get_dword
/l489 exch def
l489 l201 get_dword
/l490 exch def
l490 32 my_add l201 get_dword
/l491 exch def
l199 l491 l201 put_data_to_array
l199 12 my_sub 2304 l201 put_data_to_array
```

向`l492`地址(`l491`+0x32)处写入构造数据:
```
l492 0 l201 put_data_to_array %% 0x00 0
l492 4 my_add l375 l201 put_data_to_array %% 0x04 Address of <5E C3>
l492 8 my_add l373 l201 put_data_to_array %% 0x08 Address of <94 00 00 00 00 5E C3>
l492 12 my_add l377 l201 put_data_to_array %% 0x0C Address of <C2 0C 00>
l492 16 my_add l370 l201 put_data_to_array %% 0x10 Address of VirtualProtect()
l492 20 my_add 0 l201 put_data_to_array %% 0x14 0
l492 24 my_add 0 l201 put_data_to_array %% 0x18 0
l492 28 my_add 0 l201 put_data_to_array %% 0x1C 0
l492 32 my_add l368 l201 put_data_to_array %% 0x20 Address of Shellcode
l492 36 my_add l368 l201 put_data_to_array %% 0x24 Address of Shellcode——lpAddress
l492 40 my_add l349 l201 put_data_to_array %% 0x28 Size of Shellcode——dwSize
l492 44 my_add 64 l201 put_data_to_array %% 0x2C PAGE_EXECUTE_READWRITE——flNewProtect
l492 48 my_add l493 l201 put_data_to_array %% 0x30 lpflOldProtect
```
最后,执行`closefile`指令时跳转至Shellcode:

## 0x03 样本分析
EPS利用脚本位于`\word\media`目录下,解压之后即可看到。该漏洞利用样本除Shellcode部分,其余基本一致,故以Patchword组织某样本为例进行分析。
> 文件名:Cyber_Secure_Pakistan.docx
>
> MD5:DD89BBB916A2C909630EC78CBB0E13E5
跳转到Shellcode,恢复堆栈:

申请内存:

获取函数调用地址:

调试过程中,可能是因为环境问题导致`CreateToolhelp32Snapshot`函数调用地址未成功获取:

手动填入地址并打开Word以继续分析。枚举进程,查找WINWORD.exe:

于`C:\ProgramData\Microsoft\DeviceSync`目录下创建一名为MSBuild.exe的程序:

写入文件内容,其内容存储于EPS脚本的`payload_32`变量内:


创建vmtools.dll文件:

写入文件内容,其内容存储于EPS脚本的`payload_32_f2`变量内:


创建VMwareCplLauncher.exe文件:

其内容存储于EPS脚本的`payload_32_f1`变量内:

该文件是具有Vmware签名的白文件:

注入如下内容到explorer.exe中:

其功能为创建VMwareCplLauncher.exe进程:

之后流程于360此篇[报告](https://www.freebuf.com/vuls/157694.html)有提及,本文暂不涉及其分析部分:

有兴趣的读者可以进一步阅读该报告。
注:该漏洞的利用样本基本相似,不同之处在于最后的MSBuild.exe载荷,其存储于EPS脚本的`payload_32`变量内,可直接dump出来,填补完DOS文件头之后便可以拖进IDA分析。
## 0x04 参阅链接
- [EPS Processing Zero-Days Exploited by Multiple Threat Actors](https://www.fireeye.com/blog/threat-research/2017/05/eps-processing-zero-days.html)
- [CVE-2015-2545 Word 利用样本分析](https://paper.seebug.org/368/)
- [PostScript LANGUAGE REFERENCE](https://web.archive.org/web/20170218093716/https://www.adobe.com/products/postscript/pdfs/PLRM.pdf)
- [摩诃草组织最新漏洞攻击样本分析及预警](https://www.freebuf.com/vuls/157694.html)
文件快照
[4.0K] /data/pocs/56926f84b4ddeee198bef2305dbb7ade04bbfbd1
├── [4.0K] POC
│ ├── [4.0K] done
│ │ ├── [ 772] eps.conf
│ │ ├── [435K] EPSIMP32.FLT
│ │ ├── [3.5K] LoadEps.exe
│ │ └── [ 55K] poc.eps
│ ├── [6.9M] EPSIMP32.idb
│ ├── [4.0K] LoadEps
│ │ ├── [3.5K] LoadEps.asm
│ │ ├── [ 863] Macro.inc
│ │ └── [ 211] Makefile
│ └── [ 413] README.md
└── [ 11K] README.md
3 directories, 10 files
备注
1. 建议优先通过来源进行访问。
2. 如果因为来源失效或无法访问,请发送邮件到 f.jinxu#gmail.com 索取本地快照(把 # 换成 @)。
3. 神龙已为您对 POC 代码进行快照,为了长期维护,请考虑为本地 POC 付费/捐赠,感谢您的支持。