1. 概述
这是一种MSOffice漏洞,允许通过使用特殊的 Encapsulated PostScript (EPS)图形文件任意执行代码。这种漏洞于2015年3月被发现,漏洞未修补情况持续了4个月。之后,微软发布了修复补丁(MS15-099),解决了这一安全问题。
漏洞发布时间:2015-09-08
漏洞更新时间:2015-09-08
影响系统
Microsoft Office 2007 SP3Microsoft Office 2010 SP2Microsoft Office 2013 SP1Microsoft Office 2013 RT SP1Microsoft Office for Mac 2011Microsoft Office for Mac 2016Microsoft Office Compatibility Pack SP3 |
漏洞信息
Microsoft Office是一款微软发布的办公处理应用套件。
Microsoft Office处理EPS文件存在内存破坏,允许攻击者构建恶意文件,诱使应用解析,可使应用程序崩溃或执行任意代码。
用户可参考如下厂商提供的安全补丁以修复该漏洞:https://technet.microsoft.com/library/security/ms15-099
2. 样本来源
通过`推特`搜索`cve-2015-2545`得到相关信息,筛选信息得到其哈希值`哈希值:375e51a989525cfec8296faaffdefa35`,之后Google搜索其哈希值,在`https://www.hybrid-analysis.com/`得到样本。
3. 分析
3.1 漏洞成因以及利用
当EPS在处理`字典类型(dict)`的`copy操作`时,将会接受拷贝方的`键值对`,并且将自身空间数据全部删除,然后再重新分配一个空间进行数据拷贝(正常情况下字典拷贝时只对要拷贝的元素进行操作,而不影响其它元素)。而EPS在处理`forall操作`时,当处理类型为`字典(dict)`时,`forall`逐个处理字典(dict)中的每个`键值对`,`forall`会获得`当前键值对`的`内容`以及一个`ptrNext指针`,并指向下一个要处理的键值对,并将键(key)和值(value)的内容放到`操作栈`中,然后进行`forall的处理过程(proc)`,处理完后`仍保留ptrNext指针`以此来处理下一个键值对。如果在`forall`处理过程中有字典的`拷贝(copy)`操作,copy操作会将`键值对`条目`全部删除`,而forall的ptrNext指针仍`存在`,这时`ptrNext`就变成一个`野指针`,只要精心构造指针指向的数据,就可以达到利用效果。该样本利用方式为通过该野指针构造出一个起始地址为0x0,大小为0x7fffffff的`string对象`,这样就可以在该空间内作`任意`的`读写`操作以及后期的`ROP,Shellcode`的利用操作。
3.2 测试环境
1. Microsoft Windows [版本 6.1.7601] 旗舰版sp1 简体中文 x862. Windows Debugger Version 6.12.0002.633 X863. IDA6.8.150423 (32-bit)4. OllyDbg 1.05. Notepad++6. Microsoft Office Word 2007(12.0.6612.1000) SP3 MSO(12.0.6607.10000) |
3.3 调试方向
此样本从3个方面来进行调试分析,第一个调试方向:定位简单可以得到信息的位置`ROP`,第二个方向则为重点方向:产生`野指针的位置`,第三个方向:`Rop`+`Shellocde`,共3个方向进行调试分析。
3.4 调试分析-CreateFile入手[第一个方向]
1.通过Windbg打开样本,直接运行,可以发现样本在临时目录文件夹下进行创建文件操作,创建文件顺序:plugin.dll > igfxe.exe,之后,直接想到的是在`ZwCreateFile`下断点【此断点位置不太好,因为word在打开的时候不断的跑CreateFile,但通过追溯可以找到自己想要的内容】。

图1
2.回溯找到`ROP`,`图2`call则为`rop`call,测试方式,自己在相应call下断点,单步跟踪即可,关于rop,换栈空间的操作以及后面的shellcode这里先不详细分析,自己可以单步跟踪即可。

图1

图2
3.5 调试分析-野指针产生过程[第二个方向] 3.5.1 简易例子解释UAF 此过程比较复杂,需要了解“PostScript”语法,可以下载<<PLRM2.pdf>>来学习;大家还要了解下关于`UAF`的简单的知识,何为`UAF`?就是字面意思“释放后重新使用“,例子如下: #include<stdio.h> #include<stdlib.h> #include<malloc.h>
void *pfunc1() { printf("testn"); }
typedef struct Object1_struct{ int flag; void (*pfunc1)(); char message[4]; }OBJECT1;
typedef struct Object2_struct{ int flag; int flag2; char *welcome; }OBJECT2;
int main() { int i; OBJECT1 *pObject1; OBJECT2 *pObject2; pObject1 = (OBJECT1 *)malloc(sizeof(OBJECT1));//init struct pObject1->flag = 1; //pObject1->pfunc1(); //pObject1->message = "this is first create!";
free(pObject1); /*forget pObject1 = NULL*/ for(i=0;i<1000;i++) { pObject2 = (OBJECT2 *)malloc(sizeof(OBJECT2));//heap spray pObject2->flag=2; pObject2->flag2=4; pObject2->welcome = "AAAA"; } /*fill pointer*/
if(pObject1 != NULL) pObject1->pfunc1(); return 0; } 3.5.2 优化PostScript 了解上面的知识之后我们需要定位漏洞出现的位置,我们知道此漏洞出现的位置是在forall操作里面发生的,这个时候可以看下word文件中镶嵌的iamge1.eps文件,从文件可以看出代码都被挤压成一起了,很不方便观看这些代码,只能手动对关键代码更改`[图2]`所示
图1
图2 3.5.3 定位EPS样本文件关键位置 从代码上我们直观发现的是`<00000000ff030000030000000000000000000000444444440005000000000000000000>`这一串东西,具体是什么我们现在先不需要考虑,然后我们可以看到forall操作{}里好多其他代码,其中就包含了copy的操作,这个位置就是导致漏洞产生的地方,那我们怎么去定位呢?动态调试,栈回溯的方式这个有点坑,大概你跟一天也跟不到吧,因为里面操作特别多,要知道这里面每个操作都是一个函数,所以回溯的方法不太可取,这个时候就需要用到IDA,直接使用IDA打开漏洞模块,打开之后我们应该想到,既然是操作,那么应该跟函数一样有带字符串的名字,那么我们是不是可以通过IDA–>shitf+f12【字符串窗口】–>查找forall这个函数呢?我们用`alt+t`查找下,果然,有了我们想要的东西`图1`,我们跟进去,发现有个地方在引用`图2`,我们继续跟进去,直接定位到了`forall函数“图3`,同理可得,我们可以定位到`copy`的函数与其他关键函数,然后动态调试跟踪即可。
图1
图2
图3 3.5.4 关键函数断点位置 首先我们在关键几个位置下断点,通过IDA得到偏移地址,直接Windbg or OD 定位相应位置即可,这里我用的是Windbg下断点,下面列出的断点是我调试了很多次所记录下的断点,大部分的断点我都给下了主要为了了解下代码流程方便观看,其实刚刚开始调试只需要在forall一处下断点即可,先了解其结构在观察其做法,我们先断第一个`forall`的位置 <`EPSIMP32!RegisterPercentCallback+0x4526`> 0 e 6822bcc6 0001 (0001) 0:**** EPSIMP32!RegisterPercentCallback+0x4664 ".printf "forall_value_key_pNext"" 1 e 6823cbe4 0001 (0001) 0:**** EPSIMP32!RegisterPercentCallback+0x15582 ".printf "dict1_dict2_Copy"" 2 e 6823a4f0 0001 (0001) 0:**** EPSIMP32!RegisterPercentCallback+0x12e8e ".printf "dict1_dict2_copy_2"" 3 e 6821a0e8 0001 (0001) 0:**** EPSIMP32+0x1a0e8 ".printf "_copy_delete "" 4 e 6823cd89 0001 (0001) 0:**** EPSIMP32!RegisterPercentCallback+0x15727 ".printf"copy_memcpy "" 5 e 6823ca58 0001 (0001) 0:**** EPSIMP32!RegisterPercentCallback+0x153f6 ".printf"_exch_fun_"" 6 e 6823d592 0001 (0001) 0:**** EPSIMP32!RegisterPercentCallback+0x15f30 ".printf"_put_fun_"" 7 e 6823d73c 0001 (0001) 0:**** EPSIMP32!RegisterPercentCallback+0x160da ".printf"_PutInterVal_fun_"" 8 e 68230313 0001 (0001) 0:**** EPSIMP32!RegisterPercentCallback+0x8cb1 ".printf"_BytesavailabelFun_"" 9 e 6823d8f5 0001 (0001) 0:**** EPSIMP32!RegisterPercentCallback+0x16293 ".printf"_StringFun_"" 10 e 682312e6 0001 (0001) 0:**** EPSIMP32!RegisterPercentCallback+0x9c84 ".printf"_ShowFun_"" 11 e 6823d830 0001 (0001) 0:**** EPSIMP32!RegisterPercentCallback+0x161ce ".printf"memcpy_putinterval_"" 12 e 6820cff5 0001 (0001) 0:**** EPSIMP32+0xcff5 ".printf"memcpy_put_bitshift_and_add_"" 3.5.5 关于forall操作函数 我们通过ida可以观察forall是个`条件分支的语句`,通过查询《PLRM2.PDF》得到关于forall的解释,大约有4种情况,不过可以看出“array proc”与“packedarray proc”是一样的类型都是数组类型,其它两种类型分别是字典类型与字符串类型,验证方式直接在image.eps文件中添加实验代码即可验证。
图1
图2 |