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
编码是什么编码?查阅相关资料说是"图像解码器",莫衷一是.
在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; }
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
从上可以看出,存在两个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