WriteUp: picoCTF 2021 - Most Cookies
はじめに
こんにちは。CTF初心者のロイといいます。
今回はpicoCTFのMost Cookies(Medium, Web Exploitation)を解いたので、WriteUpを残しておきます。
解法だけでなく、なるべく思考の過程を残したいと思っていますので、ご参考になれば幸いです。
問題概要
Descriptionによると「私の暗号化は完璧だ!FlaskのセッションCookiesは完全に安全なはずだ!」といった感じです。
server.pyというpythonコードファイルと、mecury.picoctf.netのURLが記載されてますね。
リンクをブラウザで開いてみると、こんな感じです。

pythonファイルも開いてみてみましょう。
冒頭部分しか載せませんが、flaskを使っていますね。

また、flag_value変数にflagファイルの内容を入れています。
flaskアプリの動きを調べる
まずはGUIからなんか入れてボタンを押そうかと思いましたが、今回に関してはpythonファイルがあるので、そちらでflaskアプリの構造を調べた方が、flaskアプリの動きを掴みやすいと思いました。
server.pyの中身をみると、/, /search, /reset, /display の4つのルートがあるようです。
先ほど見つけたflag_value変数を検索すると、/displayルートで、flag関数が実行され、check変数が"admin"であるときに、flag.htmlがレンダリングされる動きなのがわかりました。

で、check変数 = session変数の鍵"very_auth"であると。
つまり、session変数の鍵"very_auth"が "admin" であれば、flagが取れそうということになります。
そして、session["very_auth"]はどこで組み立てられているのか、コードを見てみます。
すると、/searchルートで、POSTリクエストを受け取ったときに、session["very_auth"]が組み立てられているようです。

そうすると、入力フォームに"admin"を入れればflagゲットか?と思いましたが、ifの条件に and request.form["name"] in cookie_names: とあります。
cookie_namesの中を見てみましたが、"admin"の文字は存在せず。。。

つまり、普通にGUIからflagは取れない(どこかでsession["very_auth"]を"admin"にする必要がある)ことがわかりました。
セッションCookieってどんな値なのかな?
ブラウザの開発者ツールを使って、セッションCookieを変更して/displayにリクエストを投げれないか?と思いました。
一旦、/displayにアクセスしてセッションCookieにどんな値が入るか確認します。

本来であれば、/searchでsession["very_auth"]が組み立てますが、それをスキップしたので、どんな値になってるでしょうか。
セッションCookieの値は、"eyJ2ZXJ5X2F1dGgiOiJibGFuayJ9.aCRJkw.duMd182gzaVzvai7egxfkd8Bw7I" でした。
これをデコードしてみると、{"very_auth":"blank"}h$In1|W;ڋ;
という内容でした。
ここでflaskのセッションCookieについてお勉強しました。
参考にさせて頂いたリンクは末尾に記載しておきます。
flaskのセッションCookieはドット区切りで3部構成になっています。
eyJ2ZXJ5X2F1dGgiOiJibGFuayJ9.aCRJkw.duMd182gzaVzvai7egxfkd8Bw7I
「ペイロード」.「タイムスタンプ」.「署名」 という構成になっています。
1つ目の「ペイロード」とは、base64エンコードされたJSON形式のデータです。
(今回の例の"eyJ2ZXJ5X2F1dGgiOiJibGFuayJ9"は{"very_auth":"blank"}をbase64エンコードしたもの)
2つ目は「タイムスタンプ」とのこと。
3つ目の「署名」は、「ペイロード.タイムスタンプ」のbase64をSECRET_KEYで署名した値とのことでした。
おや?SECRET_KEYって、server.pyの中にいませんでしたっけ?
いました。ランダムでcookie_namesから選ばれるようですね。
セッションCookieを作る
セッションCookieを作る方法はみえてきましたが、具体的にどうするか。
flask-unsignというflaskセッションの解析ツールがあるので、そちらをインストールします。
$ pip install flask-unsign
まずは、このflask-unsignを使ってセッションCookieを生成してみましょう。
$ flask-unsign --sign --cookie '{"very_auth":"admin"}' --secret "snickerdoodle"
eyJ2ZXJ5X2F1dGgiOiJhZG1pbiJ9.aCSPlw.NC2omXyLDtH39rnTKE4eqd-GOhE
ドット区切りのセッションCookieが生成されました。
SECRET_KEYが当たるまで投げまくる
先ほど見たように、cookie_namesリストの中の文字列をランダムでSECRET_KEYとして使っているみたいなので、当たるまでリクエストを投げ続けたいと思います。
今回は下記スクリプトを書いて投げまくりました。別に手動でも全然いい。
そして、結果がこちらです!

無事flagをゲットしました!
参考資料
flaskのセッションCookieについて:https://qiita.com/juno_rmks/items/a707228a0682f529298d