Aller au contenu

SSRF vers IMDS et vol d’identifiants IAM

Une SSRF (Server-Side Request Forgery, falsification de requête côté serveur) transforme un service applicatif en navigateur réseau interne piloté par un attaquant. Dans un environnement Amazon Web Services (AWS), cette capacité suffit parfois à interroger le service de métadonnées d’instance, récupérer des identifiants IAM (Identity and Access Management), puis agir dans le compte cloud avec les droits du rôle attaché à la machine.

→ Lire l’explication complète de la marmotte

Une SSRF apparaît lorsqu’une application effectue une requête sortante à partir d’une adresse fournie ou influencée par l’entrée utilisateur. Les fonctions de prévisualisation d’URL, import d’images, webhooks, convertisseurs de documents, validateurs de liens et clients HTTP embarqués sont les coupables habituels. Le paramètre ne s’appelle pas forcément url : une option avatar, callback, feed, template ou remote_file fait très bien le travail. Le vocabulaire change, le problème conserve ses dents.

Sur une instance Elastic Compute Cloud (EC2), le service Instance Metadata Service (IMDS) répond à l’adresse IPv4 locale 169.254.169.254. Cette adresse appartient à la plage link-local : elle n’est pas routable sur Internet, mais elle est accessible depuis l’instance. Le service expose notamment l’identité de l’instance, sa région, son profil et, lorsqu’un rôle IAM est attaché, des identifiants temporaires émis par Security Token Service (STS).

La chaîne d’attaque complète suit généralement cet ordre :

  1. L’attaquant trouve un endpoint applicatif capable de joindre une URL arbitraire.
  2. Il force l’application à demander http://169.254.169.254/.
  3. Il énumère les chemins disponibles ou vise directement les métadonnées IAM.
  4. Il récupère le nom du rôle attaché à l’instance.
  5. Il récupère les clés temporaires, le jeton de session et leur date d’expiration.
  6. Il utilise ces identifiants hors de l’instance via les interfaces AWS.
  7. Il énumère les permissions du rôle, les ressources accessibles et les chemins d’escalade.
  8. Il persiste ou pivote si le rôle peut créer, attacher ou déléguer des permissions.

Le point décisif est souvent mal compris : IMDS ne crée pas le privilège. Il révèle au processus compromis les privilèges déjà accordés à l’instance. Une SSRF sur une machine portant un rôle de lecture limité reste un incident sérieux, mais elle ne devient pas mécaniquement administrateur global. Une SSRF sur une machine portant un rôle capable de créer des utilisateurs IAM, d’attacher des politiques, d’exécuter des commandes sur d’autres instances ou de lire tous les secrets devient une compromission de compte potentielle. Le cloud ne fabrique pas de magie, seulement des graphes d’autorisations très créatifs.

La vérification des droits récupérés doit viser les actions d’escalade connues : iam:CreateAccessKey, iam:CreateUser, iam:AttachUserPolicy, iam:AttachRolePolicy, iam:PutRolePolicy, iam:UpdateAssumeRolePolicy, sts:AssumeRole, ec2:RunInstances, ec2:ModifyInstanceAttribute, lambda:CreateFunction, lambda:UpdateFunctionCode et les permissions de passage de rôle telles que iam:PassRole. La combinaison compte plus que chaque permission isolée. Une fonction Lambda capable de recevoir un rôle plus puissant, par exemple, est un ascenseur administratif avec une étiquette décorative.

Les principes d’architecture cybersécurité restent applicables ici : séparer les plans d’administration, limiter les identités de workload et refuser les rôles omnibus. Un rôle EC2 ne doit pas devenir le trousseau du compte parce qu’un déploiement devait « juste accéder à deux buckets ».

IMDSv2 est la seconde version du service de métadonnées AWS. Elle exige un jeton de session obtenu par une requête PUT, puis transmis dans les requêtes suivantes. Le test ci-dessous doit être exécuté uniquement depuis une instance EC2 dont l’accès aux métadonnées est autorisé. Il vérifie l’identité et le rôle exposés sans afficher les identifiants temporaires eux-mêmes.

Fenêtre de terminal
# Demander un jeton IMDSv2 valable 60 secondes.
TOKEN="$(curl --fail --silent --show-error \
--request PUT \
--header 'X-aws-ec2-metadata-token-ttl-seconds: 60' \
http://169.254.169.254/latest/api/token)"
# Vérifier l'identité AWS portée par l'instance.
curl --fail --silent --show-error \
--header "X-aws-ec2-metadata-token: ${TOKEN}" \
http://169.254.169.254/latest/dynamic/instance-identity/document
# Afficher le nom du rôle IAM attaché à l'instance, s'il existe.
curl --fail --silent --show-error \
--header "X-aws-ec2-metadata-token: ${TOKEN}" \
http://169.254.169.254/latest/meta-data/iam/security-credentials/

Le dernier chemin retourne un nom de rôle. Une requête sur ce nom retourne un document JSON (JavaScript Object Notation) contenant un identifiant de clé d’accès, une clé secrète, un jeton de session et une expiration. Ces valeurs sont précisément ce que l’application AWS SDK (Software Development Kit) utilise automatiquement lorsqu’elle tourne sur l’instance.

Tester seulement la réponse HTTP 401 sans jeton ne valide pas toute la défense. Un proxy local, un agent de sécurité ou une bibliothèque HTTP peut encore relayer les en-têtes nécessaires. Il faut tester les vecteurs applicatifs réels : redirections, URL encodées, résolution DNS (Domain Name System), adresses IPv6 (Internet Protocol version 6), formats numériques alternatifs et chaînes de redirection. Une liste noire de 169.254.169.254 arrêtée au premier contrôle est un château de sable avec pare-feu marketing.

IMDSv2 casse la forme la plus commune de SSRF, celle où l’application accepte une URL et effectue un simple GET. Le jeton impose d’abord une requête PUT vers le service de métadonnées, avec l’en-tête X-aws-ec2-metadata-token-ttl-seconds. Le jeton ne peut ensuite être utilisé que depuis l’instance qui l’a demandé et pendant la durée choisie, entre une seconde et six heures.

Cette propriété ne doit pas être surinterprétée. IMDSv2 ne protège pas contre une SSRF capable de contrôler la méthode HTTP, les en-têtes et le corps de la requête. Une primitive SSRF complète peut demander le jeton, le lire dans la réponse, puis l’utiliser. Une vulnérabilité de type exécution de code à distance contourne également IMDSv2, puisque le processus compromis peut appeler le service comme n’importe quel autre processus local. Le jeton réduit une classe de relais, il ne transforme pas une application compromise en sanctuaire.

Le hop limit, configuré via HttpPutResponseHopLimit, limite le nombre de sauts réseau autorisés pour la réponse au PUT de création du jeton. La valeur recommandée est 1 pour une instance classique : le jeton ne doit pas traverser un composant réseau supplémentaire. Dans un environnement conteneurisé, cette valeur peut casser l’accès aux métadonnées depuis un conteneur, car le trafic traverse souvent l’espace réseau de l’hôte ou un pont virtuel. Augmenter la limite à 2 peut rétablir le fonctionnement, mais restaure aussi une partie de la surface d’attaque. Le compromis est net : compatibilité des workloads contre confinement du jeton.

La position à tenir est donc stricte : imposer IMDSv2 et un hop limit de 1 partout où les processus n’ont pas besoin d’accéder directement à IMDS. Si des conteneurs doivent obtenir une identité AWS, utiliser un mécanisme d’identité dédié à l’orchestrateur plutôt que partager le rôle de l’instance. Un rôle de nœud visible par tous les pods est une erreur d’architecture, pas une optimisation.

Les paramètres AWS à imposer sont HttpTokens=required, HttpEndpoint=enabled seulement si le service est nécessaire, et HttpPutResponseHopLimit=1 lorsque l’architecture le permet. Désactiver totalement IMDS (HttpEndpoint=disabled) est préférable pour les instances ne consommant aucune identité ou donnée de métadonnées. La compatibilité applicative doit être validée avant bascule : des agents anciens et des SDK vieillissants échouent parfois silencieusement, ce qui produit le genre de panne que les tableaux de bord appellent élégamment « comportement inattendu ».

IMDSv2 est obligatoire, mais insuffisant seul. La chaîne doit être coupée à plusieurs endroits, car une SSRF exploitable dispose parfois de plus de capacités que prévu.

Le premier garde-fou est applicatif. Les clients HTTP sortants doivent utiliser une liste d’autorisation de destinations, définie par schéma, nom DNS, port et chemin lorsque le protocole le permet. Autoriser seulement https, refuser les redirections par défaut, résoudre le nom puis vérifier toutes les adresses retournées avant connexion. La vérification doit être répétée après chaque redirection et après chaque nouvelle résolution DNS, sinon une réponse DNS rebinding (changement d’adresse après validation) contourne le contrôle.

Les plages suivantes doivent être bloquées pour les fonctions applicatives qui n’ont pas une raison explicite de les joindre : loopback (127.0.0.0/8 et ::1), link-local (169.254.0.0/16 et fe80::/10), réseaux privés RFC 1918 (Request for Comments 1918), multicast et adresses réservées. Ce filtrage doit se faire sur l’adresse effectivement connectée, pas seulement sur la chaîne fournie par l’utilisateur. http://2130706433/, qui représente 127.0.0.1, n’est pas une preuve de créativité offensive, juste un rappel que parser une URL est plus compliqué qu’un startsWith.

Le second garde-fou est réseau. Un proxy de sortie centralisé peut refuser les destinations non autorisées et journaliser les requêtes. Pour les workloads qui n’ont aucune raison d’interroger IMDS, bloquer 169.254.169.254:80 à la sortie de l’hôte ou du namespace réseau coupe la récupération directe. Cette règle doit préserver les composants légitimes qui dépendent des métadonnées : la casser sans inventaire provoque des échecs d’authentification difficiles à diagnostiquer.

Les groupes de sécurité AWS ne filtrent pas le trafic entre une instance et IMDS. Ils contrôlent le trafic Elastic Network Interface (ENI), pas ce service local. Compter sur eux pour stopper IMDS revient à demander à un détecteur de fumée de gérer les droits IAM. Le filtrage doit être appliqué dans le système, le pare-feu de l’hôte, le réseau de conteneurs ou un proxy egress.

La supervision doit corréler trois sources : journaux applicatifs des requêtes sortantes, CloudTrail pour les appels AWS réalisés avec le rôle, et VPC Flow Logs (Virtual Private Cloud Flow Logs) pour les flux réseau observables. Une hausse de AssumeRole, de lecture de secrets, de création de clés d’accès ou d’appels depuis une région inhabituelle après une requête applicative vers une adresse locale mérite une investigation immédiate. Les identifiants STS expirent, mais leur courte durée ne remplace pas la réponse à incident : révoquer les permissions du rôle, isoler l’instance, analyser l’entrée SSRF et rechercher les sessions actives.

IMDSv2 ne corrige pas la SSRF initiale. L’application reste capable de scanner des services internes, d’atteindre des consoles d’administration, d’appeler des webhooks privés ou de contourner une segmentation réseau mal conçue. Une défense focalisée sur 169.254.169.254 traite le symptôme le plus spectaculaire, pas la primitive d’attaque.

IMDSv2 ne réduit pas non plus les permissions IAM excessives. Si le rôle de workload peut lire tous les secrets, chiffrer des volumes, modifier les journaux ou déléguer ses droits, la compromission du processus reste critique. Les mécanismes de moindre privilège décrits dans les pages identités et accès doivent être appliqués au rôle lui-même : actions minimales, ressources nommées, conditions de session, permissions boundaries lorsque la délégation existe.

Enfin, les identifiants temporaires ne constituent pas une rotation suffisante. Leur expiration limite la durée de réutilisation, mais un attaquant peut les renouveler tant que l’accès à l’instance demeure. La reprise exige donc l’élimination de la SSRF, la réduction ou le remplacement du rôle compromis, l’analyse des appels effectués et la vérification des mécanismes de persistance créés avec ces droits.

Sans IMDSv2 imposé ni filtrage des sorties applicatives, la perte principale est la traçabilité : les appels effectués avec les identifiants volés arrivent dans CloudTrail sous l’identité du rôle de l’instance, strictement au même format que les appels légitimes de l’application. Distinguer les deux exige alors de croiser adresses sources, régions et horaires, travail impossible si les journaux applicatifs des requêtes sortantes ne sont pas conservés. La reprise s’alourdit aussi : ces clés temporaires ne se révoquent pas une par une, il faut détacher ou restreindre le rôle, ce qui interrompt le service légitime porté par la même instance. S’ajoutent les effets collatéraux d’un rôle disposant d’actions de calcul ou de stockage : ressources créées, volumes chiffrés ou configurations modifiées, dont l’inventaire doit être reconstitué après coup.

Une faille appelée falsification de requête côté serveur apparaît lorsqu’une application reçoit une adresse et va chercher son contenu sans assez vérifier la destination. Un attaquant peut alors demander à l’application de contacter des services normalement invisibles depuis Internet.

Dans une machine louée chez un fournisseur cloud, une adresse locale fournit des informations sur la machine. Cette adresse peut aussi donner accès à une identité temporaire attribuée à la machine. Cette identité permet à des programmes installés sur la machine d’accéder à des fichiers, bases de données, secrets ou autres services autorisés.

L’attaque commence quand l’application vulnérable contacte cette adresse locale à la place de l’attaquant. Si elle récupère les informations d’identité, l’attaquant peut ensuite les utiliser pour demander des actions au fournisseur cloud. Le résultat dépend des droits donnés à la machine. Des droits limités réduisent les dégâts. Des droits permettant de créer des comptes, changer des permissions ou utiliser une identité plus puissante peuvent mener à la prise de contrôle du compte cloud.

La version récente du service de métadonnées demande d’abord un jeton temporaire. Cela bloque les failles qui ne savent envoyer qu’une demande basique. Cela échoue toutefois si la faille permet aussi de choisir les méthodes et les en-têtes des demandes. Il faut donc corriger l’application, limiter les destinations qu’elle peut joindre, bloquer l’adresse locale quand elle n’est pas nécessaire, et donner à chaque machine seulement les droits strictement requis.

À lire aussi

Pages voisines

: les deux pages se citent l’une l’autre.