はじめに
下記のTweetで出題させていただいた、Flatt Security Developers' Quiz #2にご参加いただきありがとうございました!
⚡️ Flatt Security Developers' Quiz #2 開催! ⚡️
— 株式会社Flatt Security (@flatt_security) 2022年7月20日
JavaScriptコードに潜む脆弱性を見つけられますか?
抽選10名の方に公式Tシャツとステッカーをプレゼント!皆さんの回答お待ちしております!
デモ環境: https://t.co/jZhU7Na3q6
回答提出フォーム: https://t.co/bbZUPsZIUI pic.twitter.com/G1REW4nZep
景品の獲得条件を満たした方には追ってメールでご連絡を差し上げますので、ご確認いただけますと幸いです。なお、景品獲得条件を満たさなかった方にはご連絡いたしません。ご了承ください。
今回のクイズでFlatt Securityに興味を持ってくださった方は是非下記のバナーよりサービス詳細をご覧ください。
今回のクイズの確認
ソースコード
const express = require("express"); const { execSync } = require("child_process"); const app = express(); /* local ping check */ app.get("/", function(req, res) { const ip_address = req.query.ip; if(!ip_address){ res.send("?ip=[your_ip]"); return; } if(ip_address.length > 16){ res.send("Error! IP is too long."); return; } if(/[!@#$%\^&*()\-_+=\[\] {}'";:,:?~\\]/.exec(ip_address)){ res.send("Error! Your request is filtered!"); return; } const cmd = "sh -c 'ping -c 1 " + ip_address + "' 2>&1 >/dev/null; true"; const stderr = execSync(cmd, {"timeout": 1000}); if(stderr != ""){ res.send("Error! " + stderr); return; } res.send("Your IP is in a good state!"); }); app.listen(1337);
デモ環境
https://2207okapi.twitter-quiz.flatt.training/
出題形式
JavaScriptコードの脆弱性を突き ls
コマンドを実行することで隠されたファイルを発見し、ファイル名を回答する。
難易度
開発者にとっても「かんたん」という想定で設定しました。セキュリティエンジニアであれば一瞬で解けてしまうかもしれません。
解答例
https://2207okapi.twitter-quiz.flatt.training/?ip=%60ls%60
のようにパラメータを付与したURLにアクセスすると、エラーメッセージ中に ls
コマンドの実行結果が含まれ、隠されたファイル名を奪取することができます。
Error! ping: wow_congrats_you_executed_a_system_command.txt: Name or service not known
よって、wow_congrats_you_executed_a_system_command.txt
が正答となります。
解説
今回のQuizは非常にシンプルで、バッククォート `
がフィルタされていないことに気づけるかがポイントです。
まずは正規表現による記号のフィルタの実装を注視してください。バッククォートがブロックされていませんね。
if(/[!@#$%\^&*()\-_+=\[\] {}'";:,:?~\\]/.exec(ip_address)){ res.send("Error! Your request is filtered!"); return; }
そうすると、以下の行で組み立てられているシェルコマンドに `ls`
を渡すことで、我々が求めている ls
コマンドの実行結果を紛れ込ませることができそうです。
const cmd = "sh -c 'ping -c 1 " + ip_address + "' 2>&1 >/dev/null; true";
ip_address
部分が ls
コマンドの実行結果の文字列になれば当然pingコマンドはエラーを起こしますので、 stderr
経由で隠されたデータを獲得することが可能です。
ですので、解答方法の例としては
https://2207okapi.twitter-quiz.flatt.training/?ip=%60ls%60
もしくは
https://2207okapi.twitter-quiz.flatt.training/?ip=`ls`
にアクセスすることとなるでしょう。
このような脆弱性が起こるパターンへの対策方法
Quiz #1 でも同様の脆弱性が存在しましたが、「拒否リストを用いて特定の文字列の入力を阻害する」という手法は開発者の想定外なバイパスをされる可能性が残ってしまいます。
もしユーザーの入力を想定する箇所において「特定の文字列だけ受け付けたい」というような場合は「許可リストを作成し、入力された文字列がそのリストに一致する文字列かどうか比較する」といった方法で対策することが可能です。
特に今回のようにOSコマンドを用いる場合は、以下のような対応を考えてみるのもよさそうです。
- そもそもOSコマンドを使わないような実装に置き換えられないか検討する
- OSコマンドを利用するなら、シェルを起動しない関数を利用する
- Node.js なら
exec
ではなくexecFile
を利用する
- Node.js なら
終わりに
Quizは2回目ですが、前回に引き続きたくさんの方にご参加いただけて大変嬉しかったです。
なお、Quiz #1 に関してはこちらの記事をご覧ください。
また、我々株式会社Flatt Securityはセキュリティ診断サービスを提供しています。
セキュリティエンジニアによる手動診断によって高い精度で脆弱性を洗い出すことが可能です。実際、今回題材にしたような脆弱性はツールのスキャンのみでは発見は難しいことが多く、セキュリティエンジニアによる手動検査が効果的です。
ツールによる診断しか過去実施しておらず認証や決済といった重要な機能のセキュリティに不安があったり、既存のベンダーとは違う会社に依頼したいと考えていたりする方はお気軽にご相談ください。
数百万円からスタートの大掛かりなものばかりを想像されるかもしれませんが、上記のデータが示すように、診断は幅広いご予算帯に応じて実施が可能です。ご興味のある方向けに下記バナーより料金に関する資料もダウンロード可能です。
また、Flatt Securityはセキュリティに関する様々な発信を行っています。 最新情報を見逃さないよう、公式Twitterのフォローをぜひお願いします!
ここまでお読みいただきありがとうございました。