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

Published:

Techniques avancées pour protéger l'AWS ExternalID et l'accès inter-comptes AssumeRole

by FireMon

Le mois dernier, Kesten Broughton, de Praetorian Security, a publié d'excellents travaux de recherche sur les produits de sécurité cloud tiers utilisant la technique de connexion inter-comptes recommandée par Amazon – AWS IAM Assume Role Vulnerabilities Found in Many Top Vendors. Le paragraphe d'introduction offre un solide aperçu de ces travaux :

Dans ce premier article de notre série sur la confiance inter-comptes, nous présenterons les résultats portant sur 90 fournisseurs, montrant que 37 % n'avaient pas implémenté correctement l'ExternalId pour se protéger des attaques de type « confused deputy ». Par ailleurs, 15 % des fournisseurs avaient correctement implémenté l'intégration du compte AWS dans l'interface utilisateur, mais le paramètre ExternalId n'était pas validé correctement côté backend, rendant ces sites également vulnérables. Nous terminerons par une discussion sur les nouvelles surfaces d'attaque exposées par la relation de confiance AWS cross-account-assume-role. Nous concluons que les fournisseurs et les clients devraient examiner de manière critique si la confiance basée sur les rôles constitue le meilleur mécanisme de confiance pour leur solution SaaS multi-tenant.

Ma première réaction à la lecture de ces travaux a été : « pourquoi prendre une aussi mauvaise décision ? ». Mais la réalité, c'est que la notion même d'« expert en sécurité cloud » est relativement récente et que, si AWS traite correctement du problème du confused deputy, l'éditeur n'aborde pas certaines implications pratiques auxquelles on ne pense pas vraiment avant que le produit n'entre en contact avec le client. Les connexions inter-comptes reposant sur AssumeRole ont une solution technique simple, mais sans modélisation des menaces appropriée, il est extrêmement facile de commettre les erreurs documentées par Kesten.

Chez DisruptOps, nous avons pris très tôt de bonnes décisions fondées sur notre modélisation initiale des menaces, ce qui nous a protégés. Nous avons toutefois relevé quelques enseignements dans ces travaux et renforçons encore davantage nos protections. Nous menons en outre un projet confidentiel destiné à éliminer une grande partie du problème tout en permettant l'automatisation à grande échelle sans exiger d'accès en écriture direct dans les environnements clients.

J'ai cependant un désaccord majeur avec cet article. Je préférerai toujours des identifiants à rotation automatique à la recommandation d'utiliser des identifiants statiques et un coffre-fort. Nous devons encore y recourir pour d'autres fournisseurs cloud et nous cherchons en permanence des moyens de ne conserver AUCUN identifiant statique où que ce soit dans notre environnement.

Le problème

Toute une gamme de types d'applications doit aujourd'hui se connecter directement aux API cloud. Cela peut être aussi simple que l'accès à un bucket S3 ou aussi complexe qu'une plateforme de détection et de réponse cloud entièrement automatisée (à titre d'exemple pris au hasard, bien entendu). Ces requêtes nécessitent des identifiants, qui sont soit statiques (comme un nom d'utilisateur et un mot de passe, ou une clé d'accès et une clé secrète IAM), soit dynamiques. Les identifiants dynamiques comprennent un jeton ou un autre attribut éphémère limité dans le temps.

Les identifiants applicatifs statiques permettant un accès direct à votre plan de gestion cloud sont… néfastes. Nous pouvons résoudre ce problème pour les utilisateurs avec le MFA, mais cela ne nous aide pas pour les applications automatisées comme notre plateforme de détection et de réponse cloud, pas si théorique que cela. Il y a quelque temps, Amazon Web Services a traité ce problème avec le concept de rôle IAM. Dans AWS, un rôle est essentiellement un conteneur de permissions doté de deux politiques : ce que le conteneur peut faire, et qui (ou quoi) peut assumer le rôle. Les rôles reposent ensuite sur des sessions : lorsqu'une entité autorisée assume le rôle, elle dispose d'un ensemble d'identifiants (clé d'accès, clé secrète et jeton de session) utilisables pendant la durée de la session (de 1 à 24 heures).

Dans AWS, les rôles peuvent être assumés via une connexion SAML externe (pour les utilisateurs), des connexions internes « de confiance » (depuis d'autres comptes AWS) ou des services AWS (comme une instance EC2 ou une fonction Lambda). Vous pouvez ainsi faire des choses intéressantes, comme exécuter du code dans une instance sans que celle-ci ne stocke jamais d'identifiants statiques. Cela élimine réellement bien des tracas.

Autoriser des connexions depuis un compte que vous ne contrôlez pas est un peu différent, surtout si ce compte est une plateforme desservant plusieurs clients. Ce type de plateforme (soit, la nôtre) a besoin d'accéder à des centaines, voire des milliers d'autres comptes AWS. Imaginez qu'un attaquant puisse tromper la plateforme pour lui faire exécuter une action dans le mauvais compte, par exemple une évaluation de configuration sur un compte qui n'appartient pas à l'utilisateur courant. Il s'agit d'une version condensée du problème du confused deputy : le délégué bénéficie de la confiance de plusieurs comptes et l'utilisateur le trompe pour obtenir l'accès à un compte auquel il ne devrait jamais toucher.

L'exploitation la plus concrète consiste, lorsque la plateforme le permet, à saisir l'identifiant d'un compte que l'on ne contrôle pas, à l'ajouter à son profil, puis à abuser de la confiance accordée à la plateforme.

AWS intègre un mécanisme de défense contre cela, appelé AWS ExternalID. Il s'agit d'un secret partagé arbitraire que le fournisseur de la plateforme et le client échangent hors bande. Cet identifiant est un attribut transmis lors de toute requête visant à assumer le rôle dans le compte client, et la politique de confiance du rôle dans ce compte comporte une condition qui vérifie que le secret partagé est correct. Correctement implémenté, cela signifie qu'il est impossible d'ajouter au délégué un compte que l'on ne contrôle pas, puisque l'attaquant ne peut ni établir ni connaître le secret partagé dans le compte client.

Sauf que…

Vous voyez où cela mène. Praetorian a identifié un grand nombre de fournisseurs utilisant des AWS ExternalId par défaut, ou permettant aux clients de définir une valeur d'ExternalId non unique pour l'ensemble de leur compte. Les chercheurs ont également trouvé des produits qui ne vérifiaient même pas que le client impose bien l'External ID dans sa politique de confiance du rôle. Praetorian a découvert des plateformes activement exploitables, permettant des attaques de type confused deputy en raison d'une sécurité insuffisante des rôles inter-comptes.

Renforcer les connexions inter-comptes

Tout cela faisait partie de notre modélisation des menaces chez DisruptOps, nous sommes donc en bonne posture, même si nous avons retenu une ou deux idées supplémentaires pour améliorer les choses. Passons en revue chaque problème identifié par Praetorian et examinons les meilleures options de renforcement de la sécurité :

  • L'AWS ExternalID utilise les paramètres par défaut : Nous utilisons un ExternalID aléatoire.
  • L'AWS ExternalID est partagé entre plusieurs comptes clients :Nous utilisons un ExternalID aléatoire par compte, et non par client.
  • L'AWS ExternalID est énumérable ou devinable : Nous utilisons des AWS ExternalID entièrement aléatoires et longs. Le choix de notre PRNG ne m'inquiète pas, grâce à la limitation du débit des API (pour les passionnés de cryptographie).
  • Les clients peuvent définir leur propre ExternalID et risquent de le réutiliser ou d'employer des ExternalID faibles : Nous ne permettons pas aux clients de définir leur propre ExternalID, bien que la demande nous ait clairement été faite. C'est là, je pense, que certains autres fournisseurs se sont mis en difficulté. Les clients veulent cette possibilité afin d'automatiser eux-mêmes le provisionnement du produit ; mais pour le faire en toute sécurité, il nous faudrait vérifier que l'ExternalID répond aux exigences de longueur et d'aléa. Avec la bonne validation, cela peut probablement être fait de manière sûre.
  • La plateforme permet de provisionner plusieurs fois le même compte AWS : Nous ne l'autorisons pas.
  • Le rôle IAM n'est pas restreint à une seule entité du compte du fournisseur, mais ouvert à tout rôle du compte : Nous n'en avons pas parlé dans cet article, mais vous pouvez soit accorder l'accès depuis n'importe quel rôle du compte au rôle « worker » du compte client, soit accorder l'accès à une seule ressource ou à un seul rôle. Nous limitons notre accès aux rôles spécifiques que nous utilisons pour l'accès inter-comptes.
  • Le nom du rôle IAM est identique dans tous les comptes clients : Le risque tient essentiellement au fait que le nom d'utilisateur (le nom du rôle) est devinable. Je ne considère pas cela comme un risque, sauf en présence d'autres erreurs très fondamentales. Par exemple, si un attaquant connaît le nom du rôle et parvient à accéder au compte client, il pourrait s'ajouter à la politique de confiance du rôle et utiliser ensuite ces privilèges (j'enseigne ce type de scénario dans mes formations à la réponse à incident). Il s'agit d'un risque potentiel, mais que nous évaluons comme assez faible. Si un attaquant dispose d'un tel niveau d'accès, il peut vraisemblablement prendre le contrôle de n'importe quel rôle du compte.
  • Le fournisseur ne vérifie pas que l'ExternalID a bien été défini comme condition d'accès : Nous fournissons aux clients un modèle CloudFormation pour le provisionnement, qui garantit un paramétrage correct. Nous ajouterons la possibilité de vérifier qu'il reste en place, d'autant plus que nous continuons d'ajouter d'autres options de provisionnement (hors CloudFormation) pour répondre aux exigences des clients.

Kesten semble privilégier le modèle des identifiants statiques et le recours à un bon coffre-fort côté fournisseur pour sécuriser la connexion. Personnellement, je ne suis pas d'accord : j'estime que le modèle AWS est plus sûr d'emblée, à condition de respecter les précautions de base.

Nous prenons actuellement deux précautions supplémentaires par rapport aux recommandations de Praetorian :

  • Nous masquons l'ExternalID dans CloudFormation : Nous définissons l'ExternalID comme paramètre dans nos modèles CloudFormation avec l'option NoEcho activée. Cela le masque dans la console, les outils en ligne de commande et l'API. Le risque d'exposition à une personne disposant des permissions d'exécuter CloudFormation sans disposer des permissions IAM s'en trouve réduit.
  • Nous restreignons l'accès au modèle CloudFormation au seul compte cible : Cela réduit le risque qu'une personne intervienne pour récupérer l'ExternalID pendant le processus de provisionnement.

Même si l'ExternalID est exposé, cela ne devrait pas avoir d'importance : votre plateforme doit garantir qu'un compte cible n'est enregistré qu'une seule fois, et uniquement avec un ExternalID aléatoire. Même si un attaquant connaît le nom du rôle et l'ExternalID, il ne peut rien en faire, car aucun emplacement de la plateforme ne permet de saisir ces informations, et AWS impose lui-même que la connexion inter-comptes provienne du compte de confiance. Cela ne peut pas être usurpé.

J'espère que voir comment nous procédons vous donnera des idées à appliquer à vos propres outils. Je recommande les exigences d'enregistrement des comptes et d'ExternalID aléatoire même si vous utilisez AWS Organizations, car ajouter une condition restreignant l'accès à votre seule organisation peut toujours vous exposer à une attaque menée depuis un compte moins sécurisé vers un compte plus sécurisé.

Et restez attentifs à ce nouveau projet confidentiel ! Nous devrions pouvoir en parler d'ici quelques mois et il change véritablement la donne pour ce type de problèmes.

Techniques avancées pour protéger l'AWS ExternalID | FireMon