Red Agentの視点:いかにして推論を重ねてSSRFへと至ったか

パート1:Red AgentがGCP Cloud Run APIにおいてSSRFからローカルファイル読み取りを可能にする複数ステップの攻撃チェーンをどのように解明したか

+2
Red Agent, Gal Nagli, Danielle Aminov および 2 もっと

当社の「 Red Agent POVシリーズ 」の最初のディープダイブブログでは、Red Agentによって発見されたサーバーサイドリクエストフォージェリ(SSRF)のエクスプロイトに焦点を当てます。これは、現代のクラウドアプリケーションにおいて最も一般的で影響の大きいバグクラスの1つです。このエクスプロイトでは、Red AgentはGCP Cloud Run API上でSSRFからローカルファイル読み取りを可能にする重大なマルチステップ攻撃チェーンを発見しました。

まず、SSRFとは何でしょうか?

サーバーサイドリクエストフォージェリ(SSRF)は、アプリケーションが適切な検証を行わずにユーザー提供の入力に基づいて外部ネットワークリクエストを実行したときに発生します。リモートリソースの取得はアプリケーションの意図された動作であることが多いですが、サーバーが特権ネットワーク内にある場合は危険になります。攻撃者はサーバーの信頼されたネットワーク上の立場を悪用して、内部専用サービス(データベースや管理パネルなど)と強制的にやり取りさせたり、クラウドメタデータエンドポイントをクエリして資格情報を抽出したり、次のような代替URIスキームを使用してローカルファイルを読み取ったりすることさえできます: file:// 、これによって、攻撃者はサーバーを組織の最も重要な情報資産(クラウンジュエル)へのプロキシに変えてしまいます。

Red Agentは何を発見したのでしょうか?

Red Agentは、本番環境のGCP Cloud Runサービスが ?url= パラメーターを受け取り、ユーザーに代わってファイルを取得していたSSRF脆弱性を発見しました。開発者はURLフェッチャーが危険であることを認識していたため制限を設けており、このパラメーターが 唯一 受け入れ対象としていたのは構造的に有効なGitHub blob URLのみでした。以下の形式に該当しないものはすべて https://github.com/{owner}/{repo}/blob/{branch}/{path} 拒否されました。

Red Agentはこの「GitHub限定」フェッチャーを、ホストのローカルファイルシステムへの未認証読み取りへと変化させ、 /proc/self/environ から稼働中のGCPサービスアカウント資格情報を抽出し、アプリケーションの完全なソースコードをダンプしました。資格情報は本物であり、稼働中のGCP APIに対して有効でした。

どのようにしてエクスプロイトを発見したのか? AIアタッカーの手法

Red Agentは実行(Run)と反復(Iteration)を繰り返してスキャンを行い、パス走査、URL混同、インジェクション手法のテストなど、さまざまな攻撃戦略を並列で実行します。反復の合間に、Red Agentは何が成功し、何がブロックされ、アプリケーションの動作がそのロジックについて何を明らかにしているかを考察します。各実行結果は前回の結果に基づいて構築されるため、Red Agentは仮説を研ぎ澄まし、ペイロードを進化させることができます。

ターゲット

Red AgentのターゲットはGCP Cloud Runサービスでした: <redacted>.run.app/f 。当初、エンドポイントが次のパラメーターを受け入れることを特定しました: ?url= パラメータであり、次にアプリケーションがそのパラメータを使って何を行ったかを推理する必要がありました。

フェーズ 1: 偵察とベースライン調査

Red Agentはターゲットエンドポイント /f と、それが次を受け入れるGCP Cloud Runサービスであることを示すメタデータを受け取りました: ?url= パラメータ。Cloud Runコンテナ上のURL取得パラメータは、アプリケーションが呼び出し元に代わって外部リクエストを行うように設計されているため、典型的なSSRFの候補となります。また、Cloud RunコンテナはGCPメタデータサーバーにアクセスしてサービスアカウントトークンやプロジェクト認証情報を取得できます。そのため、最初の動きは、その取得リクエストを次のGCPメタデータサーバーにリダイレクトできるかどうかをテストすることでした: http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/token — 動作すれば認証情報窃取への最も手軽な経路です。しかし、動作しませんでした — APIはURLがGitHubを指していることを検証し、メタデータへの直接のピボットをブロックしました。これでRed Agentは制約を認識し、次に試すどのような手法もGitHub宛てのURL検証器を満たす必要があることを理解しました。

フェーズ 2: 古典的なパス・トラバーサル

?url= パラメータの役割はファイル内容を取得することであるため、Red Agentはバックエンドが最終的にURLをローカルファイルシステムの読み取りへと解決し、GitHubファイルをディスク(またはコンテナ内の /tmp パス)にダウンロードしてから返すと仮定しました。これが事実であれば、URLにトラバーサルシーケンスを挿入することで、意図したディレクトリをエスケープして任意のファイルを読み取れる可能性があります。Red Agentは ?url= パラメータに、それぞれ異なるクラスのサニタイズフィルターをバイパスするように設計されたトラバーサルバリエーションを送信しました:

  • 標準: ../../../../etc/passwd

  • URLエンコード: %2f..%2f..%2ffetc%2fpasswd 

  • 二重エンコード %252f..%252f

  • ヌルバイト: ..%00/etc/passwd

  • バックスラッシュ: ..%5c..%5c

  • Unicodeオーバーロング: ..%c0%af..%c0%af

  • 非再帰的バイパス: ....//....//

コンテナ固有のパスをターゲットにしました: /app/main.py (アプリケーションのエントリポイント)、 /proc/self/environ (実行時の認証情報と環境変数)、および /app/requirements.txt (依存関係マニフェスト)。これらはCloud Runコンテナ内で価値の高いデータが存在する場所だからです。すべての試みはブロックされました。URLバリデータはドメインをチェックするだけでなく、整形式のGitHub blob URLに見えないものをすべて拒否していました。

フェーズ 3: URL混同とSSRF

バリデータはパスコンポーネントをチェックするだけでなく、完全なURL構造を解析していたため、パストラバーサルは失敗に終わりました。そのため、Red Agentはアプローチを変更しました。ディスク上のファイルの読み込み元を操作する代わりに、URLパーサーを騙してまったく別のホストから取得させようと試みることにしました。 ?url= パラメータはバックエンドのHTTPクライアントに渡され、ホスト名を解決してアウトバウンドリクエストを行います。バリデータとフェッチャーがURLを異なる方法で解析した場合(典型的なパーサーディファレンシャル)、バリデータには「github.com」に見える一方で、フェッチャーが実際にはGCPメタデータサーバー(`metadata.google.internal`)に接続する可能性があります。いくつかの混同テクニックをテストしました: ?url= パラメータ:

TechniquePayloadExploits
Authority confusiongithub.com@metadata.google.internal/computeMetadata/v1/Parsers that treat text before @ as userinfo, connecting to the host after it
Fragment injectiongithub.com#@metadata.google.internalParsers that strip fragments inconsistently
Subdomain spoofinggithub.com.metadata.google.internalLax domain matching (contains "github.com")
Scheme switchinggcs://graph-explorer/repo/dir.pyHandlers that follow non-HTTP schemes to internal services

しかし、これらはどれも機能しませんでした。バリデータはスキーム(https://)と解決されたホスト名の両方に対して厳格でした。これにより、Red Agentは重要なことに気づきました。サーバーは、URLが解決する任意のホストに対して実際の外部HTTPリクエストを行っているということです。取得ロジックは擬似的なものやプロキシされたものではなく、本物のHTTPクライアントです。Red Agentは、取得先を役立つ場所に向けつつ、バリデータを満たす方法が必要であると結論付けました。

フェーズ 4: リポジトリ名インジェクション

Red Agentはリポジトリ名インジェクションもテストしました。バックエンドが解析されたリポジトリ名からローカルパスを構築している可能性があると仮定し、リポジトリコンポーネントに直接トラバーサル文字列を注入しました: - github.com/owner/..%2f..%2fapp/blob/main/main.py - github.com/owner/..%2f..%2fetc/blob/main/passwd.

フェーズ 5: 実際のリポジトリの列挙

攻撃対象のアプリケーションについてより深く理解するため、Red Agentは実際のGCP関連リポジトリ(例: GoogleCloudPlatform/graph-explorer, googleapis/graph-explorer, google/graph-explorer) )をテストすることで、環境のフィンガープリント採取を開始しました。これにより、次のような関連する内部命名規則を推測するのに役立ちました: graph-explorer-file-reader

フェーズ 6: 突破口 - ダブルスラッシュ絶対パス

この時点で、Red Agentは80回以上の失敗した試行からアプリケーションの詳細なメンタルモデルを構築していました: 

  • ?url= パラメーターには、構造的に有効な GitHub blob URL(スキーム、ドメイン、オーナー、リポジトリ、 /blob/ 、ブランチ、パス)が含まれている必要があります

  • それ以外のものは、フェッチが行われる前に拒否されます(フェーズ 1〜3 で判明) 

  • バックエンドは解決された URL に対して実際の HTTP リクエストを行います 

  • 単にローカルでパスを解析しているだけではありません(フェーズ 3 で判明) 

  • このアプリケーションは graph-explorer に関連しており、ソースコードの場所が /app/ (フェーズ4~5で学習)

したがって、Red Agentは「GitHubの検証を通過しつつ、ファイル読み取りロジックにローカルパスを解決させるURLを構築できるか?」を解明する必要がありました。ここでエクスプロイトの2つの層が結びつきます: 

  • レイヤー1 - バリデータを満たす: URLは正規のGitHub blob参照のように見える必要があります: https://github.com/{owner}/{repo}/blob/{branch}/{path} 。この構造から外れたリクエストはすべて破棄されます。

  • レイヤー2 - バイパス手法: Red Agentは、 // の後に絶対ファイルシステムパスを有効なGitHub URL構造の後ろに追加すると、バリデータがプレフィックスのみをチェックする一方で、ファイル取得ロジックが末尾の部分をローカル絶対パスとして解釈することを発見しました。 // 自体はペイロードそのものではなく、実際のペイロード(/proc/self/environ, /app/graph_api.py)をすり抜けさせるバイパス機構です。バリデータには有効なGitHub URLに見え、取得プログラムには絶対パスに見えます。

成功したペイロード:

  • ?url=https://github.com/x/y/blob/z//proc/self/environ (4,096バイトを返却)

  • ?url=https://github.com/x/y/blob/z//var/task/graph_api.py (25,000バイトを返却)

これがスキャン適応の姿です。単に多くのペイロードを試すのではなく、Red Agentは多数の失敗した試行を通じて発見された制約をまとめ上げ、バリデータを満たしつつ取得プログラムを同時に攻撃する手法を構築しました。

決定パス

確認された調査結果

Once the Red Agent established the file read primitive, it didn't stop at exfiltration- it validated the impact end-to-end. Here's what it extracted and confirmed:

FileSizeImpact
/proc/self/environ4,096 bytesContainer environment variables containing GCP service account credentials and API keys
/var/task/graph_api.py25,000 bytesFull Source Code: Exposure of the entire application logic.
/var/task/graph.zip200 bytesApp Bundle: Metadata regarding the application structure.

Why this matters

A traditional DAST scanner would have tested this endpoint in a handful of requests, trying the metadata server and path traversal, eventually getting blocked and moving on. 

The final payload, github.com/x/y/blob/z//proc/self/environ, doesn't exist in any wordlist or signature database, and at the same time it can't be found by brute force. This exploit the Red Agent uncovered required understanding and reasoning.

It requires recognizing that the validator enforces GitHub URL structure, that the fetcher resolves paths differently than the validator parses them, and that // acts as an absolute path anchor that slips through the gap between the two. This understanding was built incrementally across 96 requests of probing, failing, learning, and adapting. 

Each time the Red Agent faced a blocked attempt, it was a constraint that narrowed the solution space for its exploration. The blocked path traversal revealed strict URL parsing, blocked URL confusion confirmed the fetcher makes real HTTP calls, and the blocked repo name injection confirmed local path construction exists. The breakthrough payload sits at the intersection of all these observations. 

This is what non-deterministic, reasoning-driven security testing unlocks: the ability to discover vulnerabilities that only emerge from chaining together application-specific behaviors, the same class of findings that until now required a skilled human attacker spending hours of manual testing. 

The impact of such exploitable risk, in this example, is unauthenticated access to GCP credentials and full source code - a complete compromise chain from a single URL parameter.

Read the next Red Agent POV Blog

You can read the next blog in the series here focusing on a Broken Object-Level Authorization (BOLA) exploit. We will be sharing more examples of the risks Red Agent uncovers, you can see all the blogs in the series here. . If you would like to see what types of risks it can find in your environment, learn more about the Red Agent (login required) or schedule a live demo with our team.

続きを読む

パーソナライズされたデモを見る

実際に Wiz を見てみませんか?​

"私が今まで見た中で最高のユーザーエクスペリエンスは、クラウドワークロードを完全に可視化します。"
デビッド・エストリックCISO (最高情報責任者)
"Wiz を使えば、クラウド環境で何が起こっているかを 1 つの画面で確認することができます"
アダム・フレッチャーチーフ・セキュリティ・オフィサー
"Wizが何かを重要視した場合、それは実際に重要であることを私たちは知っています。"
グレッグ・ポニャトフスキ脅威および脆弱性管理責任者