Comprenez le risque lié aux politiques. Posez vos questions sur les politiques en langage courant. Demander une démo →

Published:

Briser les chaînes d'attaque dans AWS : les rôles IAM

by FireMon

Au cours de l’année écoulée, j’ai constaté une forte progression de l’intérêt pour des conseils concrets sur le traitement des incidents de sécurité dans le cloud, avec des techniques cloud natives. À mesure que les organisations transfèrent leurs charges de production vers le cloud, les professionnels de la sécurité réalisent rapidement que les fondamentaux, bien que conceptuellement similaires, sont assez différents dans la pratique. L’un de ces concepts clés est celui de la kill chain, terme employé pour la première fois par Lockheed Martin pour décrire le processus de l’attaquant. Rompez un maillon et vous rompez l’attaque : cela correspond bien à l’association de la défense en profondeur et des composantes actives de la réponse à incident.

Dans les déploiements cloud, il existe quatre grandes catégories d’attaques, chacune avec des kill chains différentes :

  1. Les attaques contre la plateforme cloud elle-même. Si l’on écarte une compromission fondamentale du fournisseur cloud (hors du contrôle du client), ces attaques portent généralement sur des erreurs de configuration des services cloud. Si vous laissez un bucket S3 public, si vous omettez de placer un authorizer sur une API Gateway ou si vous exposez vos identifiants AWS sur GitHub, cela relève de cette catégorie.
  2. Les attaques contre les ressources et applications déployées par le client dans le cloud. Ces attaques traditionnelles ne diffèrent en rien de celles menées contre votre centre de données. Parmi les exemples courants figurent l’injection SQL dans une application web et les serveurs vulnérables dont les mauvais ports sont ouverts sur Internet. Elles tendent à être un peu plus limitées qu’elles ne le seraient contre un centre de données, à condition que vous utilisiez des comptes/abonnements/projets et des VPC ou réseaux virtuels pour restreindre le rayon d’impact.
  3. Les attaques contre vos administrateurs et développeurs cloud. Lors de votre prochain test d’intrusion, veillez à laisser les attaquants tenter d’hameçonner vos développeurs et vos administrateurs. C’est l’un des meilleurs moyens de rebondir vers le cloud, car il est souvent bien plus facile pour un attaquant d’accéder au système d’un développeur que de casser l’application cloud elle-même. Nous traiterons ce point à l’avenir, mais commençons simplement par dire « le MFA est mon ami ».
  4. Les attaques mixtes. C’est la catégorie sur laquelle nous allons nous concentrer aujourd’hui. Dans ces attaques, l’acteur malveillant pénètre dans un élément déployé dans le cloud, puis s’en sert pour rebondir vers le plan de gestion du cloud. (Certains considèrent que les attaques contre les développeurs sont mixtes, mais je préfère les traiter à part.)

En règle générale, je pars toujours du principe que toute attaque réussie, à quelque niveau que ce soit, peut s’élever ou rebondir en attaque mixte ; à ce moment-là, la sécurité de votre plan de gestion et votre réponse à incident deviennent vos meilleures défenses.

Aujourd’hui, je vais me concentrer sur l’un des processus d’attaque mixte les plus courants et exposer un ensemble de contrôles de détection et de prévention permettant de rompre la kill chain. Avant d’entrer dans les détails, ne prenez pas cet article pour une simplification excessive d’un problème complexe. Gérer à grande échelle ce dont je vais parler est extrêmement difficile, même lorsque l’on sait ce que l’on fait.

Dans les prochaines semaines, nous déploierons nos premiers Ops spécifiquement conçus pour ces problématiques auprès de nos clients en accès anticipé, et ils devraient être en production relativement peu de temps après.

L’attaque mixte sur AWS : extraction des identifiants de rôle IAM

Dans une attaque mixte, l’acteur malveillant pénètre dans un élément plus traditionnel, puis s’en sert pour rebondir vers le plan de gestion du cloud. Cela se produit principalement de trois manières. Dans chaque cas, l’attaquant extrait soit des identifiants statiques stockés, soit des identifiants éphémères de rôle IAM, que nous expliquerons dans un instant.

  1. La compromission directe d’une instance ou d’un conteneur. Par exemple, si vous laissez le port 22 ouvert et que l’attaquant parvient à s’introduire ou à obtenir un accès shell d’une autre manière.
  2. La falsification de requête côté serveur (SSRF). L’attaquant exploite (généralement) une vulnérabilité d’un serveur ou service web et peut exécuter des commandes sans obtenir d’accès shell.
  3. La compromission d’une fonction Lambda. Bien qu’il soit impossible d’obtenir un shell sur une Lambda, celles-ci restent exposées aux vulnérabilités d’exécution de code, voire à l’exécution de code arbitraire si elles comportent une faille applicative. Les conséquences exactes peuvent s’apparenter à une SSRF.

Dans chaque cas, l’objectif de l’attaquant est d’obtenir des identifiants pour le plan de gestion AWS, puis d’exploiter les privilèges existants ou d’élever ses privilèges. Nous aborderons l’élévation de privilèges dans un prochain article ; pour l’heure, nous nous concentrerons sur la nature de ces identifiants et sur les moyens d’en empêcher l’usage abusif.

La plupart des gens comprennent ce que sont les identifiants statiques ; dans AWS, il s’agit d’une clé d’accès et d’une clé secrète. Ils s’apparentent à un nom d’utilisateur et à un mot de passe, mais servent aux appels d’API AWS. La version actuelle utilise un procédé cryptographique appelé Signature 4 pour signer les requêtes HTTP lors de ces appels d’API. Vous pouvez et devez les traiter exactement comme un nom d’utilisateur et un mot de passe — et vous ne devez jamais les stocker dans des ressources cloud telles que des instances et des Lambdas.

Les rôles IAM sont plus délicats lorsque l’on débute avec AWS : ils sont à la fois formidables et inquiétants. Un rôle IAM dans AWS est en pratique un conteneur d’autorisations que vous utilisez pour une session. Les rôles IAM sont très utiles parce qu’il ne s’agit pas d’identifiants à proprement parler… lorsque vous endossez le rôle, AWS fournit un jeu d’identifiants pour une session limitée dans le temps. Les rôles sont une notion « interne à AWS ». Vous pouvez les affecter à des ressources au sein d’AWS (comme une instance ou une fonction Lambda) et cette ressource peut alors effectuer des appels d’API sans identifiants statiques stockés ! Nous utilisons des rôles pour les connexions d’identité fédérée, les instances, les fonctions Lambda et tous les autres services au sein d’AWS. Les clés d’accès ne servent réellement que lorsque vous créez un utilisateur dans un compte AWS ; pour tout le reste, nous utilisons des rôles.

Quatre types d’autorisations sont associés aux rôles :

  1. Ce que le rôle peut faire au sein d’AWS. Il s’agit purement et simplement des politiques d’autorisation que vous attachez au rôle.
  2. Qui ou quoi peut utiliser le rôle (la politique de confiance). Créer un rôle ne signifie pas que n’importe qui ou n’importe quoi peut l’utiliser : cette politique en restreint l’accès, par exemple, à des instances AWS ou à une fonction Lambda précise.
  3. Une limite d’autorisations destinée à restreindre la portée du rôle. Ce point est un peu plus complexe et n’est pas pertinent pour notre propos d’aujourd’hui ; nous y reviendrons plus tard.
  4. Lorsque vous endossez un rôle pour une session, vous pouvez également spécifier un sous-ensemble de vos autorisations existantes à utiliser pour cette session. C’est une fonctionnalité intéressante pour le moindre privilège, mais elle n’est pas non plus totalement pertinente pour notre propos d’aujourd’hui.

Il est sans doute plus simple d’expliquer ce fonctionnement en le déroulant. Supposons que j’aie une application qui doit accéder à un bucket S3 ou à une base Dynamo. Je crée un rôle IAM pour l’instance et je configure la politique de confiance afin que le service EC2 puisse utiliser ce rôle. Je lance ensuite une instance et je lui affecte le rôle. AWS exécute l’instance et lui fait endosser le rôle. L’endossement du rôle ouvre une session et attribue une clé d’accès, une clé secrète et un jeton de session. AWS fait ensuite tourner ces identifiants toutes les 1 à 6 heures, et l’instance peut désormais effectuer les appels d’API autorisés par les politiques d’autorisation.

Bien que les identifiants ne se trouvent pas dans l’instance, ils restent accessibles à celle-ci. Tout code qui s’y exécute doit connaître les identifiants pour effectuer les appels d’API réels permettant d’accéder à S3 et à Dynamo ; c’est pourquoi ce que l’on appelle le service de métadonnées les fournit à la demande. Le service de métadonnées est un élément particulier d’AWS, destiné aux instances et aux conteneurs, qui contient toutes les informations relatives à leur configuration. Il est par exemple très important qu’un serveur puisse obtenir son adresse IP.

C’est là qu’intervient l’attaque.

Le service de métadonnées est simplement une url à laquelle vous pouvez accéder et qui renvoie les informations demandées. curl 169.254.169.254/latest/meta-data/ fournira toutes les informations de base, et le chemin curl 169.254.169.254/latest/meta-data/iam-security-credentials/ fournira la clé d’accès, la clé secrète et le jeton. (Dans le cas d’une attaque visant une Lambda, tout cela se présente différemment et l’on utilise du code SDK plutôt que curl, mais les mêmes principes s’appliquent.)

L’attaquant peut ensuite copier ces identifiants et les utiliser ailleurs, en les intégrant dans des outils plutôt que d’avoir à charger et exécuter du code sur le serveur compromis. De plus, étant fondé sur des URL, le service de métadonnées est exposé à un éventail plus large d’attaques SSRF, puisqu’il n’est pas nécessaire de disposer d’une exécution de code arbitraire complète. Les identifiants finiront par expirer, mais selon l’attaque, l’attaquant peut simplement revenir en chercher de nouveaux lorsqu’il constate que les précédents ne fonctionnent plus.

Les attaquants avisés utilisent aujourd’hui ces identifiants depuis un compte AWS qu’ils contrôlent, car Amazon dispose d’outils permettant de détecter les identifiants extraits et utilisés en dehors de ses plages d’adresses connues.

Briser la chaîne d'attaque d'extraction de rôles IAM

Cartographions la chaîne d'attaque. L'attaquant doit procéder comme suit :

  • Découvrir et exploiter une vulnérabilité dans une instance, un conteneur ou une fonction Lambda lui permettant d'accéder aux identifiants du rôle. Il s'agit presque toujours d'une erreur côté client… absence de correctifs, ouverture de ports inappropriés ou déploiement de code vulnérable.
  • Extraire les identifiants du rôle en cours.
  • Exécuter avec succès les appels d'API autorisés dans un environnement qu'il contrôle.
  • Faire quelque chose de malveillant dans le périmètre de la politique de permissions du rôle IAM autorisé. Enfin, probablement malveillant : rares sont les attaquants qui corrigent votre code à votre place.

Les techniques suivantes permettent de rompre différents maillons de la chaîne et associent des contrôles de détection et de prévention. Ne vous inquiétez pas si cela paraît démesuré… très, TRÈS peu des organisations avec lesquelles je travaille les mettent en œuvre de façon exhaustive, en particulier à grande échelle.

6 techniques pour aider à rompre différents maillons de la chaîne d'attaque

  1. Gestion des vulnérabilités
  2. Politiques de permissions IAM du moindre privilège avec restrictions de ressources
  3. Utiliser des restrictions conditionnelles d'origine de requête (IP, VPC ou autres) dans les politiques de permissions
  4. Utiliser des points de terminaison de service avec politiques + politiques de ressources
  5. Ajouter des proxys de métadonnées avec filtres sur l'en-tête HTTP User Agent (protection du service de métadonnées)
  6. Protection contre l'usage dupliqué des rôles

Gestion des vulnérabilités

  • Complexité : modérée
  • Efficacité : faible
  • Évolutivité : difficile
  • Type : détection et prévention

Sans surprise, vous devez commencer par éliminer toutes les vulnérabilités et erreurs de configuration initiales que l'attaquant peut utiliser pour rebondir et récupérer des identifiants. Je n'ai qualifié la complexité que de modérée car il n'y a là rien de nouveau ni de spécifique au cloud. Mais j'estime aussi l'efficacité faible, car la gestion exhaustive des vulnérabilités n'a pas vraiment empêché les légions de compromissions survenues ces dernières décennies. Simple sur le principe, extrêmement complexe à grande échelle.

Politiques de permissions IAM du moindre privilège avec restrictions de ressources

  • Complexité : modérée
  • Efficacité : élevée
  • Évolutivité : modérée à difficile
  • Type : prévention

Dans AWS, les politiques IAM refusent par défaut et comportent des instructions explicites d'autorisation et de refus. Vous pouvez par exemple écrire une politique qui autorise uniquement la lecture d'un bucket S3. Elles comportent également des restrictions de ressources : là où l'instruction d'autorisation permet au rôle d'appeler l'API de lecture, la restriction de ressources n'autorise le rôle qu'à lire des buckets ou objets précis. C'est ici que vos défenses doivent TOUJOURS commencer. Lors de mes évaluations, je constate, sur presque chaque projet, des politiques IAM accordant trop de privilèges (les appels d'API) et trop peu de restrictions de ressources. Oui, ce service a peut-être besoin d'accéder à une base Dynamo, mais a-t-il besoin d'accéder à toutes les tables ? Ce contrôle n'est pas trop difficile à mettre en œuvre à petite échelle, mais plus vous êtes grand et plus les décisions de politique sont prises par des humains, plus il est difficile d'être cohérent à grande échelle. Il est également important d'ajouter des instructions explicites de refus au cas où quelqu'un ajouterait au rôle une nouvelle politique avec de nouvelles permissions. Les permissions sont cumulatives, mais toute instruction de refus prévaut sur les instructions d'autorisation.

Utiliser des restrictions conditionnelles d'origine de requête (IP, VPC ou autres) dans les politiques de permissions

  • Complexité : élevée
  • Efficacité : modérée à élevée
  • Évolutivité : difficile
  • Type : prévention

Les politiques IAM prennent en charge des instructions conditionnelles offrant un éventail d'options, dont l'adresse IP ou le VPC source. Si vous savez qu'un rôle donné ne doit jamais effectuer d'appels d'API que depuis une ressource précise de votre pile applicative, vous pouvez verrouiller l'autorisation sur cette adresse IP ou ce sous-réseau exact. Si l'attaquant dérobe les identifiants et tente de les utiliser ailleurs, les appels d'API échoueront. C'est une masse à guidage de précision : simple sur le principe et difficile à exécuter, car d'autres complexités peuvent interférer avec une mise en œuvre correcte. Par exemple, les appels d'API vers les services AWS passent directement par Internet, transitent par une passerelle NAT ou sont routés en interne via un point de terminaison de service (dont nous parlerons dans un instant). L'adresse IP détectée dépendra du chemin emprunté par l'appel d'API vers Internet. Tout cela est gérable, détectable (et automatisable), mais vous voudrez d'abord vous documenter pour bien comprendre les différentes permutations.

Consultez la première partie de cet article de Netflix pour quelques exemples.

À moins d'exécuter votre fonction Lambda dans un VPC, cette option ne permettra pas de protéger une fonction compromise.

Utiliser des points de terminaison de service avec politiques + politiques de ressources

  • Complexité : modérée
  • Efficacité : modérée à élevée
  • Évolutivité : modérée
  • Type : prévention

Dans AWS, un point de terminaison de service est comparable à une dérivation sur le réseau qui prend le trafic normalement destiné à un service AWS via Internet et le réachemine en interne. Ils ont été créés à l'origine pour permettre à des sous-réseaux entièrement privés dans AWS, sans aucun moyen d'atteindre Internet, d'accéder malgré tout à certains services AWS. Les points de terminaison prennent en charge des politiques que vous pouvez utiliser pour restreindre les accès et les actions d'une manière très similaire aux politiques IAM. Dans ce cas, vous ajoutez des restrictions à la politique du point de terminaison afin de n'autoriser que l'accès à des ressources précises derrière ce point de terminaison (S3 en est l'exemple le plus courant). Vous précisez quels buckets sont autorisés, et aucune autre ressource de ce sous-réseau n'a accès à ce service. Considérez cela comme un filet de sécurité pour la politique IAM : avec un point de terminaison de service doté d'une politique restrictive, même si quelqu'un accorde accidentellement (ou délibérément) au rôle un accès plus large que nécessaire, il ne pourra toujours pas accéder à ce que la politique du point de terminaison n'autorise pas. Nous disposons donc désormais de trois couches de politiques, qui doivent toutes autoriser l'accès à la ressource :

  • La politique de permissions IAM qui autorise le rôle à accéder à la ressource.
  • La politique du point de terminaison de service qui autorise l'accès aux ressources lorsque les requêtes transitent par le point de terminaison, quelles que soient les permissions du rôle utilisé.
  • La politique de bucket ou de ressource (selon le type de ressource), qui peut restreindre l'accès aux seules adresses IP approuvées.

À moins d'exécuter votre fonction Lambda dans un VPC, cette option ne permettra là encore pas de protéger une fonction compromise.

Ajouter des proxys de métadonnées avec filtres sur l'en-tête HTTP User Agent (protection du service de métadonnées)

  • Complexité : élevée
  • Efficacité : modérée
  • Évolutivité : difficile
  • Type : prévention

Tous ces contrôles partent du principe qu'un attaquant peut dérober les identifiants du rôle, mais s'il existait un moyen de réduire sa capacité à les obtenir même après avoir compromis l'instance ou le conteneur autorisé ? (Cette technique ne fonctionne pas pour les fonctions Lambda.) Une option émergente consiste à restreindre d'emblée l'accès au service de métadonnées. Certaines tentatives ont été menées avec IPTables, mais elles risquent aussi de casser des fonctionnalités nécessaires au code exécuté sur l'instance. En novembre 2018, AWS et Netflix ont collaboré et ont commencé à ajouter des données utilisateur aux en-têtes HTTP pour les appels d'API émis depuis les SDK AWS. Il s'agit d'une défense contre les attaques SSRF, car la plupart d'entre elles consistent à tromper une application pour qu'elle effectue des requêtes HTTP au nom de l'attaquant ; or ces requêtes proviennent généralement d'un outil en ligne de commande comme curl ou d'un autre processus et ne comportent pas l'en-tête de données utilisateur ajouté par les SDK AWS. Pour que cela fonctionne, vous devez insérer un proxy pour ces requêtes. Il existe des options open source pour les instances et les conteneurs, y compris des proxys qui s'exécutent sur l'instance sans vous obliger à router le trafic vers un boîtier virtuel ou un proxy squid.

Cette technique ne fonctionnera pas si l'attaquant compromet l'instance hôte et ouvre un shell, puisqu'il peut désactiver le Roxy ou détourner le processus approuvé.

Vous trouverez tous les détails dans cet article de Netflix.

Protection contre l'usage dupliqué des rôles

  • Complexité : élevée
  • Efficacité : élevée
  • Évolutivité : élevée
  • Type : détection

Voici une autre technique issue de l'équipe de Netflix. Elle a publié un guide pratique présentant une excellente technique pour détecter l'utilisation d'un rôle IAM depuis un emplacement non autorisé, y compris au sein d'AWS. Je vous recommande vivement de lire l'article lié, mais en résumé, l'équipe combine les journaux CloudTrail avec d'autres outils pour tenir une table indiquant quelles instances utilisent quels rôles depuis quelles adresses IP. Elle surveille ensuite les autres appels d'API pour repérer les cas où un rôle est réutilisé depuis une nouvelle adresse IP alors qu'il est déjà utilisé depuis une adresse approuvée. Avec cette méthode, vous n'avez pas besoin de connaître toutes les adresses IP utilisées dans l'organisation : vous construisez dynamiquement une table de ce qui est en usage et détectez l'utilisation simultanée d'un rôle ailleurs. Cette approche est extrêmement évolutive, car vous pouvez exécuter la logique de façon centralisée si vous centralisez CloudTrail, ce qui constitue de toute façon une bonne pratique courante.

Synthèse

Voici encore un article très dense, et nous n'attendons pas de chacun qu'il mette en œuvre toutes ces options dans tous les déploiements. Pour simplifier, reprenons la chaîne d'attaque d'abus de rôles IAM :

  • Découvrir et exploiter une vulnérabilité dans une instance, un conteneur ou une fonction Lambda lui permettant d'accéder aux identifiants du rôle. Il s'agit presque toujours d'une erreur côté client… absence de correctifs, ouverture de ports inappropriés ou déploiement de code vulnérable.
    • La gestion des vulnérabilités (y compris des outils comme SASST et DAST pour les applications) et l'évaluation de votre configuration cloud (avec des outils comme DisruptOps ou des outils open source comme Prowler et CloudMapper) constituent votre première défense.
  • Extraire les identifiants du rôle en cours.
    • Protection du service de métadonnées et gestion des vulnérabilités
  • Exécuter avec succès les appels d'API autorisés dans un environnement qu'il contrôle.
    • Détection de l'usage dupliqué des rôles, utilisation de restrictions conditionnelles d'origine de requête (IP, VPC ou autres) dans les politiques de permissions, utilisation de points de terminaison de service avec politiques + politiques de ressources
  • Faire quelque chose de malveillant dans le périmètre de la politique de permissions du rôle IAM autorisé. Enfin, probablement malveillant : rares sont les attaquants qui corrigent votre code à votre place.
    • Politiques de permissions IAM du moindre privilège avec restrictions de ressources

Nous espérons que cela vous donne une meilleure idée de la façon de réduire le taux de réussite de ce type d'attaques.

Stopper les chaînes d'attaque dans AWS IAM | FireMon