注意事项:1.本篇文章由Gcow安全团队复眼小组的ERFZE师傅原创,未经许可禁止转载2.本篇文章一共2313字,39张图,预计用时20分钟3.文中提及的方法仅供参考学习,若用在实际情况而造成的损失,本团队以及本公众号概不负责4.若本篇文章中存在说得有错误或者模糊的环节,希望各位看官可以在后台留言或者评论指出,本小组不胜感激!
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
:
图片1 加载EPSIMP32.FLT
之后调用ImportGr
开始加载EPS文件:
图片2 ImportGr
于此处直接F7跟进,然后就可以成功断在EPSIMP32.FLT内所设断点。
在进入正题之前,先来铺陈下Postscript对象结构。
// PostScript Objectstruct PostScript object{ dword type; dword attr; dword value1; dword value2; // if array, point to userdict where store the array object}ps_obj;
其中不同type
对应数值如下:
0x0 nulltype0x3 integertype0x5 realtype0x8 booleantype0x10 operatortype0x20 marktype0x40 savetype0x300 nametype0x500 stringtype0x900 filetype0x30000 arraytype 0x0B0000 packedarraytype0x70000 packedarraytype0x110000 dicttype0x210000 gstatetype
以字符串为例,阐述其存储结构。对forall
函数设置断点,便可以进一步查看其如何处理字符串(如何定位forall
函数,可参阅https://paper.seebug.org/368/)。
图片3 字符串存储结构
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
漏洞第一次触发:
图片4 第一次
首先是将VM状态保存在l62
变量内,之后对于l63
变量内每一字符调用l61
——>>l59
——>>l56
处理过程;l62 restore
恢复之前的状态,如此一来,/l62 save def
语句后面l63
申请的内存空间会被释放,从而成为悬挂指针。
图片5 l95-l99
l95-l99
变量决定了后续流程,其值均为0(即32位):
图片6 exch_proc
漏洞第二次触发,首先是申请0x27大小(实际会占用0x28)的内存空间存储l63
:
图片7 l63
之后l62 restore
恢复之前的状态,导致l63
申请的内存空间被释放,从而成为悬挂指针;接下来执行l100
,之前l63
所占用内存空间会用来存储l102
(即l136
)字符串的0x28结构(这就解释了l63
为何会申请0x27大小内存空间):
图片8 l102
分别获取该结构0x4、0x20、0x24位置的数值:
图片9 获取值
最后修改l136
字符串内容(图中仅展示了部分修改之处):
图片10 构造字符串
这些修改内容是精心构造的,会于第三次触发漏洞时用到。
漏洞第三次触发,申请包含0x37个元素的数组,之后在循环到第0x34个元素时执行l62 restore
:
图片11 第三次触发流程
执行完restore
之后,数组的0x30结构被l193
字符串内容覆盖:
图片12 覆盖内容
如此一来,最后一次(0x36)forall
过程所执行的对象便成为上图中0x30结构,而获取其第0x36个元素便会来到第二次漏洞触发时所精心构造的字符串处:
图片13 获取数组元素
而其获取到的数组元素是一大小为4的数组,该数组首元素是一起始地址为0,大小为0x7FFFFFFF的字符串:
图片14 数组元素内容
该数组会存储在l159
变量中,其首元素——起始地址为0,大小为0x7FFFFFFF的字符串会存储在l201
变量中,之后便可通过l201
变量获取任意地址的值。
获取kernel32.dll基址:
图片15 获取基址-1
图片16 获取基址-2
如此一来,l314
变量内存储的便是EPSIMP32.FLT基址。
图片17 获取基址-3
注:search
命令语法如下:
图片18 search
查找指定gadget:
图片19 gadget-1
图片20 gadget-2
构造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
图片21 构造file type
向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:
图片22 closefile
0x03 样本分析
EPS利用脚本位于\word\media
目录下,解压之后即可看到。该漏洞利用样本除Shellcode部分,其余基本一致,故以Patchword组织某样本为例进行分析。
文件名:Cyber_Secure_Pakistan.docx
MD5:DD89BBB916A2C909630EC78CBB0E13E5
跳转到Shellcode,恢复堆栈:
图片23 恢复堆栈
申请内存:
图片24 VirtualAlloc
获取函数调用地址:
图片25 函数调用地址
调试过程中,可能是因为环境问题导致CreateToolhelp32Snapshot
函数调用地址未成功获取:
图片26 CreateToolhelp32Snapshot
手动填入地址并打开Word以继续分析。枚举进程,查找WINWORD.exe:
图片27 枚举进程
于C:\ProgramData\Microsoft\DeviceSync
目录下创建一名为MSBuild.exe的程序:
图片28 创建MSBuild.exe
写入文件内容,其内容存储于EPS脚本的payload_32
变量内:
图片29 WriteFile
图片30 payload_32
创建vmtools.dll文件:
图片31 创建vmtools.dll
写入文件内容,其内容存储于EPS脚本的payload_32_f2
变量内:
图片32 WriteFile
图片33 payload_32_f2
创建VMwareCplLauncher.exe文件:
图片34 创建VMwareCplLauncher.exe
其内容存储于EPS脚本的payload_32_f1
变量内:
图片35 payload_32_f1
该文件是具有Vmware签名的白文件:
图片36 Vmware签名
注入如下内容到explorer.exe中:
图片37 注入explorer.exe
其功能为创建VMwareCplLauncher.exe进程:
图片38 创建VMwareCplLauncher.exe进程
之后流程于360此篇报告(https://www.freebuf.com/vuls/157694.html)有提及,本文暂不涉及其分析部分:
图片39 流程
有兴趣的读者可以进一步阅读该报告。
注:该漏洞的利用样本基本相似,不同之处在于最后的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)