Author: 仙果
EMail: zhanglinguo11@163.com
Site: http://hi.baidu.com/zhanglinguo11  
Date: 2009-11-03
附记:一个月以前分析的,文章中大概以我翻译的那篇文章为思路,进行分析,补充了文章里的一些细节,好不容易找到的,POC样本已经找不到了。
     比较可惜。翻译文章的链接地址为:对PDF _CVE-2009-3459分析文章的翻译 

[ 目录 ]

0×00 漏洞公告
0×01 漏洞调试分析
0×01_0 触发漏洞
0×01_1 控制程序执行流程
0×02 Adobe Readr堆管理系统
0×03 总结

0×00 漏洞公告
Heap-based buffer overflow in Adobe Reader and Acrobat 7.x 

before 7.1.4, 8.x before 8.1.7, and 9.x before 9.2 allows remote 

attackers to execute arbitrary code via a crafted PDF file that 

triggers memory corruption, as exploited in the wild in October 2009. 

NOTE: some of these details are obtained from third party information.

0×01 漏洞调试分析

0×01_0 触发漏洞
  在PDF中,由于参数值触发的初始化整数溢出

代码:
25 0 obj<</Length 299/Filter/FlateDecode/DecodeParms<<

/Columns 1

/Predictor 2

/BitsPerComponent 1

/Colors 1073741838          //此处就是为关键参数,触发漏洞的关键点.

>>>>stream

Colors 的值被设置为 1073741838,其十六进制为0x4000000e,没有弄明白flate

编码是什么编码?查阅相关资料说是"图像解码器",莫衷一是.

在009f60a5h处地址,Adobe Reader会引用0x4000000e参数进行计算,如下:

代码:
009f60a5 8d148d48000000  lea     edx,<Unloaded_util.dll>+0x47 (00000048)[ecx*4]  //ecx=0x4000000e

009f60ac 89442424        mov     dword ptr [esp+24h],eax                         //eax=0x0

009f60b0 8954241c        mov     dword ptr [esp+1Ch],edx                         //0x80保存到[esp+1ch]中

009f60b4 8b4c2410        mov     ecx,dword ptr [esp+10h]                         //ecx赋值为0x0

009f60b8 8b442414        mov     eax,dword ptr [esp+14h]                         //eax赋值为0x0

009f60bc 83c003          add     eax,3                                           //eax=eax+0x3=0x3

009f60bf 83e0fc          and     eax,0FFFFFFFCh                                  //相与,为0x0

009f60c2 8d6c0703        lea     ebp,[edi+eax+3]                                 //ebp=0x08000005

009f60c6 83e5fc          and     ebp,0FFFFFFFCh                                  //ebp=0x08000004

009f60c9 8bd5            mov     edx,ebp

009f60cb 0faf542470      imul    edx,dword ptr [esp+70h]

009f60d0 03542418        add     edx,dword ptr [esp+18h]                         //edx=0x0

009f60d4 89442414        mov     dword ptr [esp+14h],eax

009f60d8 8d048a          lea     eax,[edx+ecx*4]                                 //eax=0x0

009f60db 0344241c        add     eax,dword ptr [esp+1Ch]                         //eax=0x80

009f60df 8b4c2428        mov     ecx,dword ptr [esp+28h]                         //ecx=0x03ddcf88

009f60e3 50              push    eax

009f60e4 51              push    ecx

009f60e5 e836010000      call    AcroRd32_950000!AVAcroALM_IsFeatureEnabled+0x627c1 (009f6220)

009f60ea 8bd8            mov     ebx,eax                                        //eax=0213208c ,0x80大小的缓冲区指针

009f60ec 83c408          add     esp,8

009f60ef 85db            test    ebx,ebx

............................................................................
009f60a5 地址的意思为 ecx*4+48 赋给 edx
ecx=0x4000000e,由于存在进位,最后的值就为0x80
即,0x4000000e*4+48=0x100000080,但持续只读取到了0x80
在接下来的 call    AcroRd32_950000!AVAcroALM_IsFeatureEnabled+0x627c1 (009f6220) 中的一个函数中调用API函数MemSet进行堆的分配
Adobe Reader似乎认为 Colors的每一个值都为一个对象,每一个对象都包含有4个字节即一个DWORD,语句的目的是计算为每个对象分配一个DWORD需要的内存大小。
其返回值为eax=0213208c,即0x80大小的缓冲区指针,指向缓冲区的起始位置。
注意这个值,在下面程序进行计算的时候会使用到这个地址。

..............................................................................

代码:
009f5e18 33db            xor     ebx,ebx

009f5e1a 83c40c          add     esp,0Ch

009f5e1d 3bc3            cmp     eax,ebx

009f5e1f 8986ac000000    mov     dword ptr <Unloaded_util.dll>+0xab (000000ac)[esi],eax ds:0023:03ddd034=00000000

............................................................................................................

经过几次跳转以后,程序会在009f5e1f处,把0x80大小的缓冲区指针(eax=0213208c)保存在[esi+0ach]中,并在下面的处理过程中进行调用

............................................................................................................

代码:
009f7854 83c404          add     esp,4

009f7857 8b86ac000000    mov     eax,dword ptr <Unloaded_util.dll>+0xab (000000ac)[esi] ds:0023:03ddd034=0213208c   //[esi+0ach]=0213208c 赋值给eax

009f785d 50              push    eax                                                                                //eax压入堆栈

009f785e 8d4c2410        lea     ecx,[esp+10h]

009f7862 56              push    esi                                                                                 //esi=03ddcf88,[esi]=0x7

009f7863 51              push    ecx

009f7864 896c2418        mov     dword ptr [esp+18h],ebp

009f7868 e863060000      call    AcroRd32_950000!AVAcroALM_IsFeatureEnabled+0x64471 (009f7ed0)                     //发生溢出的函数

009f786d 83c40c          add     esp,0Ch

009f7870 85c0            test    eax,eax

009f7872 7407            je      AcroRd32_950000!AVAcroALM_IsFeatureEnabled+0x63e1c (009f787b)

............................................................................................................
0x80大小的缓冲区指针(0213208c)赋值给eax,并压入堆栈
借用参考资料中相关内容:
1.lpBuff:0x80大小的缓冲区堆块的指针-------------------eax
2.bits_original_stream:解码流中的比特(bits)数量-----esi
3.v_counter:从0到bits_original_stream-1的计数器值-----ecx
以下为 函数009f7ed0的汇编代码:
............................................................................................................

代码:
009f7ed0 83ec2c          sub     esp,2Ch

009f7ed3 8b442430        mov     eax,dword ptr [esp+30h]

009f7ed7 53              push    ebx

009f7ed8 8b18            mov     ebx,dword ptr [eax]

009f7eda 8b442438        mov     eax,dword ptr [esp+38h]

009f7ede 55              push    ebp

009f7edf 56              push    esi

009f7ee0 8b742444        mov     esi,dword ptr [esp+44h]

009f7ee4 0fb70e          movzx   ecx,word ptr [esi]

009f7ee7 57              push    edi

009f7ee8 8b38            mov     edi,dword ptr [eax]

009f7eea 8b4604          mov     eax,dword ptr [esi+4]

009f7eed 33d2            xor     edx,edx

009f7eef 85c0            test    eax,eax

009f7ef1 8954242c        mov     dword ptr [esp+2Ch],edx

009f7ef5 894c241c        mov     dword ptr [esp+1Ch],ecx

009f7ef9 895c2414        mov     dword ptr [esp+14h],ebx

009f7efd 897c2424        mov     dword ptr [esp+24h],edi

009f7f01 0f8c03020000    jl      AcroRd32_950000!AVAcroALM_IsFeatureEnabled+0x646ab (009f810a)       //[不跳转]

009f7f07 83f801          cmp     eax,1

009f7f0a 7e3e            jle     AcroRd32_950000!AVAcroALM_IsFeatureEnabled+0x644eb (009f7f4a)       //[不跳转]

009f7f0c 83f802          cmp     eax,2

009f7f0f 0f85f5010000    jne     AcroRd32_950000!AVAcroALM_IsFeatureEnabled+0x646ab (009f810a)       //[不跳转]

009f7f15 8b4608          mov     eax,dword ptr [esi+8]

009f7f18 0faf460c        imul    eax,dword ptr [esi+0Ch]

009f7f1c 89442418        mov     dword ptr [esp+18h],eax

009f7f20 8b4610          mov     eax,dword ptr [esi+10h]

009f7f23 83f803          cmp     eax,3

009f7f26 7e07            jle     AcroRd32_950000!AVAcroALM_IsFeatureEnabled+0x644d0 (009f7f2f)       //[跳转]



.........

009f7f2f b903000000      mov     ecx,offset <Unloaded_util.dll>+0x2 (00000003)             //ecx=0x3

009f7f34 2bc8            sub     ecx,eax                                                   //eax=0x0,ecx=0x3  

009f7f36 d3e7            shl     edi,cl                                                    //edi=7,逻辑左移3次,即edi=0x7*2*2*2=0x38

009f7f38 897c2428        mov     dword ptr [esp+28h],edi                                   //edi赋值给[esp+28]

009f7f3c 8b442428        mov     eax,dword ptr [esp+28h]

009f7f40 33ff            xor     edi,edi

009f7f42 85c0            test    eax,eax

009f7f44 897c2448        mov     dword ptr [esp+48h],edi

009f7f48 7f0e            jg      AcroRd32_950000!AVAcroALM_IsFeatureEnabled+0x644f9 (009f7f58)        //[跳转]



......... 

009f7f58 8b5620          mov     edx,dword ptr [esi+20h] ds:0023:021320ac=00000000

009f7f5b 2bc7            sub     eax,edi

009f7f5d 8d0c10          lea     ecx,[eax+edx]

009f7f60 89442440        mov     dword ptr [esp+40h],eax

009f7f64 33c0            xor     eax,eax

009f7f66 394c2418        cmp     dword ptr [esp+18h],ecx       //0x4000000e与0x38进行比较

009f7f6a 0f9ec0          setle   al

009f7f6d 0fb7c8          movzx   ecx,ax

009f7f70 6685c9          test    cx,cx

009f7f73 8b442440        mov     eax,dword ptr [esp+40h]

009f7f77 894c2438        mov     dword ptr [esp+38h],ecx

009f7f7b 740c            je      AcroRd32_950000!AVAcroALM_IsFeatureEnabled+0x6452a (009f7f89)    //[跳转]



..........



009f7f89 89442444        mov     dword ptr [esp+44h],eax ss:0023:0012ed40=03ddcf88

009f7f8d 8bc8            mov     ecx,eax

009f7f8f 33c0            xor     eax,eax

009f7f91 39460c          cmp     dword ptr [esi+0Ch],eax                //0x4000000e与0x38进行比较

009f7f94 89442440        mov     dword ptr [esp+40h],eax

009f7f98 0f8e05010000    jle     AcroRd32_950000!AVAcroALM_IsFeatureEnabled+0x64644 (009f80a3)   //[不跳转]

009f7f9e 8d2c1f          lea     ebp,[edi+ebx]

009f7fa1 8d1c7b          lea     ebx,[ebx+edi*2]

009f7fa4 2bd7            sub     edx,edi

009f7fa6 896c2420        mov     dword ptr [esp+20h],ebp

009f7faa 895c2424        mov     dword ptr [esp+24h],ebx

009f7fae 89542430        mov     dword ptr [esp+30h],edx

009f7fb2 eb08            jmp     AcroRd32_950000!AVAcroALM_IsFeatureEnabled+0x6455d (009f7fbc)   //[跳转]

009f7fb4 8b542430        mov     edx,dword ptr [esp+30h]                                         //循环开始

009f7fb8 8b442440        mov     eax,dword ptr [esp+40h]

009f7fbc 3bc1            cmp     eax,ecx                                                         //如果v_counter>bits_original_stream则

009f7fbe 0f8ddf000000    jge     AcroRd32_950000!AVAcroALM_IsFeatureEnabled+0x64644 (009f80a3)   //循环暂停

009f7fc4 8b6e0c          mov     ebp,dword ptr [esi+0Ch]                                        //0x4000000e赋值给ebp

009f7fc7 03c7            add     eax,edi

009f7fc9 03c2            add     eax,edx

009f7fcb 99              cdq

009f7fcc f7fd            idiv    eax,ebp

009f7fce 8d0439          lea     eax,[ecx+edi]                                                  //edx变成v_counter

009f7fd1 8b4e10          mov     ecx,dword ptr [esi+10h]

009f7fd4 83f903          cmp     ecx,3

009f7fd7 8d5c9644        lea     ebx,[esi+edx*4+44h]                                            //0x80大小缓冲区指针+v_counter*4+44h,赋值给ebx.

009f7fdb 7558            jne     AcroRd32_950000!AVAcroALM_IsFeatureEnabled+0x645d6 (009f8035)   //[跳转]



..........



009f8035 83f904          cmp     ecx,4

009f8038 7522            jne     AcroRd32_950000!AVAcroALM_IsFeatureEnabled+0x645fd (009f805c)

009f803a 8b4c241c        mov     ecx,dword ptr [esp+1Ch]

009f803e 8b542414        mov     edx,dword ptr [esp+14h]

009f8042 8d3c42          lea     edi,[edx+eax*2]

009f8045 8b03            mov     eax,dword ptr [ebx]

009f8047 51              push    ecx

009f8048 8b4c2428        mov     ecx,dword ptr [esp+28h]

009f804c 8bd5            mov     edx,ebp

009f804e e88df76200      call    AcroRd32_950000!PDFLTerm+0x23e0d0 (010277e0)

009f8053 8b7c244c        mov     edi,dword ptr [esp+4Ch]

009f8057 83c404          add     esp,4

009f805a eb23            jmp     AcroRd32_950000!AVAcroALM_IsFeatureEnabled+0x64620 (009f807f)

009f805c 8b13            mov     edx,dword ptr [ebx]

009f805e 52              push    edx

009f805f 8b542420        mov     edx,dword ptr [esp+20h]

009f8063 d3e0            shl     eax,cl

009f8065 52              push    edx

009f8066 8b54241c        mov     edx,dword ptr [esp+1Ch]



................



009f8035 83f904          cmp     ecx,4

009f8038 7522            jne     AcroRd32_950000!AVAcroALM_IsFeatureEnabled+0x645fd (009f805c) [br=1]       //[跳转]



................



009f805c 8b13            mov     edx,dword ptr [ebx]  ds:0023:021320d0=00000000

009f805e 52              push    edx

009f805f 8b542420        mov     edx,dword ptr [esp+20h]

009f8063 d3e0            shl     eax,cl

009f8065 52              push    edx

009f8066 8b54241c        mov     edx,dword ptr [esp+1Ch]

009f806a d3e5            shl     ebp,cl

009f806c 55              push    ebp

009f806d 50              push    eax

009f806e 8b442450        mov     eax,dword ptr [esp+50h]

009f8072 03c7            add     eax,edi

009f8074 d3e0            shl     eax,cl

009f8076 52              push    edx

009f8077 e8d4f76200      call    AcroRd32_950000!PDFLTerm+0x23e140 (01027850)                            //关键函数

009f807c 83c414          add     esp,14h

009f807f 8903            mov     dword ptr [ebx],eax                                     //函数返回值覆盖了一个DWORD

009f8081 8b442440        mov     eax,dword ptr [esp+40h]

009f8085 8344242402      add     dword ptr [esp+24h],2

009f808a 8344242001      add     dword ptr [esp+20h],1

009f808f 8b4c2444        mov     ecx,dword ptr [esp+44h]

009f8093 83c001          add     eax,1

009f8096 3b460c          cmp     eax,dword ptr [esi+0Ch]

009f8099 89442440        mov     dword ptr [esp+40h],eax

009f809d 0f8c11ffffff    jl      AcroRd32_950000!AVAcroALM_IsFeatureEnabled+0x64555 (009f7fb4)  //跳转回009f7fb4继续执行。

............................................................................................................

009f7ed8处[eax]=00 00 20 00 00 00 10
009f7f36处指令是对bits_original_stream进行计算 bits_original_stream=0x38,也即是说 编码00 00 20 00 00 00 10经过解码
后的值为0x38

009f7fd7 8d5c9644        lea     ebx,[esi+edx*4+44h]
此处指令的为 指针+v_counter*4+44h,得到的值为0x80大小缓冲区指针+0x44处的指针。
记住0x44这个值,在下面调试过程中需要用到。
伪代码如下:

代码:
for(;;) {

  if( v_counter >  bits_original_stream ) break;       //对应:009f7f91

  tmp_counter = v_counter % ParamX;                    //对应:009f7fcc

  lpCurrent = lpBuff + tmp_counter * 4 + 0x44;         //对应:009f7fd7

  iRetVal = mystery_func( *lpCurrent, variables...);   //对应:009f804e

  *lpCurrent = iRetVal;             //对应:009f807f

  v_counter++;                                         //对应:009f8093

  if( v_counter >= ParamX ) break;

}

call    AcroRd32_950000!PDFLTerm+0x23e140 (01027850) 即为mystery_func(神秘函数)
0x80大小的缓冲区假定Colors 参数的每个对象包含一个DWORD(即有0x4000000e个对象,但实际上只分配了0x80大小的缓冲区,很明显存在溢出),
因此这里为整个溢出利用的核心。
因为是整数溢出,分配的缓冲区实际上远远小于所需要的大小:0x4000000e*4+44h远大于0x80,当计数器在枚举对象时,就超出了缓冲区的结尾,触发溢出。
从伪代码可以看出,只有v_counter>=Colors 0x4000000e时,循环才会退出,但由于Colors的极大值,循环不会退出,这导致在
call    AcroRd32_950000!PDFLTerm+0x23e140 (01027850) 
中对每一个对象调用了原来的比特流进行处理。
整段内存从0x44的偏移开始被bits_original_stream*4覆盖。这里的偏移0x44指的的相对于0x80大小缓冲区的指针的偏移,为什么?
请看这里:
009f7fd7 lea     ebx,[esi+edx*4+44h] //lpBuff+v_counter*4+44h,赋值给ebx.
0x38*4=0xe0
被分配的堆块长度只有0x80,call    AcroRd32_950000!PDFLTerm+0x23e140 (01027850) 函数实际覆盖的大小为0xa4=0x44+0xe0-0x80
而实际上之后一小段内存被覆盖:
现在返回
009f7868  call    AcroRd32_950000!AVAcroALM_IsFeatureEnabled+0x64471 (009f7ed0) 
观察整数溢出后,堆块的内容究竟哪些地方被修改
1.溢出之前
代码:
02416aec 00 00 00 00 02 00 00 00 01 00 00 00 0e 00 00 40  ...............@

02416afc 00 00 00 00 02 00 00 08 02 00 00 08 0e 00 00 40  ...............@

02416b0c 00 00 00 00 ff ff ff ff 00 00 00 00 00 00 00 00  ................

02416b1c 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................

02416b2c 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................

02416b3c 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................

02416b4c 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................

02416b5c 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................

02416b6c 84 2f 41 02 94 67 21 01 00 00 00 00 6c 67 21 01  ./A..g!.....lg!.      //此处为pointer 1=0x0121676c

02416b7c 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................

02416b8c 00 00 00 00 14 d4 17 01 00 00 00 00 00 00 00 00  ................

02416b9c 00 00 00 00 00 00 00 00 00 00 00 00 6c 67 21 01  ............lg!.

02416bac 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................

02416bbc 00 00 00 00 14 d4 17 01 00 00 00 00 00 00 00 00  ................

02416bcc 00 00 00 00 00 00 00 00 00 00 00 00 70 bd 18 00  ............p...

02416bdc ff ff ff ff 00 00 00 00 00 00 00 00 00 00 00 00  ................

02416bec 00 00 00 00 84 2f 41 02 4c a2 24 01 c0 3b 3d 02  ...../A.L.$..;=.

02416bfc 40 da 24 01 14 03 00 00 78 8a 18 00 ff ff ff ff  @.$.....x.......     //此处为pointer 2=0x0124da40







2.溢出之后

02416aec 00 00 00 00 02 00 00 00 01 00 00 00 0e 00 00 40  ...............@

02416afc 00 00 00 00 02 00 00 08 02 00 00 08 0e 00 00 40  ...............@

02416b0c 38 00 00 00 ff ff ff ff 00 00 00 00 00 00 00 00  8...............

02416b1c 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................

02416b2c 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................

02416b3c 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................

02416b4c 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................

02416b5c 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................

02416b6c 84 2f 41 02 94 67 21 01 00 00 00 00 6d 67 21 01  ./A..g!.....mg!.   //此处为pointer 1=0x0121676d

02416b7c 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................

02416b8c 00 00 00 00 14 d4 17 01 00 00 00 00 00 00 00 00  ................

02416b9c 00 00 00 00 00 00 00 00 00 00 00 00 6c 67 21 01  ............lg!.

02416bac 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................

02416bbc 00 00 00 00 14 d4 17 01 00 00 00 00 00 00 00 00  ................

02416bcc 00 00 00 00 00 00 00 00 00 00 00 00 70 bd 18 00  ............p...

02416bdc ff ff ff ff 00 00 00 00 00 00 00 00 00 00 00 00  ................

02416bec 00 00 00 00 84 2f 41 02 4c a2 24 01 c0 3b 3d 02  ...../A.L.$..;=.

02416bfc 41 da 24 01 14 03 00 00 78 8a 18 00 ff ff ff ff  A.$.....x.......   //此处为pointer 2=0x0124da41  

02416aec 即是0x80大小缓冲区的指针,
从上可以看出,存在两个DWORD值被改变
1.偏移0x0c,0x0121676c变为0x0121676d 
2.偏移0x90,0x0124da40变为0x0124da41
此处偏移是相对于缓冲区的结尾,即:
[lpBuss+80+0x0c]=0x0121676c
[lpBuss+80+0x90]=0x0124da40

在调试过程中会出现,只存在偏移0x90处的地址被改变,而偏移0x0c处则没有0x0121676c,为一随机DWORD,概率不定,需注意
但不影响漏洞利用。

下面分析关键函数
............................................................................................................
代码:
009f8077 call    AcroRd32_950000!PDFLTerm+0x23e140 (01027850):

01027850 83ec0c          sub     esp,0Ch

01027853 53              push    ebx

01027854 b302            mov     bl,2

01027856 55              push    ebp

01027857 d2e3            shl     bl,cl

01027859 57              push    edi

0102785a bd01000000      mov     ebp,offset <Unloaded_util.dll> (00000001)

0102785f 8bf8            mov     edi,eax

01027861 80eb01          sub     bl,1

01027864 d3e5            shl     ebp,cl

01027866 3b7c2420        cmp     edi,dword ptr [esp+20h]

0102786a 885c240f        mov     byte ptr [esp+0Fh],bl

0102786e 7d75            jge     AcroRd32_950000!PDFLTerm+0x23e1d5 (010278e5)

01027870 0fb6c3          movzx   eax,bl

01027873 56              push    esi

01027874 89442414        mov     dword ptr [esp+14h],eax

01027878 eb0a            jmp     AcroRd32_950000!PDFLTerm+0x23e174 (01027884)



.....................



01027884 8bf7            mov     esi,edi                                                          //edi为v_counter

01027886 c1fe03          sar     esi,3                                                            //v_counter/2*2*2

01027889 03742420        add     esi,dword ptr [esp+20h]                                          //跳过多少字节

0102788d 8bcf            mov     ecx,edi                //计数器

0102788f 0fb616          movzx   edx,byte ptr [esi]                                               //读下一个字节

01027892 83e107          and     ecx,7                                                            //跳过多少比特在下个字节中

01027895 b808000000      mov     eax,offset <Unloaded_util.dll>+0x7 (00000008)

0102789a 2bc1            sub     eax,ecx

0102789c 2bc5            sub     eax,ebp                                                          //ebp总是为1

0102789e 8ac8            mov     cl,al

010278a0 d3ea            shr     edx,cl                                                          //使最短的比特作为 corresponding bit(对应位比特)

010278a2 23542414        and     edx,dword ptr [esp+14h]                                          //对应位bit为DWORD的值

010278a6 66837c242c00    cmp     word ptr [esp+2Ch],0

010278ac 740e            je      AcroRd32_950000!PDFLTerm+0x23e1ac (010278bc)                     //[跳转]



...................



010278bc 8b4c2430        mov     ecx,dword ptr [esp+30h] ss:0023:0012ecf8=00000000          //得到old_DWORD

010278c0 03ca            add     ecx,edx                                                    //new_DWORD= old_DWORD+corresponding bit

010278c2 8bd1            mov     edx,ecx

010278c4 22542413        and     dl,byte ptr [esp+13h]

010278c8 037c2428        add     edi,dword ptr [esp+28h]

010278cc 894c2430        mov     dword ptr [esp+30h],ecx                                    //保存new_DWORD

010278d0 8bc8            mov     ecx,eax

010278d2 d2e3            shl     bl,cl

010278d4 d2e2            shl     dl,cl

010278d6 f6d3            not     bl

010278d8 221e            and     bl,byte ptr [esi]

010278da 0ada            or      bl,dl

010278dc 3b7c2424        cmp     edi,dword ptr [esp+24h]

010278e0 881e            mov     byte ptr [esi],bl

010278e2 7c9c            jl      AcroRd32_950000!PDFLTerm+0x23e170 (01027880)

010278e4 5e              pop     esi

010278e5 8b44242c        mov     eax,dword ptr [esp+2Ch]

010278e9 5f              pop     edi

010278ea 5d              pop     ebp

010278eb 5b              pop     ebx

010278ec 83c40c          add     esp,0Ch

010278ef c3              ret

................................................................................................................

函数首先使用计数器的当前值取得一个比特,在这里认为缓冲区中每个对象都在流中存在对应的比特,称为"corresponding bit",此比特会以以下的方式转换成一个DWORD:

new_DWORD=old_DWORD+corresponding_bit
而corresponding_bit要么是0要么是1,因此new_DWORD要么以1递增要么保持不变
原来的流数据为"00 00 20 00 00 00 10",只有在0x12和0x33的位置,corresponding_bit才为1,因此只有0x12和0x33位置的DWORD才会以1的倍数递增
相对于ox80大小堆块的结尾的偏移以下DWORD将会被改变:
 偏移1=0x12*4+0x44-0x80=0x0c
 偏移2=0x33*4+0x44-0x80=0x90

这与之前分析的偏移0x0c和0x90处的DWORD被改变相一致。
...............................................................................................................

0×01_1 控制程序执行流程

2个DWORD的其中一个好像是一个在一段不断变化的内存位置的c风格结构指针,Adobe Reader在解析一个PDF时会使用这个结构,大多数情况下pointer2(偏移2,0x90)会指向这个结构,
但有时候偏移0x0c会指向这个结构的,这种情况下就只有偏移0x0c,而没有偏移0x90处的指针,不管什么情况下,指针的内容都为0x0124da40(溢出前)
通过之前的调试可以发现,溢出之前的pointer为0x0124da40,溢出之后就变成了0x0124da41

以下函数揭示如何通过改变函数指针来控制函数流程的:
.............................................................................

代码:
009d9ba0 8b4c2404        mov     ecx,dword ptr [esp+4]

009d9ba4 85c9            test    ecx,ecx

009d9ba6 7439            je      AcroRd32_950000!AVAcroALM_IsFeatureEnabled+0x46182 (009d9be1)

009d9ba8 8b4108          mov     eax,dword ptr [ecx+8]                                            //ecx+8为pointer:0x0124da40(正常情况)或者0x0124da41(overflow)

009d9bab 85c0            test    eax,eax

009d9bad 7432            je      AcroRd32_950000!AVAcroALM_IsFeatureEnabled+0x46182 (009d9be1)

009d9baf 813890000000    cmp     dword ptr [eax],offset <Unloaded_util.dll>+0x8f (00000090)     //[eax]与0x90进行比较

009d9bb5 762a            jbe     AcroRd32_950000!AVAcroALM_IsFeatureEnabled+0x46182 (009d9be1)

009d9bb7 8b8090000000    mov     eax,dword ptr <Unloaded_util.dll>+0x8f (00000090)[eax]         //eax=[eax+90]

009d9bbd 85c0            test    eax,eax

009d9bbf 7420            je      AcroRd32_950000!AVAcroALM_IsFeatureEnabled+0x46182 (009d9be1)   //

009d9bc1 668b542408      mov     dx,word ptr [esp+8]

009d9bc6 8b490c          mov     ecx,dword ptr [ecx+0Ch]                                      //ecx=[ecx+0c]

009d9bc9 66f7da          neg     dx

009d9bcc 6a04            push    4

009d9bce 1bd2            sbb     edx,edx

009d9bd0 83e204          and     edx,4

009d9bd3 52              push    edx

009d9bd4 51              push    ecx

009d9bd5 ffd0            call    eax {50000000}

..............................................................................................

正常情况下

代码:
0:000> dd 0124da40

0124da40  0000010c 00965450 00968cc0 00aae890

0124da50  01021e10 01024380 01021e40 01024430

0124da60  009671c0 010243e0 00971670 01022e80

0124da70  00a468c0 00970d10 0096f060 00964e80

0124da80  0096f7d0 0096fe30 00961ca0 0095c060

0124da90  010228e0 00000000 00000000 00000000

0124daa0  00000000 00000000 00000000 00000000

0124dab0  00000000 00960420 00000000 00000000

.........................................................
即使0x10c大于0x90(009d9baf指令),因为函数指针在0x90处偏移即0x0124dad0处的内容为0
函数跳往其他地方,不会执行到下方
代码:
0:000> dd 0124da40+90

0124dad0  00000000 00a03950 00a1ced0 01022c10

0124dae0  01022cc0 01022d40 00000000 00000000

溢出情况下:

代码:
0:000> dd 0124da41

0124da41  50000001 c0009654 9000968c 1000aae8

0124da51  8001021e 40010243 3001021e c0010244

0124da61  e0009671 70010243 80009716 c001022e

0124da71  1000a468 6000970d 800096f0 d000964e

0124da81  300096f7 a00096fe 6000961c e00095c0

0124da91  00010228 00000000 00000000 00000000

0124daa1  00000000 00000000 00000000 00000000

0124dab1  20000000 00009604 00000000 e0000000

........................................................

0x50000001大于0x90,而且0x0124da41对于0x90的偏移(0124dad1)=0x50000000很明显不为0x0,
程序流程就会转到0x50000000,即:
009d9bd5 ffd0            call    eax {50000000}

接下来做的就是在堆中进行填充,直到0x5000000这个地址处,程序会往在这个地址内执行,其内容就为填充的ShellCOde,
这里主要分析过程,填充过程及ShellCode执行过程就不再进行分析,有兴趣的话可以跟踪调试。
........................................................
0×02 Adobe Readr堆管理系统

这里解释为什么选择0x80作为堆块的大小,即为什么选择0x4000000e作为Colors的参数。
一般来说在Windows系统中堆分配最终会调用系统API RtlAllocateHeap来申请新的堆块,在这样一个情况下,对应用程序来说内存的返回值是不可预测的。但是Adobe Reader使用自身的堆管理例程,
当堆块的长度小于或等于0x80时,返回值并不会提交到系统级堆管理例程,相应的,Adobe Reader自身内存管理例程会重新利用堆块来应用被请求的分配长度。

以下为在"acrord32.dll"中 堆管理例程 片段(因IDA中查看分析此段代码更容易理解,故借用资料中代码,在WinDbg中首地址为0x009542dc)
代码:
.text:003042DC                 push    offset stru_E886F0 ; lpCriticalSection

.text:003042E1                 mov     [esp+1Ch+allocate_len], offset stru_E886F0

.text:003042E9                 call    ds:EnterCriticalSection

.text:003042EF                 cmp     edi, 80h        ; allocate_len>0x80?

.text:003042F5                 mov     [esp+18h+var_4], 0

.text:003042FD                 ja      short loc_30433B ; allocate_len=0x80, no jumping here

.text:003042FF                 movzx   eax, ds:byte_BFD898[edi]

.text:00304306                 mov     ecx, [esi+eax*4+0Ch]   ; get structure pointer which manages all recycled 0x80-length blocks

.text:00304306                                           

.text:0030430A                 mov     eax, [ecx+4]      ; the first recycled 0x80-length block

.text:0030430D                 test    eax, eax

.text:0030430F                 jz      short loc_30432F

.text:00304311                 mov     esi, eax

.text:00304313                 mov     eax, [eax+4]      ; next block

.text:00304316                 test    eax, eax

.text:00304318                 mov     edx, [esi-4]

.text:0030431B                 mov     [ecx+4], eax      ; take off the first block from the list

.text:0030431E                 jz      short loc_304326   ; how many recycled blocks have been reused

.text:00304320                 mov     dword ptr [eax], 0

.text:00304326

.text:00304326 loc_304326:                             ; CODE XREF: acro_allocate_routine+7Ej

.text:00304326                 add     dword ptr [edx+4], 1 ; how many recycled blocks have been reused

.text:0030432A                 jmp     loc_3043C4      ; exit, return the first block for use

........................................................
程序并没有进入系统级管理例程中来分配一段新的内存,它只是重新使用被应用程序标记为"free"的堆块,实际上系统并没有释放这段内存,
因此被重新使用的堆块的内存值相当容易的预测到,或者说相对稳定。
这表明存在一个相当有效的方法利用此漏洞。
........................................................



0×03 总结
   到此整个漏洞的触发过程就分析完毕了,回过头来从整体看,Adobe Reader在解析PDF过程对Colors的每个参数进行解码(已被flate编码),并为其分配一个DWORD的空间,
当Colors为极大值时,并没有很好的计算需要分配的堆块的大小,导致整数溢出,溢出了堆中某一个C结构的指针,并使其错误的执行到堆中,至此很容易的被利用。


PS:谢谢,楼下的兄弟momoomo提供POC

  • 标 题:答复
  • 作 者:momoomo
  • 时 间:2009-12-09 14:39:26

潜水了很多日子,浮上水面贡献贡献 :)
calc.exe poc

上传的附件 calc-1.rar
该样本的解压密码:PEDIY