Scroll to top

事例研究:ひとつのSSRFが、クラウドアカウント全体を明け渡すまで

私はJoe、東京を拠点とするサイバーセキュリティの専門家です。これは実際の案件の記録で、クライアント名と製品の詳細は伏せ、技術的な構造はそのまま残しています。ここで取り上げるのは、これが現代のクラウドアプリケーションで今なお最も多く見かける深刻な指摘であり、そしてこれを見せたほとんどのチームが最初に同じことを言うからです。「でも、そのエンドポイントは内部向けです」。まさにそこが問題なのです。リクエストを行っているのが自社のサーバー自身であるとき、「内部向け」はセキュリティ対策になりません。

  1. 入口を開けたのは、ごく普通の機能だった

    そのアプリケーションには、URLを貼り付けてプロフィール画像を取り込む機能がありました。サーバーがそのURLを取得し、結果を保存する。それだけです。ファイルアップロードもパーサーもなく、アーキテクチャ図の上では危険には見えません。しかし「ユーザーが指定したURLをサーバーが取得する」というのは、まさにサーバーサイドリクエストフォージェリ(SSRF)の定義そのものです。しかもそのサーバーは、ブラウザからは決して到達できない多数の隣接システムが存在するクラウドVPCの内側にあります。この種の機能で私が最初に試すのはインターネット側ではなく、「ユーザーには見えないが、サーバーには見えるもの」です。

  2. 許可リストが持ちこたえられなかった理由

    開発チームもこの点は考慮していました。127.0.0.1、localhost、10.0.0.0/8 に対するブロックリストが存在したのです。しかし、それはよくある理由で破られました。私は一つずつ実演しました。内部アドレスに解決されるホスト名は文字列チェックを通過してしまう。公開URLが内部URLへの302を返せば、再検証されないままリダイレクトが追跡される。IPv6の各表記(ループバック、ULA、リンクローカル、IPv4マップド、NAT64)はそもそもリストに含まれていない。そして userinfo 接頭辞(http://trusted.example.com@169.254.169.254/)は、手作業でホストを切り出すあらゆるチェックを欺きます。文字列を検証することは、宛先を検証することではありません。

  3. メタデータエンドポイントから、本物の認証情報へ

    効いたペイロードは、実に平凡なものでした。169.254.169.254 にあるクラウドのインスタンスメタデータサービスです。このホストではまだバージョン1のリクエストに応答する設定になっており、つまりトークンなしの単純なGETで、インスタンスにアタッチされたIAMロール名、続いてその一時アクセスキー・シークレット・セッショントークンが返ってきます。シェルもコールバックも、マルウェアの1バイトも必要ありませんでした。必要だったのは、アプリケーションが私に代わって行うよう設計されていたHTTPリクエスト1回だけで、その応答はアプリ自身のエラーメッセージの中に表示されて返ってきました。

  4. 報告する前に、被害範囲を測る

    発見事項の価値は、証明できる影響の大きさで決まります。しかし影響を証明することが、影響を発生させることであってはなりません。クライアントの書面による承認のもと、取得した認証情報は読み取り専用の呼び出しにのみ使用しました。自分は誰か、どのポリシーが付与されているか、このロールがどのバケットを一覧できるか。結果として、そのロールは機能に必要な範囲をはるかに超える権限を持っており、顧客文書を保管するオブジェクトストアを読み取れることが分かりました。ID確認の呼び出し、ポリシー一覧、そしてディレクトリ一覧を1回だけ証跡として記録し、そこで停止しました。データのダウンロードも変更も一切行わず、一連の操作にはタイムスタンプを付し、ブルーチームが自分たちのログと突き合わせられるようにしました。

  5. 実際に問題を塞いだ対策

    導入順に、四つの変更を行いました。まず IMDSv2 を強制し、SSRFでは生成できないセッショントークンをメタデータサービスに要求させ、ホップ制限を1に設定します。次に、インスタンスロールの権限をその機能が必要とする2つのアクションだけに絞り込みます。さらに、取得処理が公開インターネットにのみ到達でき、リンクローカルやプライベートレンジには決して到達できないようエグレスポリシーを追加します。そして最後に、パース時ではなく名前解決時に検証すること。ホスト名を解決し、返ってきたすべてのアドレスをIPv4・IPv6双方のプライベート/特殊用途レンジの拒否リストと照合したうえで、その解決済みアドレスに接続します。こうすればチェックとリクエストの間にDNSが答えを変えることはできません。

  6. なぜ、これをライブラリにしたのか

    案件のたびに同じURL検証コードを書き直し、そのたびに開発チームが微妙に実装を誤るのを見てきました。たいていはIPv6かリダイレクトの扱いです。そこで、それをパッケージにしました。DSSRFです。クライアントがリクエストを送る前にURLを検証・サニタイズする、MITライセンスの小さなJavaScriptライブラリです。現在は OWASP Foundation の「Free for Open Source Application Security Tools」に防御ツールとして掲載されています。また、このライブラリ自体にも公開されたCVEがあります。私が自分のライブラリを攻撃し続け、見つけたものを修正しているからです。それこそが要点で、誰にも攻撃されない防御ツールは、心地よい思い込みにすぎません。

  7. 明日、あなたの環境で確認すべきこと

    バックエンドがユーザー指定のURLを取得している箇所をすべて洗い出してください。アバターの取り込み、Webhook、PDFやスクリーンショットのレンダラー、リンクプレビュー、RSSの取り込み、SSOメタデータ、ヘルスチェックなどです。それぞれについて三つの問いを立てます。169.254.169.254 やプライベートレンジに到達できるか。再検証なしにリダイレクトを追跡していないか。その処理が使うIDは、機能に必要な範囲を超えた権限を持っていないか。ひとつでも「はい」があれば、同じ事例研究が起こるのを待っている状態です。そしてそれは、長く理論上の話にとどまってはくれません。

Cybersecurity case study

コメント(5件)

  1. cloud_arch_maya 2日前 返信
    同じような話を読んで先四半期にIMDSv2を強制しましたが、作業は半日で終わりました。ホップ制限の話は、多くの人が見落とす部分です。
    1. appsec_dan 2日前 返信
      同感です。難しかったのは修正そのものではなく、取得処理をすべて見つけることでした。誰も覚えていなかったのがリンクプレビューのサービスでした。
  2. backend_ren 2日前 返信
    先月のコードレビューで、userinfo接頭辞のトリックにやられました。誰かがあのURLを見せるまで、私たちの正規表現は完全に妥当に見えていたのです。
  3. sec_lead_kenji 2日前 返信
    ディレクトリ一覧の取得で止めている点に感謝します。触れるべきでないものを持ち出して「影響を証明」する報告が多すぎます。
    1. joecybertech 2日前 返信
      常にそうしています。まず書面でのスコープ合意、読み取り専用の呼び出し、そして防御側へのタイムスタンプの共有。証明がクライアントに何かを失わせることがあってはなりません。

コメントを残す

メールアドレスをご確認ください
メッセージをご確認ください
ありがとうございます。メッセージは送信されました。
エラーが発生し、メールを送信できませんでした