De la SSRF à la prise de contrôle d’un compte cloud : attaquer le service de métadonnées
Une seule faille SSRF dans un récupérateur d’URL peut atteindre le point d’accès des métadonnées cloud, voler les identifiants de rôle IAM et aboutir à une prise de contrôle totale du compte. Voici la chaîne d’attaque – et comment la briser.

Confiez-nous une application web qui récupère une URL que vous contrôlez – un import d’avatar, un testeur de webhook, un bouton du genre “générer un PDF à partir de ce lien” – et la première chose que nous tentons est une sonde SSRF vers les métadonnées cloud. L’adresse visée change à peine d’une cible à l’autre : l’adresse link-local 169.254.169.254, où AWS (et, sur des chemins légèrement différents, GCP et Azure) fait tourner un service de métadonnées d’instance qui distribue des identifiants IAM temporaires à la charge de travail.
Cette unique requête, c’est tout l’enjeu. Un constat qui se lit “le serveur a passé un appel sortant qu’il n’aurait pas dû” devient discrètement “le serveur nous a livré son identité cloud”. Nous avons vu un proxy d’images en lecture seule se muer en tête de pont qui lisait des buckets S3 et énumérait d’autres rôles. La server-side request forgery, c’est CWE-918, et sur un hôte cloud elle n’est presque jamais une simple note informative dans le rapport.
Ce guide suit la chaîne complète : comment une fonctionnalité ordinaire devient une SSRF, comment la requête atterrit sur le point d’accès des métadonnées, pourquoi IMDSv1 face à IMDSv2 décide si l’attaque fonctionne, et comment des identifiants volés grimpent vers la prise de contrôle du compte. Puis les défenses qui tiennent réellement à l’épreuve des tests.
Points clés
- La SSRF (CWE-918) amène un serveur à envoyer des requêtes HTTP vers des destinations choisies par l’attaquant, y compris des adresses internes que l’application peut atteindre mais pas l’internet public.
- Le gros lot sur un hôte cloud, c’est le point d’accès des métadonnées à 169.254.169.254. Sur AWS EC2 avec IMDSv1, il renvoie des identifiants de rôle IAM temporaires en HTTP en clair, sans aucune authentification.
- Ces identifiants s’injectent directement dans l’AWS CLI ou un SDK. Si le rôle de l’instance est surprivilégié, le bug dégénère en vol de données, mouvement latéral et prise de contrôle du compte.
- IMDSv2 exige un jeton de session émis par PUT et impose une limite de sauts, ce qui casse la plupart des tentatives naïves de SSRF vers les métadonnées. L’imposer – c’est-à-dire désactiver IMDSv1 – est le correctif à plus fort impact.
- Empilez le reste : allowlists d’egress, blocage des plages link-local et RFC 1918, réresolution des URL après chaque redirection, et rôles d’instance en moindre privilège.
Qu’est-ce que la SSRF, et pourquoi le cloud aggrave-t-il les choses ?
La SSRF est une faille où une application accepte une URL ou un nom d’hôte en entrée et effectue une requête côté serveur vers celui-ci, permettant à un attaquant de diriger cette requête là où elle n’aurait jamais dû aller. Le navigateur est hors circuit. C’est le serveur vulnérable qui passe l’appel à sa place, depuis l’intérieur de la frontière de confiance, au-delà du pare-feu périmétrique, avec l’accès réseau interne dont l’hôte dispose.
Sur une machine ordinaire, c’est déjà grave : panneaux d’administration internes, une instance Redis ou Elasticsearch non authentifiée, un balayage rapide du sous-réseau interne. Le cloud relève la mise pour une raison bien précise. Chaque grand fournisseur exécute un service de métadonnées sur la même adresse link-local magique, 169.254.169.254, joignable uniquement depuis l’instance elle-même, et son unique rôle est de remettre des secrets au code qui y tourne. Amenez l’application à l’interroger et l’attaquant hérite de l’identité de l’instance.
Comment une fonctionnalité normale se transforme-t-elle en SSRF ?
La fonctionnalité vulnérable est en général une de celles que l’équipe produit a livrées avec une certaine fierté. Les suspects récurrents que nous testons :
- Récupérateurs d’URL et “import depuis un lien” : photo de profil par URL, “importez vos données depuis cette URL”, importateurs de RSS et de sitemap. Le serveur déréférence tout ce que vous collez.
- Webhooks et testeurs de callback : “nous enverrons vos événements en POST vers votre point d’accès, cliquez sur Test.” Ce bouton de test déclenche une requête côté serveur vers un hôte que vous choisissez.
- Rendus de PDF et d’images : les moteurs HTML-vers-PDF et les captureurs d’écran Headless-Chrome suivent les références
<img>,<iframe>et CSSurl(). Pointez-en un vers le point d’accès des métadonnées et la sortie rendue peut vous renvoyer la réponse. - Processeurs de documents et de SVG : les parseurs XML et les moteurs de rendu SVG peuvent être poussés à récupérer des ressources externes – c’est là qu’une XXE se mue discrètement en SSRF.
Le signe révélateur est tout paramètre qui finit par devenir une requête sortante. Nous confirmons ce comportement d’abord avec un payload hors bande, en général Burp Collaborator ou un interactsh auto-hébergé, avant de toucher quoi que ce soit d’interne :
POST /api/avatar/import HTTP/1.1
Host: example.com
Content-Type: application/json
{"image_url":"http://abcd1234.oastify.com/ping"}
Si le listener enregistre un appel, le serveur récupère des URL pour nous. Notez si ce n’était qu’une résolution DNS ou un rappel HTTP complet, car un simple appel DNS pointe parfois vers un pipeline de rendu qui résout mais n’affiche pas le corps. Quoi qu’il en soit, nous le pointons désormais vers quelque chose d’intéressant.
Comment atteint-on le point d’accès des métadonnées à 169.254.169.254 ?
Une fois les requêtes sortantes confirmées, nous remplaçons l’hôte externe par l’adresse link-local des métadonnées. Sur une instance EC2 avec IMDSv1, la première étape demande quel rôle est attaché :
GET /latest/meta-data/iam/security-credentials/ HTTP/1.1
Host: 169.254.169.254
Acheminé via le paramètre vulnérable, cela n’est que :
{"image_url":"http://169.254.169.254/latest/meta-data/iam/security-credentials/"}
La réponse est le nom du rôle. Ajoutez-le au chemin et le point d’accès renvoie des identifiants valides :
GET /latest/meta-data/iam/security-credentials/example-app-role HTTP/1.1
Host: 169.254.169.254
{
"Code": "Success",
"AccessKeyId": "ASIAEXAMPLEKEY",
"SecretAccessKey": "EXAMPLEsecret...",
"Token": "IQoJb3JpZ2luX2VjE...",
"Expiration": "2026-07-02T18:00:00Z"
}
Quand un développeur a bricolé une blocklist naïve, les contournements classiques ressortent. En décimal, la même IP est http://2852039166/, en octal http://0251.0376.0251.0376/, et sous une forme IPv6 mappée IPv4 http://[::ffff:169.254.169.254]/. Un nom DNS qui résout vers l’adresse link-local marche aussi. Notre préféré en pratique est un open redirect sur un domaine autorisé : l’application valide docilement que l’URL pointe vers un hôte de confiance, suit le 302 et atterrit malgré tout sur le point d’accès des métadonnées. C’est exactement pourquoi valider la chaîne d’URL initiale, à elle seule, ne suffit pas.
Quelle est la différence entre IMDSv1 et IMDSv2 ?
C’est la différence entre un moyen et un critique. IMDSv1 est un simple service requête-réponse : tout processus capable d’envoyer un GET à 169.254.169.254 obtient une réponse. Pas de jeton, pas de session, pas d’authentification. C’est exactement la forme d’une SSRF basique – d’où le fait que les deux forment un couple si dangereux.
IMDSv2 rend le flux à état. Le client envoie d’abord un PUT pour obtenir un jeton, puis inclut ce jeton comme en-tête sur chaque GET suivant :
PUT /latest/api/token HTTP/1.1
Host: 169.254.169.254
X-aws-ec2-metadata-token-ttl-seconds: 21600
GET /latest/meta-data/iam/security-credentials/ HTTP/1.1
Host: 169.254.169.254
X-aws-ec2-metadata-token: <token-from-put>
Deux propriétés déjouent ici la plupart des SSRF. Premièrement, il faut un PUT, et bien des primitives de SSRF n’émettent que des GET et ne peuvent pas contrôler la méthode. Deuxièmement, il faut un en-tête de requête personnalisé que le récupérateur vulnérable n’ajoutera pas à votre place. IMDSv2 fixe aussi une limite de sauts par défaut de 1 pour la réponse, si bien que si la requête a été relayée ne serait-ce que par un saut réseau supplémentaire, le service de métadonnées la rejette en silence. Ce que nous voulons voir imposé, c’est IMDSv1 désactivé, pas seulement IMDSv2 disponible.
Qu’IMDSv2 soit disponible ne change rien en soi. Si IMDSv1 est encore accepté, un attaquant se contente d’emprunter l’ancien chemin, sans jeton. Le réglage qui compte, c’est HttpTokens = required.
Comment un accès à des identifiants volés devient-il une prise de contrôle de compte ?
Les identifiants issus du service de métadonnées sont des clés temporaires STS ordinaires : une clé d’accès, un secret et un jeton de session. Nous exportons les trois et pilotons la CLI sous l’identité du rôle de l’instance :
export AWS_ACCESS_KEY_ID=ASIAEXAMPLEKEY
export AWS_SECRET_ACCESS_KEY=EXAMPLEsecret...
export AWS_SESSION_TOKEN=IQoJb3JpZ2luX2VjE...
aws sts get-caller-identity
À partir de là, le rayon d’impact est une fonction directe de ce que ce rôle a le droit de faire. Le premier geste est une énumération discrète : quels buckets, quels secrets, quels autres rôles. Si le rôle peut lire Secrets Manager ou SSM Parameter Store, nous tirons régulièrement des mots de passe de base de données et des clés d’API qui ouvrent bien plus que la seule instance. S’il porte de larges droits IAM – l’anti-pattern que nous voyons bien trop souvent -, le chemin vers la prise de contrôle est court : attacher une politique admin, générer une nouvelle clé d’accès sur un utilisateur privilégié, ou assumer un rôle plus puissant. Côté attaquant, cela correspond à MITRE ATT&CK Unsecured Credentials (T1552) et à la réutilisation de matériel d’authentification alternatif.
La leçon de ces années de missions est sans détour. La gravité d’une SSRF est fixée par le rôle de l’instance, pas par le bug web lui-même. Un rôle étroitement délimité transforme un constat d’apparence effrayante en un constat circonscrit. Un rôle permissif transforme un unique paramètre d’URL en incident à l’échelle du cloud.
Comment prévenir les attaques SSRF vers les métadonnées ?
Il n’y a pas d’interrupteur unique, aussi recommandons-nous d’empiler les contrôles ci-dessous. Chacun pris isolément peut être contourné. Empilés ensemble, ils rendent la chaîne réellement difficile à mener à terme.
- Imposer IMDSv2 et désactiver IMDSv1. Mettez HttpTokens à required sur chaque instance et inscrivez-le dans les launch templates et les AMIs pour que les nouveaux hôtes en héritent par défaut. Là où une charge de travail n’a jamais besoin des métadonnées d’instance, coupez complètement le point d’accès.
- Bloquer les plages link-local et privées. L’application – ou un proxy sortant par lequel elle doit obligatoirement passer – devrait refuser les connexions vers 169.254.0.0/16, les plages RFC 1918 (10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16) et le loopback. Faites-le au niveau réseau, pas seulement dans le code applicatif.
- Filtrage d’egress avec allowlists. Le trafic sortant des hôtes applicatifs ne devrait atteindre que les destinations dont une fonctionnalité a légitimement besoin. Un récupérateur d’URL qui ne tire jamais que d’une seule API partenaire n’a rien à faire sur une IP interne.
- Valider et réresoudre les URL. Analysez l’URL, rejetez les schémas non HTTP et les ports insolites, résolvez le nom d’hôte en une IP, et vérifiez cette IP contre votre denylist avant de vous connecter. Revérifiez après chaque redirection pour qu’un 302 ne puisse pas vous conduire au service de métadonnées. Cela émousse aussi le DNS rebinding.
- Rôles d’instance en moindre privilège. Délimitez chaque rôle à ce que sa charge de travail utilise réellement, et rien de plus. C’est le contrôle qui plafonne les dégâts quand les autres cèdent.
Un avertissement de terrain : les blocklists de chaînes qui matchent “169.254.169.254” et s’en contentent échouent constamment face aux formes décimales, octales et IPv6. Résolvez en une IP numérique et comparez contre des plages, jamais contre le texte littéral.
Comment CyberXplore vous aide
La SSRF vers les métadonnées est l’un des chemins standard que nos testeurs empruntent lors d’une mission de test d’intrusion cloud. Nous éprouvons la fonctionnalité qui émet des requêtes sortantes, tentons la chaîne complète jusqu’au service de métadonnées, puis retraçons jusqu’où les identifiants de rôle volés porteraient réellement dans votre compte – pour que vous obteniez le véritable impact métier plutôt qu’une case cochée. Si vous voulez que cela soit joué contre votre propre environnement, demandez un devis et nous en définirons le périmètre avec vous.
FAQ
La SSRF reste-t-elle un risque réel si je tourne dans le cloud avec un pare-feu ?
Oui. Un pare-feu périmétrique n’y fait rien, car la requête malveillante provient de votre propre instance, précisément là où le service de métadonnées est joignable. La SSRF transforme votre serveur de confiance en proxy de l’attaquant, si bien que tout contrôle qui n’inspecte que le trafic entrant est contourné. Il vous faut des contrôles d’egress et un IMDSv2 imposé, pas seulement un pare-feu entrant.
IMDSv2 empêche-t-il totalement la SSRF de voler des identifiants ?
Il arrête la grande majorité des cas réels, mais ce n’est pas absolu. IMDSv2 déjoue la SSRF limitée aux GET et toute requête qui franchit un saut supplémentaire, ce qui couvre la plupart des bugs que nous trouvons. Une SSRF très flexible, capable d’émettre un PUT et d’injecter l’en-tête personnalisé requis, pourrait tout de même passer – raison pour laquelle IMDSv2 a sa place aux côtés des rôles en moindre privilège et du filtrage d’egress, plutôt que d’être traité comme le seul contrôle.
Qu’est-ce que l’adresse 169.254.169.254 et pourquoi revient-elle sans cesse ?
C’est l’IP link-local que les fournisseurs cloud utilisent pour exposer leur service de métadonnées d’instance, joignable uniquement depuis l’instance elle-même. Sur AWS, elle sert les métadonnées EC2 et, surtout, les identifiants de rôle IAM temporaires. GCP et Azure exposent des services similaires à cette adresse ou via des noms d’hôte de métadonnées – ce qui en fait la cible universelle et première dès qu’un attaquant confirme une SSRF sur un hôte cloud.
Comment savoir si une fonctionnalité est vulnérable avant qu’un attaquant ne le fasse ?
Cherchez toute entrée qui devient une requête sortante côté serveur : importateurs d’URL, webhooks, rendus de PDF ou d’images, parseurs de documents. Testez chacune avec un listener hors bande tel que Burp Collaborator ou interactsh. Si le serveur atteint votre listener, vérifiez s’il atteindra aussi des adresses internes et link-local et s’il suit les redirections vers elles. C’est le comportement que nous sondons dans un test d’intrusion.
Quels CWE et quels standards couvrent ce problème ?
La server-side request forgery, c’est CWE-918, et elle a sa propre catégorie, A10, dans le OWASP Top 10 (2021). Côté attaquant, la récolte d’identifiants d’instance et leur usage se rattachent à MITRE ATT&CK autour des unsecured credentials (T1552) et à l’emploi de matériel d’authentification alternatif. Les citer dans un rapport aide les équipes à hiérarchiser et à router correctement le correctif.
Quel est le correctif le plus important à lui seul ?
Imposez IMDSv2 avec IMDSv1 désactivé sur chaque instance, puis délimitez vos rôles d’instance au moindre privilège. Le premier changement casse d’emblée la technique courante de SSRF vers les métadonnées. Le second garantit que même un vol d’identifiants réussi reste confiné à un ensemble réduit et bien compris de permissions plutôt qu’à votre compte entier.



