作者:Nicolas Joly
译者:riusksk(泉哥:http://riusksk.blogbus.com)
本文旨在与大家分享一些关于最近Adobe Acrobat/Reader 0-Day exploit (CVE-2010-2883)的技术细节。在VUPEN组织中,我们分析了大量的漏洞和0day,并且针对各类程序和操作系统编写出相当完善的exploit代码。近几月来,我们编写了许多关于Adobe Reader漏洞的利用代码,并且均可绕过DEP保护。
那为何本0day exploit会如此有趣呢,以致我们还需著文一篇呢?因为它使用了一些令人难忘的奇淫异技,以及一些非常手段以此绕过DEP和ASLR保护,这在我们平时是很少见的。也许部分人已经知晓,在本漏洞中,经过恶意构造的PDF文件可利用”CoolType.dll”中的非安全函数”strcat()”导致溢出漏洞的发生。当PDF文件以特制表段来嵌入字体时,即可以此触发漏洞。
.text:0803DCF9 push ebp .text:0803DCFA sub esp, 104h .text:0803DD00 lea ebp, [esp-4] .text:0803DD04 mov eax, dword_8230FB8 .text:0803DD09 xor eax, ebp .text:0803DD0B mov [ebp+108h+var_4], eax .text:0803DD11 push 4Ch .text:0803DD13 mov eax, offset loc_8184A54 .text:0803DD18 call __EH_prolog3_catch // set up an SE handler .text:0803DD1D mov eax, [ebp+108h+arg_C] .text:0803DD23 mov edi, [ebp+108h+arg_0] .text:0803DD29 mov ebx, [ebp+108h+arg_4] .text:0803DD2F mov [ebp+108h+var_130], edi .text:0803DD32 mov [ebp+108h+var_138], eax
当字体文件中的SING表段被找到后,程序执行以下指令:
.text:0803DD74 push offset aSing ; "SING" .text:0803DD79 push edi ; int .text:0803DD7A lea ecx, [ebp+108h+var_12C] .text:0803DD7D call sub_8021B06 ... .text:0803DD9F loc_803DD9F: .text:0803DD9F add eax, 10h .text:0803DDA2 push eax // long string following the SING table .text:0803DDA3 lea eax, [ebp+108h+Dest] .text:0803DDA6 push eax // ~256 bytes stack buffer .text:0803DDA7 mov [ebp+108h+Dest], 0 .text:0803DDAB call strcat // insecure!
为了利用此漏洞,通常黑客都是通过覆写返回地址或者SE handler,但这里它可能就行不通了。因为它存在一个stack cookie,从而阻止返回地址被利用,而当进入一个设置了异常处理例程的函数后,其栈情况如下:
| SE | | NEXT SE | | DEST | <-- vulnerable buffer | ... | | COOKIE | | EBP | | RET ADD |
如上所示,如果你覆写了返回地址,那么cookie也会被改写,进而阻止恶意代码的运行。而如果你继续覆写下去直到触发异常,那么你就可以步过cookie,然后将控制权交由SE handler处理。显然,这种解决方法并没有什么特别之处。还有另一种方法,就是覆盖函数中的参数或者变量。这里攻击者也正好是使用这种方法,当他覆盖了栈空间后,在BIB.dll中出现首次异常,因为下一个调用函数使用了这一个覆盖的参数:
.text:0803DDB0 pop ecx .text:0803DDB1 pop ecx .text:0803DDB2 lea eax, [ebp+108h+Dest] .text:0803DDB5 push eax .text:0803DDB6 mov ecx, ebx // ebx actually points to arg_4, which is overwritten .text:0803DDB8 call sub_8001243
然后:
.text:070013F7 lea eax, [ecx+1Ch] // ecx = [arg_4] .text:070013FA mov [ebp+var_8], eax .text:070013FD mov eax, [ebp+var_8] .text:07001400 lock dec dword ptr [eax] // first crash here
若指向了一个无效地址,那么首次异常将会在0x07001400地址触发,进而执行SE handler,最终导致Adobe Acrobat/Reader崩溃。因此必须设置一个有效指针,至少得允许程序可以在内存中任意递减一个dword字节,这也着实有效(例如CVE-2008-4812)。但即便如此,也很难编写出一个可以绕过DEP和ASLR的exploit。不过sub_8016BDE给我们带来了希望,它将两个指针压入栈中作为参数:
.text:0803DEA9 loc_803DEA9: .text:0803DEA9 .text:0803DEA9 lea eax, [ebp+108h+var_124] .text:0803DEAC push eax .text:0803DEAD push ebx .text:0803DEAE push edi // ebx and edi point to arg_4 and arg_0 .text:0803DEAF call sub_8016BDE
上面这段代码实际上是以arg_0为参数来调用sub_8016BDE,而sub_8016BDE将返回0或者一个指针。如果返回0,那么程序将会跳到地址0x080172CE,然后退出函数。但如果是返回一个指针,那么sub_801BB21(以arg_0作为其中的一个参数)将被调用。
.text:08016C2B push edi .text:08016C2C mov [ebp+664h+var_668], ebx .text:08016C2F mov [ebp+664h+var_694], ebx .text:08016C32 mov [ebp+664h+var_678], ebx .text:08016C35 call sub_801BB1C // return 0 or a pointer .text:08016C3A cmp eax, ebx .text:08016C3C pop ecx .text:08016C3D mov [ebp+664h+var_67C], eax .text:08016C40 jz loc_80172CE .text:08016C46 push 1 .text:08016C48 push ebx .text:08016C49 push ebx .text:08016C4A lea eax, [ebp+664h+var_678] .text:08016C4D push eax .text:08016C4E lea eax, [ebp+664h+var_694] .text:08016C51 push eax .text:08016C52 push edi .text:08016C53 push [ebp+664h+var_67C] .text:08016C56 call sub_801BB21 // this call must be reached
函数sub_801BB1C执行后:
.text:0801BA57 mov eax, dword_823A728 .text:0801BA5C test eax, eax // the attacker does not control this pointer .text:0801BA5E jz short locret_801BA73 .text:0801BA60 mov ecx, [esp+arg_0] // however ecx may point to a controlled dword .text:0801BA64 mov ecx, [ecx+4] .text:0801BA67 .text:0801BA67 loc_801BA67: .text:0801BA67 cmp ecx, [eax+4] .text:0801BA6A jz short locret_801BA73 .text:0801BA6C mov eax, [eax+8] .text:0801BA6F test eax, eax .text:0801BA71 jnz short loc_801BA67 .text:0801BA73 .text:0801BA73 locret_801BA73: .text:0801BA73 .text:0801BA73 retn
这里ECX必须等于[eax+4],以便使函数能够返回除NULL以外的值,那么eax+4 必须指向哪个值呢?
0x0000006c 0x0000006b 0x00000070 0x0000006f 0x0000006d
由于strcat要被利用,因此null字节是不能使用的!此处令arg_0 + 4指向0x0000006D,这意味着此值是不可随意更改的,以便限制拷入栈中的字节数。这也正是exploit作者没有覆盖整个栈空间,而只覆盖部分空间的原因所在。最后进入sub_801BB21和sub_808B116:
.text:0808B2E3 mov eax, [edi+3Ch] // edi = arg_0, but edi + 3Ch points to a stack pointer, // itself pointing to a controlled value .text:0808B2E6 cmp eax, ebx .text:0808B2E8 mov [esi+2F4h], eax .text:0808B2EE mov [esi+2F8h], ebx .text:0808B2F4 mov [ebp+var_4], ebx .text:0808B2F7 jnz short loc_808B300 .text:0808B2F9 .text:0808B2F9 loc_808B2F9: .text:0808B2F9 xor al, al .text:0808B2FB jmp loc_808B594 .text:0808B300 .text:0808B300 loc_808B300: .text:0808B300 lea ecx, [ebp+var_4] .text:0808B303 push ecx .text:0808B304 push ebx .text:0808B305 push 3 .text:0808B307 push eax .text:0808B308 call dword ptr [eax] // EIP is redirected here
故事到此就可以结束了,但关于ROP技术还是比较少见的。取icucnv36.dll上的地址并不能实现各版本的通用性(至少在9.2.0版本以上),并且不能绕过ASLR。不过有位exploit作者对同一恶意pdf文件进行了改进,他在上面提及的dll文件上(阅读器版本>= 9.20)使用了ROP技术,借此达到攻击目的。这一DLL确实有趣,他并不是使用了函数VirtualAlloc,VirtualProctect,HeapCreate,WriteMemory,甚至连LoadLibrary也没有,这个利用起来有点复杂。他是通过查找并使用了以下函数:
4A84903C CreateFileA // create the file iso88591 4A849038 CreateFileMappingA // attrib RWE 4A849030 MapViewOfFile // load this file in memory with RWE flags 4A849170 memcpy // copy the payload
攻击者是想利用ROP模块进行堆喷射,而其后用shellcode填充。他先在磁盘上创建一个文件(iso88591),然后以RWE属性将其加载到内存中,接着将payload复制到内存,最后执行shellcode。这一exploit先将ESP指向欲喷射的地址:
.text:4A80CB38 add ebp, 794h .text:4A80CB3E leave .text:4A80CB3F retn
返回后来到:
.text:4A82A714 pop esp // esp = 0x08852030 .text:4A82A715 retn
0x08852030实际指向一块由同一ROP地址所覆盖的空间,以实现栈喷射:
接着返回到ROP地址,然后将所创建的文件映射到一块可执行的内存页,该地址pop入eax中,然后以下列指令实现调用:
.text:4A80B692 jmp dword ptr [eax]
现在你可以注意到动态参数已被写入栈中:
.text:4A80A8A6 and dword ptr [2*ebx + esp], edi .text:4A80A8A9 jnz short loc_4A80A8AE ... .text:4A80A8AE loc_4A80A8AE: .text:4A80A8AE cmp al, 2Fh .text:4A80A8B0 jz short loc_4A80A8AB .text:4A80A8B2 cmp al, 41h .text:4A80A8B4 jl short loc_4A80A8BA ... .text:4A80A8C8 loc_4A80A8C8: .text:4A80A8C8 .text:4A80A8C8 xor al, al .text:4A80A8CA retn
最后它调用memcpy,然后跳到payload执行任意代码,即使开启了DEP和ASLR,亦均可无视。
至于此漏洞是通过fuzzing还是静态分析挖掘到,这就不得而知了。但很显然,攻击者对此exploit的巧妙设计,足以表明作者对Acrobat,LiveCycle,Javascript和ROP技术熟知的深厚功底,并对调试器的运用也是相当娴熟。
事实上,这是一个典型的案例,对于同一DLL文件,同一个exploit文件,攻击者依然能够在在不同版本的程序、操作系统(windows XP,Vista,7)上执行成功,即使开启了各类安全机制,依然能够凑效。
注意,存在同类安全问题的DLL文件也存在于微软的主产品中……
|