Fetch the Flag CTF 2022 解説:Not So Smart Fridge
Antonio Gomes
2022年11月10日
0 分で読めますFetchに参加してくれてありがとう! 参加してくれた何千人ものプレイヤーの皆さん、 Fetch the Flag CTFにご参加いただき、ありがとうございました。また、チャレンジの作成、テスト、解説記事の執筆を担当してくれたSnykerの皆さんにも心から感謝します!
このFetch the Flag CTFのチャレンジは、新品のSmart Fridge Ultra SFU-3000について必要な情報をすべて教えてくれる、心温まる歓迎から始まります。ワクワクしますね!
アイザック・アシモフはかつて、「完全調理済みの食事も半調理済みの食事も冷蔵庫に保存しておけば、いつでも食べられる。キッチン設備が『自動で食事を作り』、水を沸かし、コーヒーを淹れてくれるようになる」と予言しました。そして今、私たちはこの冷蔵庫で商品を注文したり、内蔵オーディオで音楽を再生したりしています。すばらしいですね!

ところが、Music機能が使えないとわかるまでは、ワクワクしていました。さらに、冷蔵庫のファームウェアを更新するSettings機能も壊れていて、pistache/0.0.3.20220107から最新バージョンにアップグレードする方法がありません。少なくともファームウェアのバージョンはわかりました。ここでの目標はフラグを見つけて、このチャレンジを解くことなので、これは役に立ちます!

解説
Smart Fridge Ultra SFU-3000 —のウェブページにアクセスし、実際にできることの少なさにがっかりしながら、Settingsを開くと、ファームウェアがpistache/0.0.3.20220107だとわかりました。pistacheについて詳しくない方のために説明すると、C++向けの最新のHTTPおよびRESTフレームワークです。チャレンジのウェブページで使われているソフトウェアの手がかりになるのはありがたいですね。
この情報を使えば、Snyk Intel Vulnerability Databaseでpistache/0.0.3.20220107に関連するセキュリティ上の問題を簡単に検索できます。pistacheを検索すると、Pistacheにパストラバーサルの脆弱性があり、冷蔵庫のファームウェアのバージョン0.0.3.20220107も影響を受けることがわかります。これは好都合です。以下のSnykのPoCで提案されているコマンドを使って、悪用を試してみましょう。

curl --path-as-isを実行すると、curlはフルパス内の/../の並びを正規化しません。また、以下の結果を取得できます。これにより、パストラバーサルの脆弱性を利用して、このシステムにアクセスできるユーザー一覧を取得できたこと、そしてdocというフォルダがあることがわかります。
docフォルダのパスにアクセスすると、Swagger APIドキュメントを取得でき、ウェブアプリケーションで利用可能なエンドポイント(例:v1/system/flag/{flag})を確認できます。この時点で、C++バイナリとしてpistacheを使用するウェブアプリケーションであることがわかっています。

次はどうするか?Linuxのprocを利用します。具体的には/proc/self/exeです。これは文字どおり現在実行中のプロセスを指し、この場合はpistacheのウェブアプリのバイナリです。次のコマンドを実行します。
これでpistacheのバイナリをダウンロードできます。フラグを特定するため、このバイナリをリバースエンジニアリングする必要があります。まず、ソフトウェアのリバースエンジニアリング用ツールをダウンロードします。ここではGhidraを使います(MacOSユーザーはbrewからも利用できます)。
Ghidraで新しいプロジェクトを作成し、先ほどダウンロードした.exeバイナリをインポートします。

いよいよチャレンジの楽しい部分です。フラグがどのようなものか、調べていきましょう!Ghidraで「flag」を検索すると、checkFlagというメソッドが見つかります。内容は、以下の画像のDecompile: checkFlag - (exe)ウィンドウで確認できます。

逆コンパイルしたウィンドウを見ると、フラグの想定される長さは70文字だとわかります。
if lVar4 == 0x46。元のコードで定義された制約によると、lVar4には入力されたフラグの長さが格納され、0x46は10進数で70に相当します。

上記の条件length == 70を満たすと、checkIndexesという別のメソッドが呼び出されます。

画像の上と下に示されているように、checkIndexesはプログラムからフラグ文字列、複数のインデックスを含むベクター、そして文字(上の画像では'7')を受け取ります。

これはどういう意味でしょうか?checkIndexesを呼び出すたびに、n個のインデックス位置にある文字が検証され、フラグ文字列が有効かどうかが判定されます。すべてのcheckIndexes呼び出しが成功すれば、フラグを特定できたということです。フラグを見つけるには、逆アセンブルしたファイルを調べ、各local_*変数を16進数から10進数に変換して位置を特定する方法があります。

{13, 22, 29, 39,40, 45, 59}は、フラグ内で文字`5`があるインデックスの一覧です。これでフラグを組み立てます。
ベクターごとに割り当てられたすべてのインデックスを特定する別の方法は、スクリプトを使うことです(コードを確認)。これを行うには、プログラムをC/C++ファイルとしてエクスポートし、ほかの部分を削除して、checkFlagメソッドだけを対象にします。さらに、local_*変数を16進数から10進数に変換して各文字のインデックスを特定し、フラグを再構築するスクリプトを用意します。

冷蔵庫より賢く
このチャレンジでは、Snykを使ってC++ウェブアプリケーションのパストラバーサル脆弱性を特定し、実行中のプロセスのバイナリにアクセスしてダウンロードし、Ghidraでリバースエンジニアリングを行い、逆コンパイルしたバイナリを分析してフラグを見つける方法を学びました。楽しかったですね!
以上です。チャレンジを楽しんでいただけたなら幸いです。Snyk CTF 2022にご参加いただき、ありがとうございました!ほかのフラグの見つけ方も知りたいですか?Fetch the Flagの解答ページで、解き方をご覧ください。



