picoCTF 2021 - Most Cookies

WriteUp: picoCTF 2021 - Most Cookies

はじめに

こんにちは。CTF初心者のロイといいます。
今回はpicoCTFのMost Cookies(Medium, Web Exploitation)を解いたので、WriteUpを残しておきます。
解法だけでなく、なるべく思考の過程を残したいと思っていますので、ご参考になれば幸いです。

問題概要

Descriptionによると「私の暗号化は完璧だ!FlaskのセッションCookiesは完全に安全なはずだ!」といった感じです。

server.pyというpythonコードファイルと、mecury.picoctf.netのURLが記載されてますね。
リンクをブラウザで開いてみると、こんな感じです。

picoCTF MostCookies

pythonファイルも開いてみてみましょう。

冒頭部分しか載せませんが、flaskを使っていますね。

picoCTF MostCookies flask01

また、flag_value変数にflagファイルの内容を入れています。

flaskアプリの動きを調べる

まずはGUIからなんか入れてボタンを押そうかと思いましたが、今回に関してはpythonファイルがあるので、そちらでflaskアプリの構造を調べた方が、flaskアプリの動きを掴みやすいと思いました。

server.pyの中身をみると、/, /search, /reset, /display の4つのルートがあるようです。

先ほど見つけたflag_value変数を検索すると、/displayルートで、flag関数が実行され、check変数が"admin"であるときに、flag.htmlがレンダリングされる動きなのがわかりました。

picoCTF MostCookies flask02

で、check変数 = session変数の鍵"very_auth"であると。

つまり、session変数の鍵"very_auth"が "admin" であれば、flagが取れそうということになります。

そして、session["very_auth"]はどこで組み立てられているのか、コードを見てみます。

すると、/searchルートで、POSTリクエストを受け取ったときに、session["very_auth"]が組み立てられているようです。

picoCTF MostCookies flask03

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

cookie_namesの中を見てみましたが、"admin"の文字は存在せず。。。

picoCTF MostCookies flask04

つまり、普通にGUIからflagは取れない(どこかでsession["very_auth"]を"admin"にする必要がある)ことがわかりました。

セッションCookieってどんな値なのかな?

ブラウザの開発者ツールを使って、セッションCookieを変更して/displayにリクエストを投げれないか?と思いました。

一旦、/displayにアクセスしてセッションCookieにどんな値が入るか確認します。

picoCTF MostCookies session

本来であれば、/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の中にいませんでしたっけ?

picoCTF MostCookies flask05

いました。ランダムで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として使っているみたいなので、当たるまでリクエストを投げ続けたいと思います。

今回は下記スクリプトを書いて投げまくりました。別に手動でも全然いい。

picoCTF MostCookies code

そして、結果がこちらです!

picoCTF MostCookies flag

無事flagをゲットしました!

参考資料

flaskのセッションCookieについて:https://qiita.com/juno_rmks/items/a707228a0682f529298d

TECH BLOG BY OZAKI