テクノロジー
SECCON Beginners CTF 2022のWriteup (前編)
2022年6月4日〜5日に開催されたSECCON Beginners CTF 2022にチームマイナビで参加しました。
本記事はそのWriteupとなります。
概要
CTF(Capture the Flag)とは、情報セキュリティの知識を使うセキュリティコンテストの一つです。
いくつかのジャンルに分かれており、例えば
- リバースエンジニアリングによって実行ファイルの脆弱性をつく
- Webアプリの脆弱性をつく
- ファイル内に隠された情報を抜き取る
などの、そういった攻撃をして隠された『Flag』を手に入れることを目的としています。
今回、国内のCTFの中で最大となるSECCONが開催しているビギナー向けのコンテストに参加しました。
※ちなみに昨年も参加しています。
結果
結果は891チーム中63位でした!
- 結果
- 解いた問題のカテゴリ割合
メンバー
メンバー | 紹介(筆者の偏見が混じっています) | |
---|---|---|
S.H.さん | チームmnsecのポイントゲッター | |
S.R.さん | おいおい知らねえぜ、俺が全完しててもよォ | |
S.T.さん | 期待のにゅーふぇいす | |
M.W.(私) | 特に言うことなし |
流れ
Writeup
web
textex
課題ページ( https://textex.quals.beginners.seccon.jp/ )に移動し、適当にTeX文書を入力すると、コンパイルされてPDFが返ってくるというアプリケーションが実装されている。
サーバのソースコードを見てみると、flag というファイルがあるので、その中をTeX経由で覗くと正解が得られると考えられる。
ファイルの中身をそのまま展開することのできる\verbatiminputコマンドでやろうとする。
\documentclass{article}
\usepackage{verbatim}
\begin{document}
\verbatiminput{../../flag}
\end{document}
しかし、TeXのソースコードに flagという文字列が含まれているとエラーになるように app.pyが実装されている。
# No flag !!!!
if "flag" in tex_code.lower():
tex_code = ""
そこで、 flagというファイル名を、マクロで分解して、TeXで展開した時に flagに見えるようにするとフラグの中身を覗くことができる。
\documentclass{article}
\usepackage{verbatim}
\newcommand{\fl}{fl}
\newcommand{\ag}{ag}
\begin{document}
\verbatiminput{../../\fl\ag}
\end{document}
gallery
絵文字のギャラリーサービスからフラグを取得する問題です。
https://gallery.quals.beginners.seccon.jp/?file_extension=fla
にアクセスすると、flag_7a96139e-71a2-4381-bf31-adf37df94c04.pdf
というファイルが存在していることが分かります。
こちらはブラウザからそのままダウンロードはできません。
サーバーサイドアプリケーションで10,240バイト以上のレスポンスは、そのまま返さないように制限がかけれているからです。
Rangeヘッダを利用して、10,000バイトづつに分割してダウンロードし、それらを結合できればよさそうです。
参考: https://developer.mozilla.org/ja/docs/Web/HTTP/Headers/Range
$ curl 'https://gallery.quals.beginners.seccon.jp/images/flag_7a96139e-71a2-4381-bf31-adf37df94c04.pdf' -H 'Range: bytes=0-9999' --output 0.bin
$ curl 'https://gallery.quals.beginners.seccon.jp/images/flag_7a96139e-71a2-4381-bf31-adf37df94c04.pdf' -H 'Range: bytes=10000-19999' --output 1.bin
$ cat 0.bin 1.bin > test.pdf
無事、フラグを取得できました。
Ironhand
お題としてWebサイトが与えられます。ユーザー名を指定してログインすると、user(一般ユーザー)としてログインします。テキストを読むに、adminとしてログインするとflagをもらえそうです。
Cookieを確認すると、JWT(JSON Web Token)が与えられていました。デコードすると、IsAdmin
というパラメータを持っていることが分かります。現在false
になっていますが、これをtrue
にすれば万事解決……しません。なぜなら、JWTはヘッダー部、ペイロード部、およびヘッダー部とペイロード部を結合した文字列のデジタル署名からなり、IsAdmin
だけを変更すると、署名検証が失敗するからです。署名は改ざん検知に有用だということがよく分かります。
adminになるためには適切な署名を生成する必要があり、そのためには署名に使われたシークレットを入手しなければなりません。提供されているアプリのコードを読むと、署名に使われたシークレットであるJWT_SECRET_KEY
は、アプリケーションの環境変数として宣言されていることが分かります。
コードを追っていくと、/static/:path
にきな臭さを感じます。具体的には、静的ファイルの配信ならNginxでやればいいのに、わざわざアプリケーションでやっているあたりが臭いです。
更に、Nginxの設定ファイルにもmerge_slashes off
という馴染みのないオプションがあります。URLにスラッシュをたくさん投げてくれと言わんばかりです。
検証の結果、案の定/static/:path
にパストラバーサル脆弱性があることが分かりました。画像は脆弱性を利用して、本来表示できないはずの/etc/passwd
を表示しているところです。
パストラバーサルを使ってどうやって環境変数を窃視するかというと、/proc/self/environ
を呼び出すことで、アプリケーションの環境変数を含むファイルを取得できます。
JWT_SECRET_KEY
が含まれていることが分かります。
あとは、IsAdmin
をtrue
を変更して、JWT_SECRET_KEY
を使った正しい署名を含むJWTを作ったら、ブラウザのCookieを書き換えて更新してやればflagです。
なお、この問題には刺さりませんが、alg: none
にして署名検証そのものをバイパスするという手法・脆弱性もあるみたいです。その発想はなかった。いずれにせよ、パストラバーサル怖~~というのが教訓です。
後編へ続く・・
※サムネ画像ロゴは『SECCON Beginners CTF』より引用