欢迎来到兴华永恒!加入收藏设为首页
您当前所在位置:首页 > 技术专栏 > 专业发布
技术专栏

针对CVE-2015-2545漏洞研究分析(中篇)


3.5.6 字典对象处理流程

关键的函数为forall在处理dict的过程,我们在第三个语句块首部分下断点<`EPSIMP32!RegisterPercentCallback+0x4664`>,观察断点,入栈了3个参数,调用一函数,那么我们需要思考下这三个参数是什么,通过查询<<PLRM2.PDF>>,我们得到关键性的资料,[`图2`]上面说如果第一个操作是字典类型,那么它会入栈一个`key`和一个`数据`,这里类似哈希表的形式,通过一个key可以迅速得到对应的数值;为什么`图1`我写`pNext`,通过`图3`我们可以判断其内容是否为null,则`lea eax, [ebp-14]`eax取出来的是个指针,所以这里这个名字主要是方便识别其为指针所定。

1491893143694777.png

图1

1491893171474704.png

图2

1491893193459016.png

图3

3.5.7 对象结构解析

关于第一个CALL所做的事情,大致可以分为3个方面,给PNEXT附值,给KEY赋值,给VALUE赋值,这里说的简单,里面的功能CALL需要自己手动调试下做好记录更加方便理解是如何赋值的过程,这里直接说下是如何得到的这些数值,首先看下`ECX`所对应的内容`图1`所示,其中`+0X8`偏移的位置是循环遍历的次数,相当于一个`INDEX`也可以说是个`界限值`,里面的CALL循环条件的范围就是 `400`, 其中`+0XC`的位置`8`表示`KEY`的个数,为什么,如`图2`所示,那么关于`+0X0`偏移的位置则是个`基址`的意思,所有的结果都是通过[基址+偏移*X]的形式得到相应的数据,`图3`表示第一个`KEY1`中的内容,同时`2B4`则为下标,通过基址+下标*4即可获取第一个KEY1对应内容的地址,这0x28个字节就是个结构体,大致内容如下所示,正好可以跟我们的数据一一对应,不过这里还是需要`多跟几遍`才会更加明白`结构`的情况,当跑完第一个CALL之后的内存情况如图4所示。

2017-03-27_163447.png

图1

1491893295712524.png

图2

2017-03-27_164236.png

图3

     

 struct {    
      dword * pNext; //指向下个结构体
      dword dwIndex; //下标
      ps_obj key;   //key
      ps_obj value;  //数据
      }kv_pair_element;
     
      struct PostScript object {
      dword type;//类型
      dword attr; //属性
      dword value1; //数据
      dword value2; //数据
      }ps_obj;

2017-03-27_165635.png

图4

00030000[类型] 00000000[属性] 000001ff[数据]  04f32ea4[数据]
00000300[类型] 00000000[属性] 04f69db0[数据]  04f32ea4[数据]
04f8fd98[pNext]

3.5.8 释放空间函数分析

第二call,第三call,为同一call,在对key与value进行操作,copy到其他位置,之后的`PROC`则为重点call,此call则执行`forall`的所有的操作,我们只需要在copy函数的位置下断点即可得到出发漏洞的位置<copy:`EPSIMP32!RegisterPercentCallback+0x15582`>,然后我们单步跟踪即可,这里可以先用ida观察下copy的大致流程,然后通过动态的方式更好的去跟踪我们想要的数据,由于copy的函数块比较大,动态调试起来也不是特别容易,我这里跟的时候是每个call都跟进去了,很费时间,但肯定可以找到想要的函数数据的,这里我直接给下偏移<`EPSIMP32!RegisterPercentCallback+0x12e8e`>,如`图1`所示为释放过程的函数<`EPSIMP32+0x1a0e8`>,这个释放的过程可以仔细观察下,还记得最开始的时候的this指针所指向的内容吗?[[this]],记得第一个call的时候,我们获取KEY1-KEY8都是存在以[[this]]为基址,然后加上一个偏移得到这些数据,第一次的循环delete也是从[[this]]为基址开始遍历,判断是否为null,不是空则delete,那么最后这正片空间都被delete了,这个的范围也是我们刚刚进入第一个call的时候的一个下标`400`,循环结束之后,delete this,并且清空`+0x0`, `+0x8`,就是清空key的个数。

1491893589730477.png

图1

1491893613379181.png

图2(释放:dict1 copy dict2—>释放dict2过程)

3.5.9 copy字典对象

之后会进行拷贝的操作,要把dict1–>copy到dict2中,则会new一段空间,而这段空间刚刚好为之前所释放的[[this]],这里new的是`0x1000`大小,并且通过`memset`清空刚刚申请的空间中的内容`图1,2`所示,之后会继续new0x28空间大小,为什么?因为`dect1–>copy–>dect2`中,diect1只有`1个key`,则在0x1000中所填充的数据也就是1个4字节地址,所以需要new 0x28大小来保存数据图3所示,copy完数据`图4`所示。

1491893712750396.png

图1

1491893741462057.png

图2(新new空间与释放的dict2一致)

1491893794695892.png

图3

2017-03-27_184239.png

图4(new0x28大小空间:内容pNext,index, 类型,属性,数据,数据,类型,属性,数据,数据)

3.5.10 填充构造数据

之后则为putinterval操作的过程,这个就会填充我们的pNext指针所指向的数据空间,断点位置<`EPSIMP32!RegisterPercentCallback+0x160da`>,然后会进行`memcpy`的操作,把我们数据正好填充到我们之前的pNext所指向的空间中,这样会whlile循环会再次跑一边,而这次的 key,vale则是我们自己所构造的指定数据,结构如下所示。

1491893888518643.png

图1

00000000[pNext]    000003ff[index]    00000003[类型]    00000000[属性]
00000000[key.valu1]    44444444[key.value2]    00000500[类型]    00000000[属性] 
00000000[value.1]    0462e0b0[value.2]//这里是原来数据,并非复制过来的数据
在线咨询 周一至周五
09:00-18:00