1. 概述
这是一种MSOffice漏洞,允许通过使用特殊的 Encapsulated PostScript (EPS)图形文件任意执行代码。这种漏洞于2015年3月被发现,漏洞未修补情况持续了4个月。之后,微软发布了修复补丁(MS15-099),解决了这一安全问题。
漏洞发布时间:2015-09-08
漏洞更新时间:2015-09-08
影响系统
Microsoft Office 2007 SP3 Microsoft Office 2010 SP2 Microsoft Office 2013 SP1 Microsoft Office 2013 RT SP1 Microsoft Office for Mac 2011 Microsoft Office for Mac 2016 Microsoft Office Compatibility Pack SP3 |
漏洞信息
Microsoft Office是一款微软发布的办公处理应用套件。
Microsoft Office处理EPS文件存在内存破坏,允许攻击者构建恶意文件,诱使应用解析,可使应用程序崩溃或执行任意代码。
用户可参考如下厂商提供的安全补丁以修复该漏洞:
2. 样本来源
通过`推特`搜索`cve-2015-2545`得到相关信息,筛选信息得到其哈希值`哈希值:375e51a989525cfec8296faaffdefa35`,之后Google搜索其哈希值,在`
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 简体中文 x86 2. Windows Debugger Version 6.12.0002.633 X86 3. IDA6.8.150423 (32-bit) 4. OllyDbg 1.0 5. 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 |