IPFactory Welcome CTF 2022
新入生向けのCTFをIPFactoryで開催。私はまたWebの作問をしました。
今年も初心者に楽しんでもらえるように、スコープを狭くして、問題の誘導も結構頑張って考えました。
今年はヒントも結構考えています。
難易度の想定は以下のような感じ。
hardも1問作ってはいましたが、easy < medium <<<<<<<<<<<<<<<<< hard
みたいになってたので没にしました。
難易度 | 対象 |
---|---|
easy | セキュリティ初めての人向け。調べたらすぐわかるくらい。 |
medium | ちょっと頭を使う問題 ~ ソースを読まなきゃ解けない |
Webは全4問、1問は後輩に作問してもらいました。
ソースはflag以外全配布、先日のWSL2環境構築会に参加してくれた一年生はDockerを利用してローカルで検証もできるようにしました。
Web writeup
THE WORLD - warmup, easy 200pt ( 7 Solves )
あ...ありのまま 今 起こった事を話すぜ!
『おれはやつの前で階段を登っていた
と思ったらいつのまにか降りていた』
Hint 1
/up
にアクセスした際に、強制的に/down
にリダイレクトさせられています。
ブラウザのリダイレクトの仕組みはどうなっているでしょうか。調べてみましょう。
Hint 2
/THE WORLD/python/src/main.py
の12行目から16行目が/upの時に動くソースです。
リダイレクトに必要なLocationヘッダーの他にもヘッダを追加していますね。ヘッダーはどうやって見れば良いでしょうか。
welcome問題です。
アクセスすると以下のような表示。
登る・降りるのリンクがあり、それぞれ/up
, /down
に遷移します。
<a href="/up">登る</a> / <a href="/down">降りる</a><br>
階段を登ればflagをくれるようなので、登ってみましょう。
『おれはやつの前で階段を登っていたと思ったらいつのまにか降りていた』というやつです。
確かに/up
に飛んだはずですが、URLバーを見るとなぜか/down
に居ます。
/up
にアクセスした際に動くコードを見てみましょう。
@app.route("/up") def theworld(): resp = make_response() resp.headers['Flag'] = os.environ['flag'] resp.headers['Location'] = '/down' return resp, 302
resp.headers['Location'] = '/down'
という部分の所為で/down
にリダイレクトさせられてしまっています。
他に、resp.headers['Flag'] = os.environ['flag']
というのもありますね。
/down
へのリダイレクト命令の他にも、flagがくっついているようです。
resp.headers
等で調べると、どうやらHTTPヘッダーというものらしいことがわかります。
HTTPヘッダーを見る方法があればflagが見れそうです。
HTTPヘッダーを見る方法はいろいろありますが、今回はブラウザで見る方法をやってみます。
ブラウザにフォーカスしている状態でF12キーを押すと、「開発者ツール」が開けます。
Networkというタブを開いた状態で、トップページの"登る"リンクをクリックしてみましょう。
/up
と/down
のログがでてきます。今見たいのは/up
の時のレスポンスなので、/up
をクリック。
Response Headersの中にLocation: /down
とFlag: ...
があります。
flag{Or3d4k3n0_Z1k4nd4z3}
CVE-2021-41773 - medium 300pt ( 2 Solves )
去年の年末、apacheに深刻な脆弱性が見つかったと聞きました。。
/flag.txt
を見られると困るのですが、大丈夫でしょうか・・・?https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-41773
Hint 1
「CVE-2021-41773 exploit」「CVE-2021-41773 検証」等で調べると情報がたくさんでてきます。 肝心の場所が隠されている事が多いですが、根気強く調べてみましょう。
CVE-2021-41773、つまりapacheのパストラバーサルです。
原理を理解しようとするとURLエンコードや相対パスの概念を理解する必要がありますが、再現するだけならそんな必要もありません。
「CVE-2021-41773 exploit」とかで根気よく調べると、次のようなペイロードが見つけられると思います。
http://$host/cgi-bin/.%2e/.%2e/.%2e/.%2e/etc/passwd
とりあえずこのペイロードに沿ってリクエストを送信してみましょう。
ブラウザでhttp://localhost:8080/cgi-bin/.%2e/.%2e/.%2e/.%2e/etc/passwdとかにアクセスしても、ブラウザがhttp://localhost:8080/etc/passwdに最適化してしまうのでcurlを使います。
この辺のツールの選択も、調べ方によってはすぐ出るかと思います。
/etc/passwd
が盗れました。
問題文では/flag.txt
だと言っているので、/flag.txt
を見ます。
flagゲット!
flag{4p4ch3_p4th_tr4v3rs4l}
# PIYOTASO - medium 300pt ( 2 Solves )
ひよこの管理者になりましょう。
2年前にISCCTFで出題したcrackjwt のjwtじゃないバージョンです。
今回もぴよたそ の画像をお借りしています。
適当に名前を入力してログインすると、flagを閲覧する権限がないと言われてしまいます。
該当コードを見てみましょう。
<?php $status = json_decode('{"isAdmin": false, "name": "' . $_POST['name'] . '"}', true); if ($status["isAdmin"] == false) { echo '<img src="/gif/nyoronyoro.gif" alt="nyoronyoro"><p>ようこそ ' . htmlspecialchars($status["name"]) . 'さん。<br>あなたにはflagを閲覧する権限はありません。</p>'; } else { echo '<img src="/gif/congrats.gif" alt="congrats"><p>ようこそ ' . htmlspecialchars($status["name"]) . 'さん。<br><strong>' . $_ENV["flag"] . '</strong></p>'; } ?>
json_decode('{"isAdmin": false, "name": "' . $_POST['name'] . '"}', true);
というところがミソです。
yodenと入力した際のjsonは以下のようになります。
{"isAdmin": false, "name": "yoden"}
isAdminがfalseなため閲覧権限がなく、nyoronyoro.gifが返ってきます。
この情報をヒントにするか迷ったのですが、PHPのjson_decodeではdecode時にkeyが衝突したら一番最後の値が優先されます。
つまり以下のようなjsonを作ることができたら、json_decode時にisAdminがtrueになります。
{"isAdmin": false, "name": "yoden", "isAdmin": true}
ログイン時の入力を工夫してみましょう。
まずは"
のみを入力した際はどうなるか考えてみます。
jsonは{"isAdmin": false, "name": "yoden""}
のようになり、パースはエラーになります。
これを利用して{"isAdmin": false, "name": "yoden", "isAdmin": true}
のようなjsonを作りたいところですが、
yoden", "isAdmin": true
のようにしてしまうと、実際に生成されるjsonは以下のようになり、またエラーになってしまいます。
{"isAdmin": false, "name": "yoden", "isAdmin": true"}
最後の"
が邪魔なので、もう一度"name"
を挟むことで解決します。
yoden", "isAdmin": true, "name": "y0d3n
を入力することでjsonを以下のようにするとflagが手に入ります。
{"isAdmin": false, "name": "yoden", "isAdmin": true, "name": "y0d3n"}
flag{hiyoko_manju_tottemo_oisi}
振り返り
THE WORLD、出オチ感はありますがシナリオとしてピッタリだったのでとても気に入っています。
この問題がマージされるまでが一番大変でした。
問題のレビュー時にrequested changtesでこんなことを言われてしまったので、CTFに間に合うように必死にジョジョ3部を見ました。
結構楽しかったです。
(ネタバレ回避のためにしっかり隠しています。)
見終わった報告をするとApproveされ、最終的にマージされたのは開催二日前。あぶなかった。
今年はとても参加者が少なくてへこんでたのですが、12時間ずっとやってくれてた子もいたおかげでログを見るのは楽しかったです。
Webの全完も二人出て(うち一人は一年生!)、全体的な難易度も丁度良かったかなと思います。
参加してくれた方、ありがとうございました!