Contactez-nous
-
cloud public

Comment corriger la vulnérabilité 'Confused Deputy Problem' sur AWS ?

Comment corriger la vulnérabilité 'Confused Deputy Problem' sur AWS ?

Sommaire

Définitions

Connaissez-vous le Confused Deputy Problem ? Il s’agit d’une vulnérabilité dans laquelle une entité qui n’a pas l’autorisation d’effectuer une action peut contraindre une autre entité plus privilégiée à effectuer l’action.

Il s’agit concrètement d’une faille permettant de l’élévation de privilège. Elle est documentée sous l’identifiant CWE-441: Unintended proxy or intermediary par MITRE et a été référencée par plusieurs exemples au fil des années. Cet article se concentre sur son application dans AWS, mais voici quelques autres exemples d’applications possibles :

Comme nous parlons de permissions, dans AWS, le service IAM est obligatoirement impliqué. Il existe une page de documentation AWS dédiée au Confused Deputy Problem et cette page indique le contexte de présence de la faille : quand on autorise une entité tierce à faire des actions sur nos ressources. Faisons une rapide parenthèse pour expliquer à quoi correspond le mécanisme de resource-based policies qui permet cette attribution de permissions.

Presque toutes les permissions dans AWS sont définies par des documents appelés policies. Le but de ces documents est de décrire de façon standardisée les permissions aux différents endroits où ces dernières s’appliqueront. Une policy a donc un contexte attaché au document qu’elle représente, ce contexte apporte des informations complémentaires au document en lui-même.

L’approche la plus facilement envisagée pour définir des permissions consiste à lister les permissions accordées à une identité dans AWS, on parle d’identity-based policy. On devra donc lister les Actions sur les Resources que l’identité est autorisée à manipuler. Comme il est possible d’utiliser différentes identités, les permissions changeront suivant celle utilisée.

Il existe des limites importantes à ce cas d’usage, par exemple : comment assigner des permissions à une entité qui n’est pas directement sous notre contrôle ou qui n’est pas directement dans le contexte que nous pouvons configurer ? De même, est-il possible pour un administrateur de ressources, de protéger une ressource en restreignant même les administrateurs ?

Dans tous ces cas, l’approche proposée par AWS consiste non plus à attribuer ces permissions/restrictions à une identité actrice, mais à attacher ces autorisations sur les ressources qu’on veut permettre à l’acteur de manipuler. Dans ce cas, le document policy introduit en plus le Principal, qui indique l’identité qui pourra faire les actions. Le document est nommé resource-based policy et ce mécanisme est disponible dans de nombreux services.

Les resource-based policies peuvent porter des noms différents suivant le service concerné. On peut noter que ces policies dans le service IAM AWS sont nommées trust policies.

 

Identity-based VS Resource-based policies

Ce sont ces resource-based policies qui peuvent créer des situations sensibles au Confused Deputy Problem car, si la définition du Principal est trop large, elle devient vulnérable.

Dans la documentation du service IAM, AWS identifie ce phénomène pour deux situations distinctes :

  • Cross-account quand une trust policy autorise une identité provenant d’un autre compte AWS (le Principal est l’identifiant d’un autre compte)
{
 "Version": "2012-10-17",
  "Statement": {
    "Effect": "Allow",
    "Principal": {
      "AWS": "Account ID"
    },
    ...
  }
}
  • Cross-service quand une resource-based policy (trust policy ou autre) autorise un service AWS à qui on attribue directement des permissions (le Principal est l’identifiant d’un service AWS)
{
  "Version": "2012-10-17",
  "Statement": {
    "Effect": "Allow",
    "Principal": {
      "Service": "Service name"
    },
    ...
  }
}

Cross-account

La vulnérabilité Confused Deputy s’applique à la situation cross-account quand le Principal d’une trust policy est l’identifiant d’un autre compte AWS pour permettre aux entités de ce second compte d’effectuer des actions dans le compte courant.

La vulnérabilité vient du fait que simplement définir l’autorisation pour l’ensemble du compte distant ouvre une porte à toute entité qui pourra accéder à ce contexte distant. Une autre entité pourrait exploiter un accès qu’elle aurait dans le contexte du second compte pour accéder au compte courant. Toute entité d’un compte autorisé à effectuer des actions cross-account dans le compte courant a accès à ces mêmes permissions.

Un cas d’exemple concerne des comptes partagés ou ayant plusieurs accès de tiers (par exemple, plusieurs clients) dans le cadre d’une offre de service. Si on autorise le compte AWS offrant ce service, alors chaque client peut acquérir les permissions, à travers ce second compte, dans le compte courant.

Cross-account : scénario illustrant le Confused Deputy

{
  "Version": "2012-10-17",
  "Statement": {
    "Effect": "Allow",
    "Principal": {
      "AWS": "Account ID"
    },
    "Action": "sts:AssumeRole"
  }
}

Cross-account : exemple de policy vulnérable

Cross-service

Nous l’avons vu, le cross-service Confused Deputy apparaît lorsque le Principal d’une resource-based policy est un service. Tous les services sont concernés. Dans les exemples suivants, nous allons prendre pour référence le service Lambda, mais on pourrait très bien parler de SNS, SQS ou encore d’API Gateway.

La subtilité réside surtout dans le type de ressource qui sera associé à une resource-based policy. Les risques induits sont bien différents en fonction de la ressource. Ainsi, on peut séparer :

  1. Une trust policy attachée à un rôle qui autorise tout un service AWS. C’est ce service role qu’un attaquant va chercher à assumer en exploitant le service autorisé.
  2. Les autres resource-based policies. Ici, l’attaquant va tenter d’accéder directement à la ressource associée, toujours via le service autorisé.

Voyons quels sont les impacts d’une mauvaise configuration des policies pour ces deux cas de figure. Évaluons également leur criticité en différenciant des accès malveillants au sein d’un même compte puis en cross-account.

Trust policy same account

Dans le cas d’une trust policy, imaginons que notre fonction Alice cherche à récupérer un secret dans AWS Secrets Manager. Il s’agit d’un cas nominal : le service a juste à assumer le rôle associé pour récupérer le secret.

Cependant, si on ne fait pas attention, n’importe quelle fonction du même compte AWS peut aussi assumer le rôle et atteindre le secret. C’est par exemple le cas de Mallory, une autre fonction. Étant donné que l’on a autorisé tout le service Lambda en Principal de la trust policy, cette autre fonction peut aussi assumer ce rôle et accéder au secret.

Mallory peut assumer le service role dans le même compte

On pourrait se demander quel est l’impact réel de cette vulnérabilité. En effet, Mallory est une fonction qui fait partie du même compte AWS qu’Alice. Le risque dépend donc de la confiance qu’on accorde aux identités du compte. S’il y a plusieurs équipes travaillant au sein d’un même compte, celles-ci pourraient faire assumer le service role de leurs voisins. L’impact reste probablement minime puisque nous sommes censés avoir un minimum de contrôle dans notre compte (via des stacks de CI/CD, d’observabilité, etc.).

Mais alors, est-ce que nous pouvons exploiter cette vulnérabilité en cross-account ? C’est ce que nous allons expérimenter. Si c’est le cas, les conséquences sont dramatiques…

Trust policy cross-account

Fort heureusement, il est impossible pour un service d’assumer un rôle en cross-account. Ou plutôt, il n’est pas possible de passer un rôle à un service si ce rôle est dans un compte différent. Le PassRole est une autorisation particulière qui donne le droit à une identité de déléguer un rôle à un service.

Par exemple, si un utilisateur souhaite donner les droits d’un rôle Administrateur à une fonction Lambda, différentes vérifications seront menées :

  • Le service Lambda va contrôler que le rôle Administrateur existe dans le même compte AWS de la fonction
  • Le service IAM va vérifier que l’utilisateur possède bien le droit PassRole pour le rôle Administrateur

Si les vérifications sont en échec, l’utilisateur est même incapable de créer la fonction.

Ouf ! On ne peut pas passer le role à Mallory en cross-account

Comme Lambda, chaque autre service AWS doit normalement implémenter un garde-fou pour éviter de passer un rôle en cross-account à un service.

Nous avons bien dit « normalement », parce qu’il existe certaines failles. C’était le cas pour le service AppSync (voir article dédié). En jouant un peu avec le format de l’ARN du rôle en CLI, il était possible de contourner la validation AWS et donc de faire un PassRole cross-account.

La vulnérabilité a été corrigée depuis, mais on reste tributaire de l’implémentation chez AWS, qui semble différer en fonction des services.

Resource-based policy

Voyons maintenant le cas des autres resource-based policies. C’est une autre paire de manches !

Ici, Alice souhaite accéder à son secret depuis une API Gateway et une fonction Lambda. La fonction possède une resource-based policy qui autorise le service API Gateway à l’invoquer.

C’est bien tout le service API Gateway qui est autorisé ! Si on ne fait pas attention, n’importe quelle API Gateway de n’importe quel compte AWS pourra accéder à la lambda.
Dans notre exemple, Mallory peut, depuis son compte, invoquer la fonction et récupérer les secrets tant convoités.

Mallory peut atteindre la ressource en cross-account

{
  "Version": "2012-10-17",
  "Statement": {
    "Sid": "AllowExecutionFromAPIGateway",
    "Effect": "Allow",
    "Principal": {
      "Service": "apigateway.amazonaws.com"
    },
    "Action": "lambda:InvokeFunction",
    "Resource": "Lambda function ARN"
 }
}

Cross-service : exemple de policy vulnérable

Dans ce cas, il n’y a pas d’histoire d’AssumeRole ni de PassRole : sans ces garde-fous, on peut accéder à une ressource directement en cross-account.

Remédiation

Avec ces différents cas de figure, le Confused Deputy Problem peut paraître compliqué à appréhender. Heureusement, la mitigation est relativement aisée.

Que ce soit pour le cross-account ou le cross-service, la méthode de correction est la même : il suffit de rajouter une condition dans la policy IAM.

Pour le cross-account, il faut vérifier ce qu’on appelle un ExternalID. Cet ID sera partagé entre les deux comptes : Alice va ajouter l’ExternalID dans sa trust policy et les entités du compte de Bob ne pourront utiliser le role qu’en fournissant cet ID lors de l’action. Il s’agit donc d’un secret à préserver et à générer de manière aléatoire et unique. Nous recommandons notamment l’usage de Universally Unique Identifiers (UUIDs).

Cross-account : impact de l’ExternalID dans le scénario d’exploitation

{
  "Version": "2012-10-17",
  "Statement": {
    "Effect": "Allow",
    "Principal": {
      "AWS": "Account ID"
    },
    "Action": "sts:AssumeRole",
    "Condition": {
      "StringEquals": {
        "sts:ExternalId": "Unique ID assigned"
      }
    }
  }
}

Cross-account : exemple de policy avec condition sur ExternalID

Concernant le cross-service, c’est encore plus simple ! On ajoute ici une condition sur la SourceArn. Donc, plutôt que d’autoriser tout un service AWS en entier, nous allons spécifier la ressource qui peut nous atteindre.

{
  "Version": "2012-10-17",
  "Statement": {
    "Sid": "AllowExecutionFromAPIGateway",
    "Effect": "Allow",
    "Principal": {
      "Service": "apigateway.amazonaws.com"
    },
    "Action": "lambda:InvokeFunction",
    "Resource": "Lambda function ARN",
    "Condition": {
      "ArnLike": {
        "aws:SourceArn": "API Gateway method ARN"
     }
   }
 }
}

Cross-service : exemple de policy avec condition sur le SourceArn

À noter que SourceArn n’est pas la seule condition permettant de se protéger du cross-service. C’est la plus restrictive, donc celle qui tend le plus vers le principe du moindre privilège. Cependant, elle peut être difficile à maintenir lorsque l’on souhaite autoriser de nombreuses ressources.

Pour plus d’élasticité, AWS vient de mettre à disposition deux nouvelles clés de condition : SourceOrgID et SourceOrgPaths. Nous vous invitons à lire l’article de blog dédié, qui vous aidera à faire un compromis entre sécurité et maintenabilité.

Pour information, d’autres situations impliquant aussi des policies IAM, notamment les trust policies pointant vers des Principals de type Federated d’Identity Providers, peuvent se résoudre elles aussi avec l’ajout de conditions sur le Principal. C’est le cas par exemple lorsque l’on vérifie les claims d’un token envoyé par GitHub
AWS ne classifie pas ces cas additionnels comme des occurrences du Confused Deputy Problem car ici ce n’est pas AWS qui gère l’authentification et l’autorisation
Le Confused Deputy Problem peut survenir du côté du Identity Provider et le détail de sa remédiation dépend de l’implémentation fournie par ce provider.

Conclusion

Nous en savons maintenant un peu plus sur le Confused Deputy Problem dans AWS. Malgré sa remédiation relativement simple, cette vulnérabilité possède plusieurs déclinaisons pas forcément évidentes à distinguer.

Existe-t-il des outils permettant de détecter et de remédier au Confused Deputy Problem de manière automatique ?

C’est ce que nous aborderons dans le prochain article, alors restez à l’écoute en vous abonnant à notre newsletter ou en nous suivant sur les divers réseaux sociaux !