Skip to main content

Fetch the Flag CTF 2022の解説:Roadrunner

著者

Giovanni Funchal

feature ctf roadrunner

2022年11月10日

0 分で読めます

Fetchに参加してくれてありがとう! 参加してくれた数千人のプレイヤーの皆さん、Fetch the Flag CTFへのご参加、おめでとうございます。そして、チャレンジを作成、テストし、解説記事を書いてくれたSnykのメンバーにも心から感謝します!

脱出ゲームが好きなら、Snykの2022 Fetch the Flag大会に登場したRoadrunnerチャレンジもきっと楽しめるはずです!この記事では、サンドボックスから脱出できるコードの入力検証の脆弱性を悪用し、どのようにチャレンジに取り組み、解いたのかを説明します。

チャレンジ

ロードランナーより速く走れる?そんなの無理だよ、ホセ!

このチャレンジは、私たちを挑発するところから始まります。フラグを見つけられるでしょうか?

まず、ウェブページにアクセスすると、オンラインのコードプレイグラウンドのようなものが見つかります。このようなオンラインプレイグラウンドは、言語ツールチェーンをインストールせずに学習や実験を行うためによく使われます。Go言語にも公式のものがあります。

Goのコードを入力して実行をクリックできます。コードはバックエンドに送信され、実行結果が返されて、ウェブページに表示されます。

このウェブページを動かすバックエンドサービスのGoソースコード(roadrunner.go)と、このサービスをパッケージ化するためのDockerfileも提供されています。

最初に、与えられたヒントを確認し、いろいろ試してみます。

解説

いろいろ試してみる

こうしたチャレンジを解くには、好奇心が大切です。そこで、まず周りを調べていくつか試してみることにしました。私はGoのプログラマーではないので、オンラインで「hello world」を検索し、ウェブページに貼り付けて実行をクリックしてみました。すると「hello world」が返ってきました。ここまでは順調です。

「hello world」と出力するGoコード、Runボタン、「hello world」と表示された出力パネルが表示されたRoadrunner Sandbox

この「hello world」の例では、fmtライブラリを使ってメッセージを出力しています。

次に、ブラウザーでウェブページのソースを調べました。実行ボタンが、フォームのsubmitアクションに紐付けられていることに気付きました。

「Roadrunner Sand…」というラベルと「実行」ボタンがあるフォームを示すHTMLコードスニペット

続いてJavaScriptのハンドラーを確認すると、予想どおりバックエンドの/runエンドポイントにPOSTリクエストを送信し、レスポンスから結果ボックスの内容を設定していました。

フォーム送信を処理し、POSTリクエストでスクリプトデータを/runエンドポイントに送信するJavaScriptコード

次に、チャレンジで提供されているバックエンドのソースコードに注目しました。まずはDockerfileから見てみましょう。

DebianとGoのバージョン引数、パッケージのインストールコマンド、flag.txtをアプリケーションディレクトリにコピーする処理を示すDockerfileのコード

フラグを見つけました!デプロイされたイメージのルートディレクトリにflag.txtファイルがあることがわかります。あとはこれを取得するだけです。このファイルの内容を読み取る方法を見つける必要があります。

手がかりを探す

roadrunner.goを見てみましょう。main関数から始めるのが定石です。

Roadrunnerサーバーが起動し、ローカルポートでリッスンしている様子を示すGoのコードスニペット

ここには/と/runの2つのエンドポイントがあります。まず、/を見てみましょう。これはwelcome関数で処理されています。

index.htmlテンプレートを解析して実行する、Goのwelcome関数を表示したコードエディター。

このコードではGoのhtml/templateライブラリを使い、index.htmlファイルを読み込んで属性を埋め込み、ウェブページのHTMLを生成しています。興味深いですね。

次に、/runエンドポイントを見てみましょう。これはrunner関数で処理されています。

リクエストをデコードし、サニタイズしたスクリプトを実行して結果を書き込むGoのHTTPハンドラーを表示した、ダークテーマのコードエディター。

ここではいくつか興味深い処理が行われています。まずサンドボックスを作成し、リクエストから入力スクリプトを取得して、サンドボックス内のファイルに書き込みます。続いてスクリプトのサニタイズ処理があり、実行しても安全かどうかを確認し、入力を拒否する場合もあります。入力が許可されると、スクリプトが実行され、結果が取得されてレスポンスとして返されます。

これを見たとき、最初に気付いたのは、dirname構造体のSandboxフィールドもリクエストのJSONからデコードされるため、ユーザーが制御できるということでした。このフィールドはファイル書き込み用のパスを作るのに使われており、脆弱性となります。ただし、どう悪用すればよいのかはわからなかったので、サニタイズ処理に注目し直しました。sanitizeScriptを詳しく見てみましょう。

ファイル操作、システムコール、ネットワーク関連のインポートをブロックするsanitizeScriptのGoコード。

このコードは入力スクリプトを解析し、インポートを確認しています。ファイル操作によく使われるGoライブラリを含むブロックリストがあり、入力スクリプトがそのいずれかをインポートすると、ちょっとおかしなメッセージとともに拒否されます。

目標は/flag.txtファイルの内容を読み取ることです。

最初に考えたのは、このリストに漏れがあるのではないか、あるいはファイルを読み取る別の方法があるのではないか、ということでした。先ほど述べたように、私はGoのプログラマーではないので、数分ほどオンラインで調べました。

ひらめき

以前、別の場所でファイルを読み込んでいたのを覚えていますか?

Goのwelcomeハンドラーがindex.htmlテンプレートを解析して実行するコードスニペット。

そう、またwelcome関数です。明らかにファイルを読み込んでいて、sanitizeScriptの禁止リストに含まれていないテンプレートライブラリを使っています。これを利用できるかもしれません。

そこで、このライブラリのドキュメントを確認しました。Templateオブジェクトを作成した後、テンプレートを実行すると、その結果がwriterに書き込まれます。

Template の Go `Execute` メソッドについて、writer、data、error の各パラメーターを示すコードドキュメント

io.Writerインターフェースを文字列に変換すれば、それを出力できます。さらにオンラインで調べたところ、strings.Builderを使えることがわかりました(stringsライブラリもブロックリストに含まれていません)。

最終的な解決策は次のとおりです。

「Roadrunner Sandbox」と題されたスクリーンショット。flag.txtのテンプレートを解析し、レンダリングされた出力を表示するGoコードが写っています。

このコードを実行すると、flag.txtファイルの内容全体が出力されます。やりました!

Roadrunnerのまとめ

今回の学びを振り返りましょう。細工した汚染入力を使って、オンラインコードプレイグラウンドの入力検証の脆弱性を悪用しました。スクリプトのサニタイズ処理に不備があったため、サンドボックスから脱出できました。アプリケーションの内部動作に関する知識が役立ち、アイデアをすぐにエクスプロイトへとつなげられました。これはCTFのチャレンジにすぎません。実際の環境でこの種のエクスプロイトを組み立てるには、推測を交えた試行錯誤が必要になることもあります。

無料の静的アプリケーションセキュリティテスト(SAST)ツールであるSnyk Codeは、コード内の入力検証の脆弱性の発見と修正に役立ちます。

多層防御などの手法を取り入れることで、このエクスプロイトをさらに困難にできます。たとえば、runScript関数を変更し、権限を制限した別のユーザーとしてスクリプトを実行できます。この場合、権限昇格などの別の脆弱性と組み合わせない限り、この脆弱性だけでは悪用できません。

信頼できないコードをサンドボックス内で安全に実行するのは、非常に困難です。サンドボックスは、アプリケーションが他のリソースやデータにアクセスするのを防ぐためのものです。可能であれば、信頼できないコードの実行自体を避けるのが最善です。

以上です。楽しんでいただけたでしょうか!ほかのフラグをどうやって見つけたか知りたい方は、Fetch the Flagの解答ページをご覧ください。

Capture the Flagを始めよう

オンデマンドのバーチャル入門ワークショップを見て、Capture the Flagの課題の解き方を学びましょう。

続きを読む

Blog

フロンティアモデルは脆弱性を発見した。攻撃者だけがエクスプロイトチェーンを見つけた。

静的解析で欠陥は見つかりましたが、ライブ攻撃テストで侵害につながる連鎖を実証できたのは唯一でした。Evo COS、Claude Security、Claude Code Securityを比較します。

feature insights context
Blog

自律型攻撃はすでに始まっている。防御もそのスピードに追いつかなければならない。

自律型攻撃者によって、防御に使える時間は短くなっています。継続的な検出、修復、検証、予防で、セキュリティチームが攻撃に歩調を合わせる方法をご紹介します。

Blog

AIコーディングエージェントが不適切なアクセス制御を繰り返し実装する理由

AIコーディングエージェントは、コンパイルが通りレビューも通過する一方で、あるテナントのデータを別のテナントに公開してしまう認可ロジックを生成することがあります。不適切なアクセス制御が検出しにくい理由と、その防止策をご紹介します。