作者: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
// setup 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 pusheax
// ~256 bytes stack buffer
.text:0803DDA7 mov [ebp+108h+Dest], 0
.text:0803DDAB callstrcat
//insecure!
为了利用此漏洞,通常黑客都是通过覆写返回地址或者SEhandler,但这里它可能就行不通了。因为它存在一个stackcookie,从而阻止返回地址被利用,而当进入一个设置了异常处理例程的函数后,其栈情况如下: | 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,最终导致AdobeAcrobat/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 pushedi
// 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 callsub_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 callsub_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 astack 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]
// EIPis redirected here
故事到此就可以结束了,但关于ROP技术还是比较少见的。取icucnv36.dll上的地址并不能实现各版本的通用性(至少在9.2.0版本以上),并且不能绕过ASLR。不过有位exploit作者对同一恶意pdf文件进行了改进,他在上面提及的dll文件上(阅读器版本>= 9.20)使用了ROP技术,借此达到攻击目的。这一DLL确实有趣,他并不是使用了函数VirtualAlloc,VirtualProctect,HeapCreate,WriteMemory,甚至连LoadLibrary也没有,这个利用起来有点复杂。他是通过查找并使用了以下函数: 4A84903CCreateFileA
// create the file iso88591
4A849038 CreateFileMappingA
//attrib RWE
4A849030 MapViewOfFile
//load this file in memory with RWE flags
4A849170memcpy
//copy the payload
攻击者是想利用ROP模块进行堆喷射,而其后用shellcode填充。他先在磁盘上创建一个文件(iso88591),然后以RWE属性将其加载到内存中,接着将payload复制到内存,最后执行shellcode。这一exploit先将ESP指向欲喷射的地址: .text:4A80CB38 add ebp, 794h
.text:4A80CB3E leave
.text:4A80CB3F retn
返回后来到: .text:4A82A714 popesp
// esp = 0x08852030
.text:4A82A715 retn
0x08852030实际指向一块由同一ROP地址所覆盖的空间,以实现栈喷射: .text:4A801064 retn
接着返回到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文件也存在于微软的主产品中…… |