iwantcoding.com
🔥 Daily 👥 Rooms 🏆 Top Log in Sign up

SSRF Testing

Server-Side Request Forgery happens when a server fetches a URL on behalf of a user and the user controls that URL. The blast radius is large: reading cloud metadata (AWS 169.254.169.254), pivoting to internal services, exfiltrating data. Testing for it in an authorised engagement requires care — a single curl from a vulnerable endpoint can expose IAM credentials.

Authorised SSRF testing — RoE-first, low-impact PoC

EXAMPLE
# 1) Rules of engagement (excerpt — agreed in writing before testing)
# - Targets:      https://target.lab.example.test  (staging mirror, no prod data)
# - Off-limits:   any prod host, real cloud metadata endpoints from prod accounts
# - Payload class: SSRF probes only; data exfiltration is PROHIBITED.
# - Window:       2026-06-11 09:00-17:00 AEST
# - PoC policy:   prove the bug with a benign callback (canary domain).
#                 DO NOT enumerate IAM creds or internal services beyond the PoC.
# - Stop:         5xx burst > 30s, client incident page, RoE end time.

# 2) Map suspicious endpoints (manual; do not scan unfocused)
#    Look for anything that accepts a URL or fetches one:
#    - PDF generators (HTML -> PDF), URL previewers, image proxies
#    - 'fetch and parse' integrations (RSS readers, webhooks, social-card previewers)
#    - SSO callback / OAuth redirect that takes a 'redirect_uri'
#    - Avatar uploaders that accept 'url'

# 3) Probe with a CANARY DOMAIN you own (DNS + HTTP logs)
#    A canary lets the bug fire safely without touching anything sensitive.
#    Set up before the engagement:
#        - canary.example-research.test -> server logs every request
#        - DNS wildcard *.canary.example-research.test
curl -s -X POST https://target.lab.example.test/api/webhook \
  -H 'content-type: application/json' \
  -d '{"url":"http://canary.example-research.test/probe-1"}'

#    Check your canary logs. If you see a hit from a target IP and User-Agent
#    that looks like the target's HTTP client, you have an SSRF.

# 4) Confirm — minimum PoC, no exfil
#    Add a unique path per finding to disambiguate request streams.
curl -s -X POST https://target.lab.example.test/api/webhook \
  -d '{"url":"http://canary.example-research.test/SSRF-CONFIRM-orderwebhook"}'

# 5) DO NOT pivot — do not curl 169.254.169.254 / 127.0.0.1:* / internal RFC1918
#    Even from a staging system the metadata endpoint may expose live IAM creds.
#    A canary hit + a careful description of the impact is enough for the report.

# 6) Write up the finding
cat <<'EOF'
## Finding: SSRF in /api/webhook 'url' parameter
Severity: High (server makes attacker-controlled outbound requests)
URL:      https://target.lab.example.test/api/webhook
Steps:    POST {"url":"http://canary.example-research.test/<token>"}
Evidence: canary log shows incoming request at HH:MM, source IP <target IP>.
Impact:   attacker can reach internal services (incl. cloud metadata).
Fix:
  - Resolve the URL and BLOCK private ranges (RFC1918, 127/8, 169.254/16, link-local).
  - Whitelist allowed protocols (https only) and ports (443).
  - Disable HTTP redirects in the outbound client, OR re-validate after each redirect.
  - Run the outbound fetch from a sandbox network with no IAM role attached.
EOF

# 7) Common SSRF defences worth recommending in the report
# - Defence in depth: deny outbound to 169.254.169.254 at the egress firewall.
# - On AWS, require IMDSv2 (HttpTokens=required) — defeats the most common SSRF -> IAM chain.
# - For URL fetchers, use a small allow-list per integration, not a global allow-all.

Why it matters

A clean PoC — canary hit + screenshot + stop — is what gets the finding triaged quickly and keeps the engagement scope tight. Probing metadata endpoints, even on staging, blurs the line with the very behaviour the RoE was meant to prevent and risks losing the test window.

Tip: Tweak the snippet with Try it Yourself », then sit the quiz at the bottom of the page.

Example

Example
# Test fetch-from-URL features (image proxies, webhooks) for SSRF.
#   - Try http://127.0.0.1, http://169.254.169.254 (cloud metadata)
#   - Try redirect-to-internal: https://your-domain → 302 → http://127.0.0.1
# Fix: outbound allow-list + block RFC1918 + use IMDSv2.
Try it Yourself »

Discussion

Loading…