MeePwn CTF 2018 Quals babysandbox, house_of_card, secure_message, 0xBAD MINTON writeup
はじめに
TokyoWesternsで参加して5920ptsで6位だった。finalsに行ける順位なのでベトナムに行くかもしれない。ワールドカップの試合の結果を予想して当てると点数が貰えたりする良いCTFだった。 共同で解いた分を含めて4問解いた。house_of_cardとsecure_messageはfirst solveだったので良かった。
babysandbox
pythonで書かれたサーバ部分では、投げられたペイロードに対してシステムコールのチェックを行っている。 open, read, write, execveといった、よくあるシステムコールが禁止されている。 チェックを通るとバイナリが実行される。 実行結果は、実行が成功したかどうかしか知ることができない。つまりバイナリ側の出力は分からない。
バイナリはシェルコードを受け取り実行するだけである。
以上から、システムコールが制限された状況でシェルコードを書くだけである。 方針は単純で、open, read, writeは禁止されているので、openat, readv, writevで代用する。 出力の受け取りは、socketを作ってサーバーで待ち受けるようにする。
MeePwn CTF 2018 Quals babysandbox · GitHub
% nc -lv 31337
Listening on [0.0.0.0] (family 0, port 31337)
Connection from [178.128.100.75] port 31337 [tcp/*] accepted (family 2, sport 42588)
MeePwnCTF{Unicorn_Engine_Is_So_Good_But_Not_Perfect}
(Unicorn関係あったか?)
house_of_card
first solveだった。ログを見ると問題が公開されて1時間程度で解けていた。
実行すると以下のようにnoteを作ったり編集したり削除ができる。編集と削除を行うときにnoteの中身を見ることができる。noteは以下の構造体で管理される。バグは、edit時に作った時のサイズよりも0x40多く指定しても問題ないので、heap bofが起きることである。
# ./house_of_card
============ 📚 Notes 📚 ============
1. New Note
2. Edit Note
3. Del Note
4. Quit
⛩
struct note {
char name[0x40];
int length;
char description[length];
};
struct node {
struct note* note;
struct node* own;
struct node* next;
};
まずは、libcのアドレスをリークさせるためにチャンクのサイズを書き換えてオーバーラップを起こした。リーク後は、単方向リストを形成する方の構造体のポインタを書き換えて、__free_hookを書き換えた。
MeePwnCTF 2018 Quals house_of_card · GitHub
# python exploit.py r
[*] '/root/ctf/MeePwnCTF/House_of_Card/house_of_card'
Arch: amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
FORTIFY: Enabled
[+] Opening connection to 178.128.87.12 on port 31336: Done
[*] Pwning
[*] libc_base: 0x7f01a3333000
[*] Switching to interactive mode
>$ cat flag
MeePwnCTF{no_moreeeeee_h43p}
secure_message
これもfirst solveだった。
適当にやっていたら解けたのでよくわかっていない。
urandomのfdを保持している箇所は、4番目のユーザーのパスワードを最大で入れるとnull終端時に0で上書きされる。つまりランダムっぽくしたい部分を任意にできる。
messageはmmap()で取得された領域が使用され、第一引数はurandomから呼んでランダムになるはずである。上記のバグで任意の第一引数でmmap()を実行できる。MAP_FIXEDが付いているので、おおよそ第一引数通りのアドレスが取得できる。
edit時にサイズを変えるとsegvで落ちるパターンがあり、落ちないように適当にやっていたら解けた。
MeePwn CTF 2018 Quals secure_message · GitHub
# python exploit.py r
[*] '/root/ctf/MeePwnCTF/secure_message'
Arch: amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
FORTIFY: Enabled
[+] Opening connection to 178.128.87.12 on port 31337: Done
[*] Pwning
[*] bin_base: 0x557bac867000
[*] libc_base: 0x7fb8da3da000
[*] heap_base: 0x557bacb57000
[*] Switching to interactive mode
$ cat /home/sm/flag
MEEPWNCTF{MAP_FIXED_1s_v3ry_dangerous}
0xBAD MINTON
misc(web+pwn)。 pwnパートをやった。フロントエンドではアカウントのregister/loginができ、アカウントごとに固有のトークンが付与される。 3回までenrollでき、4回目以降は普通ではできなかった。
リスティングが有効でファイル一覧が取得できた。ファイル一覧では各種phpファイルとTODO.txt、backend_serverが見れる。
TODOには、178.128.84.72:9997で待ち受けている旨が書かれている。
実際にアクセスしてみると、backend_serverが動いていた。
backend_serverは、トークンをもとにDBからアカウントの情報を取得し諸々の処理を行う。
enrollしている場合、enrollした回数だけstack上にペイロードを投げることができる。ペイロードはstack上に保存される。
backend_serverのバグは、4回以上enrollしている場合stack bofとなる点である。しかし、SSPが有効なのでROPはできない。
フラグは事前にbss上に直置きされる。No PIEなのでアドレスは固定である。
方針は単純で、webパートで4回以上enrollしてstack bofを起こし、argv[0] leakを行う。チームメンバが4回enrollしたアカウントを作ってくれたので、あとはやるだけだった。pwnパートは以下の通りである。stderrがネットワーク越しに漏れてこないと面倒だったが特に気にしなくて良かった。
MeePwn CTF 2018 Quals 0xBAD MINTON · GitHub
# python exploit.py r
[*] '/root/ctf/MeePwnCTF2018/0xBADMINTON/backend_server'
Arch: amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x400000)
[+] Opening connection to 178.128.84.72 on port 9997: Done
[*] Pwning
[*] Switching to interactive mode
Okie let's recheck your courses again:
#0 aaaaaaaaaaaaaaaa
#1 aaaaaaaaaaaaaaaa
#2 aaaaaaaaaaaaaaaa
#3 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxp@`
Okay! Let's warm-up
----------------BADMINTON TRAINING----------------
1. Push-up
2. Running
3. Give up
> *** stack smashing detected ***: MeePwnCTF{web+pwn+oldschool+stuff+stack+terminated}
terminated
[*] Got EOF while reading in interactive