SSRF: Turning the Server into an Attacker's Proxy
Server-Side Request Forgery: When the Server Fetches What the Attacker Wants
Server-Side Request Forgery (SSRF) is a vulnerability that allows an attacker to coerce a server into making HTTP requests to internal hosts that the attacker cannot reach directly. The server becomes a proxy that accesses resources on behalf of the attacker, bypassing firewalls and network segmentation.
How SSRF Works
When a web application fetches a user-supplied URL without proper validation, the attacker can supply URLs that point to internal services:
# VULNERABLE — fetches user-supplied URL without validation
@app.route('/fetch')
def fetch_url():
url = request.args.get('url')
response = requests.get(url) # Attacker controls the URL
return response.text
An attacker supplies http://169.254.169.254/latest/meta-data/iam/security-credentials/ and the server retrieves its own cloud IAM credentials.
SSRF Attack Targets
1. Cloud Metadata Endpoints
Cloud providers expose metadata services at 169.254.169.254 (AWS, GCP, Azure). These endpoints provide:
- Temporary IAM credentials
- User-data scripts (executed at instance boot)
- Instance configuration and network details
# AWS metadata — retrieve IAM credentials
curl http://169.254.169.254/latest/meta-data/iam/security-credentials/my-role
# AWS user-data — retrieve boot scripts
curl http://169.254.169.254/latest/user-data
2. Internal Services
Internal services often have no authentication because they are only accessible from within the network:
http://localhost:6379— Redis (no auth by default)http://localhost:9200— Elasticsearch (no auth by default)http://localhost:8080/manager/html— Tomcat manager (default credentials)http://127.0.0.1:22— SSH (port scanning via response timing)
3. File Protocol Abuse
If the application uses a library that supports non-HTTP protocols:
file:///etc/passwd
file:///proc/self/environ
gopher://127.0.0.1:6379/_*1%0d%0a$4%0d%0aFLUSHALL%0d%0a
4. DNS Rebinding
An attacker controls a domain that resolves to a public IP during validation but to an internal IP (e.g., 127.0.0.1) when the server actually fetches it. This bypasses URL validation that checks the resolved IP.
SSRF Impact
- Credential theft: Cloud metadata endpoints expose temporary credentials
- Internal network scanning: Response timing and content reveal internal hosts and ports
- Data exfiltration: Internal services (Redis, Elasticsearch) can be read or modified
- Remote code execution: Some internal services allow command execution (e.g., Jenkins script console)
Real-World SSRF
In 2019, Capital One suffered a breach that exposed 100 million customer records. An SSRF vulnerability in a web application firewall allowed the attacker to access the AWS metadata service and retrieve temporary credentials for an IAM role with excessive permissions. The attacker then used these credentials to access S3 buckets containing sensitive data.
SSRF Defense and Unsafe Deserialization
Defending Against SSRF and Understanding Deserialization Attacks
SSRF Defense Layers
1. URL Validation (Allow-List)
The most effective defense is to validate user-supplied URLs against an allow-list of permitted domains and IP ranges:
import ipaddress
from urllib.parse import urlparse
def is_safe_url(url):
parsed = urlparse(url)
# Only allow HTTP/HTTPS
if parsed.scheme not in ('http', 'https'):
return False
# Resolve the hostname and check the IP
try:
ip = ipaddress.ip_address(socket.gethostbyname(parsed.hostname))
except socket.gaierror:
return False
# Block private, loopback, and link-local addresses
if ip.is_private or ip.is_loopback or ip.is_link_local or ip.is_reserved:
return False
return True
2. Network-Level Controls
- Egress filtering: Configure firewalls to block outbound requests from application servers to internal IP ranges (10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, 169.254.0.0/16).
- Separate metadata endpoint: Use AWS IMDSv2 (Instance Metadata Service Version 2) which requires a session token obtained via a PUT request, preventing simple SSRF attacks.
- Disable unnecessary protocols: If the application only needs HTTP/HTTPS, disable support for
file://,gopher://,dict://, etc.
3. Response Validation
- Do not return the full response body to the user. Parse and sanitize the response.
- Set a maximum response size to prevent large data exfiltration.
- Set a short timeout to prevent slow-response attacks.
Unsafe Deserialization
Deserialization is the process of converting a serialized byte stream back into an object. Unsafe deserialization occurs when an application deserializes untrusted data without verification, allowing attackers to instantiate arbitrary objects and trigger unintended code paths.
How deserialization attacks work:
- Attacker crafts a serialized object that, when deserialized, executes a "gadget chain" — a sequence of method calls that leads to code execution.
- The application deserializes the object without checking its type or integrity.
- The gadget chain executes during or after deserialization.
Java deserialization example:
// VULNERABLE — deserializes untrusted data
ObjectInputStream ois = new ObjectInputStream(request.getInputStream());
Object obj = ois.readObject(); // Attacker controls the byte stream
Python pickle example:
# VULNERABLE — deserializes untrusted data
import pickle
data = pickle.loads(request.POST['data']) # Attacker controls the data
PHP unserialize example:
// VULNERABLE
$data = unserialize($_POST['data']);
Gadget Chains
A gadget chain is a sequence of existing methods in the application's codebase (or its dependencies) that, when called in sequence during deserialization, produce a dangerous effect (command execution, file write, etc.). Common gadgets include:
- Templates:
freemarker.template.utility.Execute(Java) - Commons Collections:
InvokerTransformerchain (Java) - YAML:
yaml.loadwith!!python/object/apply:os.system(Python)
Deserialization Defense
1. Do not deserialize untrusted data. Use JSON or other data-only formats instead.
2. If deserialization is required:
- Use integrity checks (HMAC signatures) to verify the data has not been tampered with:
import hmac, hashlib
def safe_deserialize(data, secret):
# data = signature + payload
sig, payload = data[:32], data[32:]
expected = hmac.new(secret, payload, hashlib.sha256).digest()
if not hmac.compare_digest(sig, expected):
raise ValueError('Invalid signature')
return pickle.loads(payload)
- Use type restrictions (e.g., Java's
ObjectInputFilterin newer versions) - Use safe serialization formats (JSON, Protocol Buffers) instead of native serialization
3. Keep dependencies updated. Many deserialization gadgets exist in third-party libraries (e.g., Apache Commons Collections, Spring Framework).
Practice Problems
The following endpoint is vulnerable to SSRF. Identify the vulnerability and implement a fix. ```python @app.route('/webhook') def webhook(): url = request.json.get('url') response = requests.get(url, timeout=5) return response.text ``` An attacker can supply `http://169.254.169.254/latest/meta-data/` to access cloud metadata.
Solution
The endpoint fetches a user-supplied URL without any validation. Fix by implementing URL validation:
```python
import ipaddress
import socket
from urllib.parse import urlparse
def is_safe_url(url):
parsed = urlparse(url)
if parsed.scheme not in ('http', 'https'):
return False
try:
ip = ipaddress.ip_address(socket.gethostbyname(parsed.hostname))
except (socket.gaierror, ValueError):
return False
if ip.is_private or ip.is_loopback or ip.is_link_local or ip.is_reserved:
return False
return True
@app.route('/webhook')
def webhook():
url = request.json.get('url')
if not is_safe_url(url):
return jsonify({'error': 'URL not allowed'}), 403
response = requests.get(url, timeout=5)
return response.text
```
Additionally, implement network-level egress filtering to block requests to internal IP ranges. A Java application uses `ObjectInputStream.readObject()` to deserialize user-supplied data. Explain how an attacker could exploit this and what the defense should be.
Solution
An attacker crafts a serialized Java object that, when deserialized, triggers a gadget chain leading to remote code execution. The most famous example is the Apache Commons Collections gadget chain, which uses `InvokerTransformer` to execute arbitrary commands during deserialization.
Defense:
1. Do not deserialize untrusted data — use JSON instead
2. If deserialization is required, implement an `ObjectInputFilter` to restrict which classes can be deserialized
3. Add integrity checks (HMAC signatures) to verify the data has not been tampered with
4. Keep all dependencies updated — many gadgets exist in third-party libraries
5. Use the `serial-killer` or `SerialKiller` library as a runtime deserialization firewall Quiz
1. What is the primary defense against SSRF attacks?
2. Why are cloud metadata endpoints like 169.254.169.254 frequent SSRF targets?
3. What is a gadget chain in the context of deserialization attacks?
4. Which of the following is NOT a valid defense against unsafe deserialization?
5. What is DNS rebinding in the context of SSRF?
Flashcards
Question
What is SSRF?
Click to reveal answer
Answer
Server-Side Request Forgery — coercing a server into making requests to internal hosts the attacker cannot reach directly. The server becomes a proxy that accesses internal resources on behalf of the attacker.
Question
Why are cloud metadata endpoints (169.254.169.254) dangerous in SSRF attacks?
Click to reveal answer
Answer
They provide temporary IAM credentials and instance configuration without authentication. An attacker who can make the server fetch this endpoint can obtain credentials with the instance's IAM permissions.
Question
What is a gadget chain?
Click to reveal answer
Answer
A sequence of existing methods in the application or its dependencies that, when called in sequence during deserialization, produce a dangerous effect like command execution. Example: Apache Commons Collections InvokerTransformer chain.
Question
What is the primary defense against SSRF?
Click to reveal answer
Answer
URL validation — checking user-supplied URLs against an allow-list and blocking requests to private, loopback, and link-local IP ranges. Network-level egress filtering provides an additional layer.
Question
What is DNS rebinding in SSRF context?
Click to reveal answer
Answer
An attack where a domain resolves to a public IP during validation but to an internal IP when the server fetches it, bypassing IP-based URL validation. Defense: resolve the hostname once and use the resolved IP for the actual request.
Revision Notes
Key Takeaways
- 1. SSRF coerces a server into making requests to internal hosts the attacker cannot reach directly
- 2. Cloud metadata endpoints (169.254.169.254) are high-value SSRF targets because they expose temporary credentials without authentication
- 3. Primary SSRF defense: URL validation with allow-list + blocking internal IP ranges + network egress filtering
- 4. Unsafe deserialization can lead to remote code execution via gadget chains in third-party libraries
- 5. DNS rebinding bypasses IP-based URL validation by changing DNS resolution between validation and fetch
Interview Tips
- • Walk through the Capital One breach (2019) as a concrete example of SSRF leading to cloud credential theft
- • Explain why URL validation alone is insufficient — mention DNS rebinding and the need for network-level controls
- • Discuss deserialization gadget chains with the Apache Commons Collections example
- • Connect SSRF to cloud security — explain how IMDSv2 mitigates the metadata endpoint attack
Cheat Sheet
SSRF and Unsafe Deserialization Cheat Sheet
Phase: Web Vulnerabilities and the OWASP Top 10 | Level: advanced
SSRF Attack Targets:
- Cloud metadata:
http://169.254.169.254/latest/meta-data/ - Internal services: Redis (6379), Elasticsearch (9200), Tomcat manager (8080)
- File protocol:
file:///etc/passwd - DNS rebinding: domain resolves to public IP during validation, internal IP during fetch
SSRF Defense:
- URL validation with allow-list of permitted domains
- Block private, loopback, link-local, and reserved IP ranges
- Network-level egress filtering
- Use AWS IMDSv2 (requires session token)
- Disable unnecessary protocols (file://, gopher://)
Deserialization Defense:
- Do not deserialize untrusted data — use JSON instead
- HMAC integrity checks on serialized data
- ObjectInputFilter to restrict deserializable classes (Java)
- Keep dependencies updated (gadgets exist in third-party libraries)
Key points:
- SSRF coerces a server into making requests to internal hosts the attacker cannot reach directly
- Cloud metadata endpoints like 169.254.169.254 are frequent SSRF targets for credential theft
- Deserializing untrusted data can instantiate gadgets that execute arbitrary code paths
- DNS rebinding bypasses IP-based URL validation
Tags: ssrf, web-security
Continue Learning
Before You Start
- No prerequisites listed.