Cognito User Pools

Support HackTricks

Basic Information

Un pool d'utilisateurs est un annuaire d'utilisateurs dans Amazon Cognito. Avec un pool d'utilisateurs, vos utilisateurs peuvent se connecter à votre application web ou mobile via Amazon Cognito, ou se fédérer via un fournisseur d'identité tiers (IdP). Que vos utilisateurs se connectent directement ou via un tiers, tous les membres du pool d'utilisateurs ont un profil d'annuaire auquel vous pouvez accéder via un SDK.

Les pools d'utilisateurs fournissent :

  • Des services d'inscription et de connexion.

  • Une interface utilisateur web intégrée et personnalisable pour connecter les utilisateurs.

  • Connexion sociale avec Facebook, Google, Login with Amazon, et Sign in with Apple, ainsi que via des fournisseurs d'identité SAML et OIDC de votre pool d'utilisateurs.

  • Gestion de l'annuaire des utilisateurs et des profils d'utilisateurs.

  • Fonctionnalités de sécurité telles que l'authentification multi-facteurs (MFA), des vérifications pour des identifiants compromis, la protection contre la prise de contrôle de compte, et la vérification par téléphone et par email.

  • Flux de travail personnalisés et migration des utilisateurs via des déclencheurs AWS Lambda.

Le code source des applications contiendra généralement également l'ID du pool d'utilisateurs et l'ID de l'application cliente, (et parfois le secret de l'application ?) qui sont nécessaires pour qu'un utilisateur se connecte à un pool d'utilisateurs Cognito.

Potential attacks

  • Inscription : Par défaut, un utilisateur peut s'inscrire lui-même, il pourrait donc créer un utilisateur pour lui-même.

  • Énumération des utilisateurs : La fonctionnalité d'inscription peut être utilisée pour trouver des noms d'utilisateur qui existent déjà. Cette information peut être utile pour l'attaque par force brute.

  • Force brute de connexion : Dans la section Authentication, vous avez tous les méthodes qu'un utilisateur doit utiliser pour se connecter, vous pourriez essayer de les forcer pour trouver des identifiants valides.

Tools for pentesting

  • Pacu, inclut maintenant les modules cognito__enum et cognito__attack qui automatisent l'énumération de tous les actifs Cognito dans un compte et signalent les configurations faibles, les attributs d'utilisateur utilisés pour le contrôle d'accès, etc., et automatisent également la création d'utilisateurs (y compris le support MFA) et l'escalade de privilèges basée sur des attributs personnalisables modifiables, des identifiants de pool d'identité utilisables, des rôles assumables dans des jetons d'identité, etc. Pour une description des fonctions des modules, voir la partie 2 du blog post. Pour des instructions d'installation, voir la page principale de Pacu.

# Run cognito__enum usage to gather all user pools, user pool clients, identity pools, users, etc. visible in the current AWS account
Pacu (new:test) > run cognito__enum

# cognito__attack usage to attempt user creation and all privesc vectors against a given identity pool and user pool client:
Pacu (new:test) > run cognito__attack --username randomuser --email XX+sdfs2@gmail.com --identity_pools
us-east-2:a06XXXXX-c9XX-4aXX-9a33-9ceXXXXXXXXX --user_pool_clients
59f6tuhfXXXXXXXXXXXXXXXXXX@us-east-2_0aXXXXXXX
  • Cognito Scanner est un outil CLI en python qui implémente différentes attaques sur Cognito, y compris la création de comptes non désirés et l'oracle de compte. Consultez ce lien pour plus d'infos.

# Install
pip install cognito-scanner
# Run
cognito-scanner --help
python cognito-attribute-enu.py -client_id 16f1g98bfuj9i0g3f8be36kkrl

Inscription

User Pools permet par défaut de s'inscrire de nouveaux utilisateurs.

aws cognito-idp sign-up --client-id <client-id> \
--username <username> --password <password> \
--region <region> --no-sign-request

Si quelqu'un peut s'inscrire

Vous pourriez trouver une erreur indiquant que vous devez fournir plus de détails sur l'utilisateur :

An error occurred (InvalidParameterException) when calling the SignUp operation: Attributes did not conform to the schema: address: The attribute is required

Vous pouvez fournir les détails nécessaires avec un JSON tel que :

--user-attributes '[{"Name": "email", "Value": "carlospolop@gmail.com"}, {"Name":"gender", "Value": "M"}, {"Name": "address", "Value": "street"}, {"Name": "custom:custom_name", "Value":"supername&\"*$"}]'

Vous pouvez également utiliser cette fonctionnalité pour énumérer les utilisateurs existants. Voici le message d'erreur lorsqu'un utilisateur existe déjà avec ce nom :

An error occurred (UsernameExistsException) when calling the SignUp operation: User already exists

Notez dans la commande précédente comment les attributs personnalisés commencent par "custom:". Sachez également que lors de l'enregistrement, vous ne pouvez pas créer de nouveaux attributs personnalisés pour l'utilisateur. Vous ne pouvez donner de valeur qu'aux attributs par défaut (même s'ils ne sont pas requis) et aux attributs personnalisés spécifiés.

Ou juste pour tester si un identifiant client existe. Voici l'erreur si l'identifiant client n'existe pas :

An error occurred (ResourceNotFoundException) when calling the SignUp operation: User pool client 3ig612gjm56p1ljls1prq2miut does not exist.

Si seul l'administrateur peut enregistrer des utilisateurs

Vous rencontrerez cette erreur et vous ne pourrez pas enregistrer ou énumérer des utilisateurs :

An error occurred (NotAuthorizedException) when calling the SignUp operation: SignUp is not permitted for this user pool

Vérification de l'inscription

Cognito permet de vérifier un nouvel utilisateur en vérifiant son email ou son numéro de téléphone. Par conséquent, lors de la création d'un utilisateur, il vous sera généralement demandé au moins le nom d'utilisateur et le mot de passe ainsi que l'email et/ou le numéro de téléphone. Il suffit de définir un que vous contrôlez afin de recevoir le code pour vérifier votre compte d'utilisateur nouvellement créé comme ceci :

aws cognito-idp confirm-sign-up --client-id <cliet_id> \
--username aasdasd2 --confirmation-code <conf_code> \
--no-sign-request --region us-east-1

Même si il semble que vous pouvez utiliser le même email et numéro de téléphone, lorsque vous devez vérifier l'utilisateur créé, Cognito se plaindra d'utiliser les mêmes informations et ne vous permettra pas de vérifier le compte.

Escalade de privilèges / Mise à jour des attributs

Par défaut, un utilisateur peut modifier la valeur de ses attributs avec quelque chose comme :

aws cognito-idp update-user-attributes \
--region us-east-1 --no-sign-request \
--user-attributes Name=address,Value=street \
--access-token <access token>

Privesc d'attributs personnalisés

Vous pourriez trouver des attributs personnalisés utilisés (comme isAdmin), car par défaut vous pouvez changer les valeurs de vos propres attributs, vous pourriez être en mesure d'escalader les privilèges en changeant la valeur vous-même !

Privesc de modification d'email/nom d'utilisateur

Vous pouvez utiliser cela pour modifier l'email et le numéro de téléphone d'un utilisateur, mais ensuite, même si le compte reste vérifié, ces attributs sont définis en statut non vérifié (vous devez les vérifier à nouveau).

Vous ne pourrez pas vous connecter avec l'email ou le numéro de téléphone tant que vous ne les avez pas vérifiés, mais vous pourrez vous connecter avec le nom d'utilisateur. Notez que même si l'email a été modifié et non vérifié, il apparaîtra dans le jeton ID à l'intérieur du champ email et le champ email_verified sera faux, mais si l'application ne vérifie pas cela, vous pourriez usurper l'identité d'autres utilisateurs.

De plus, notez que vous pouvez mettre n'importe quoi dans le champ name en modifiant simplement l'attribut name. Si une application vérifie ce champ pour une raison quelconque au lieu de email (ou tout autre attribut), vous pourriez être en mesure d'usurper l'identité d'autres utilisateurs.

Quoi qu'il en soit, si pour une raison quelconque vous avez changé votre email par exemple pour un nouveau auquel vous pouvez accéder, vous pouvez confirmer l'email avec le code que vous avez reçu à cette adresse email :

aws cognito-idp verify-user-attribute \
--access-token <access_token> \
--attribute-name email --code <code> \
--region <region> --no-sign-request

Utilisez phone_number au lieu de email pour changer/vérifier un nouveau numéro de téléphone.

L'administrateur peut également activer l'option de connexion avec un nom d'utilisateur préféré par l'utilisateur. Notez que vous ne pourrez pas changer cette valeur pour un nom d'utilisateur ou un preferred_username déjà utilisé pour usurper un autre utilisateur.

Récupérer/Changer le mot de passe

Il est possible de récupérer un mot de passe en connaissant le nom d'utilisateur (ou l'email ou le téléphone est accepté) et en y ayant accès car un code y sera envoyé :

aws cognito-idp forgot-password \
--client-id <client_id> \
--username <username/email/phone> --region <region>

La réponse du serveur sera toujours positive, comme si le nom d'utilisateur existait. Vous ne pouvez pas utiliser cette méthode pour énumérer les utilisateurs.

Avec le code, vous pouvez changer le mot de passe avec :

aws cognito-idp confirm-forgot-password \
--client-id <client_id> \
--username <username> \
--confirmation-code <conf_code> \
--password <pwd> --region <region>

Pour changer le mot de passe, vous devez connaître le mot de passe précédent :

aws cognito-idp change-password \
--previous-password <value> \
--proposed-password <value> \
--access-token <value>

Authentification

Un pool d'utilisateurs prend en charge différentes manières de s'authentifier. Si vous avez un nom d'utilisateur et un mot de passe, il existe également différentes méthodes prises en charge pour se connecter. De plus, lorsqu'un utilisateur est authentifié dans le Pool, 3 types de jetons sont donnés : Le jeton ID, le jeton d'accès et le jeton d'actualisation.

  • Jeton ID : Il contient des revendications sur l'identité de l'utilisateur authentifié, telles que name, email et phone_number. Le jeton ID peut également être utilisé pour authentifier les utilisateurs auprès de vos serveurs de ressources ou applications serveur. Vous devez vérifier la signature du jeton ID avant de pouvoir faire confiance à des revendications à l'intérieur du jeton ID si vous l'utilisez dans des applications externes.

  • Le jeton ID est le jeton qui contient les valeurs des attributs de l'utilisateur, même les personnalisés.

  • Jeton d'accès : Il contient des revendications sur l'utilisateur authentifié, une liste des groupes de l'utilisateur et une liste de portées. Le but du jeton d'accès est de autoriser les opérations API dans le contexte de l'utilisateur dans le pool d'utilisateurs. Par exemple, vous pouvez utiliser le jeton d'accès pour accorder à votre utilisateur l'accès pour ajouter, modifier ou supprimer des attributs d'utilisateur.

  • Jeton d'actualisation : Avec les jetons d'actualisation, vous pouvez obtenir de nouveaux jetons ID et jetons d'accès pour l'utilisateur jusqu'à ce que le jeton d'actualisation soit invalide. Par défaut, le jeton d'actualisation expire 30 jours après que l'utilisateur de votre application se connecte à votre pool d'utilisateurs. Lorsque vous créez une application pour votre pool d'utilisateurs, vous pouvez définir l'expiration du jeton d'actualisation de l'application à n'importe quelle valeur entre 60 minutes et 10 ans.

ADMIN_NO_SRP_AUTH & ADMIN_USER_PASSWORD_AUTH

Voici le flux d'authentification côté serveur :

  • L'application côté serveur appelle l'opération API AdminInitiateAuth (au lieu de InitiateAuth). Cette opération nécessite des identifiants AWS avec des autorisations qui incluent cognito-idp:AdminInitiateAuth et cognito-idp:AdminRespondToAuthChallenge. L'opération renvoie les paramètres d'authentification requis.

  • Après que l'application côté serveur a les paramètres d'authentification, elle appelle l'opération API AdminRespondToAuthChallenge. L'opération API AdminRespondToAuthChallenge ne réussit que si vous fournissez des identifiants AWS.

Cette méthode n'est PAS activée par défaut.

Pour se connecter, vous devez connaître :

  • l'identifiant du pool d'utilisateurs

  • l'identifiant du client

  • le nom d'utilisateur

  • le mot de passe

  • le secret du client (uniquement si l'application est configurée pour utiliser un secret)

Pour pouvoir se connecter avec cette méthode, cette application doit autoriser la connexion avec ALLOW_ADMIN_USER_PASSWORD_AUTH. De plus, pour effectuer cette action, vous avez besoin d'identifiants avec les autorisations cognito-idp:AdminInitiateAuth et cognito-idp:AdminRespondToAuthChallenge

aws cognito-idp admin-initiate-auth \
--client-id <client-id> \
--auth-flow ADMIN_USER_PASSWORD_AUTH \
--region <region> \
--auth-parameters 'USERNAME=<username>,PASSWORD=<password>,SECRET_HASH=<hash_if_needed>'
--user-pool-id "<pool-id>"

# Check the python code to learn how to generate the hsecret_hash
Code de connexion

```python import boto3 import botocore import hmac import hashlib import base64

client_id = "" user_pool_id = "" client_secret = "" username = "" password = ""

boto_client = boto3.client('cognito-idp', region_name='us-east-1')

def get_secret_hash(username, client_id, client_secret): key = bytes(client_secret, 'utf-8') message = bytes(f'{username}{client_id}', 'utf-8') return base64.b64encode(hmac.new(key, message, digestmod=hashlib.sha256).digest()).decode()

If the Client App isn't configured to use a secret

just delete the line setting the SECRET_HASH

def login_user(username_or_alias, password, client_id, client_secret, user_pool_id): try: return boto_client.admin_initiate_auth( UserPoolId=user_pool_id, ClientId=client_id, AuthFlow='ADMIN_USER_PASSWORD_AUTH', AuthParameters={ 'USERNAME': username_or_alias, 'PASSWORD': password, 'SECRET_HASH': get_secret_hash(username_or_alias, client_id, client_secret) } ) except botocore.exceptions.ClientError as e: return e.response

print(login_user(username, password, client_id, client_secret, user_pool_id))

</details>

### USER\_PASSWORD\_AUTH

Cette méthode est un autre flux simple et **traditionnel d'authentification utilisateur et mot de passe**. Il est recommandé de **migrer une méthode d'authentification traditionnelle** **vers Cognito** et **recommandé** de **la désactiver** puis **d'utiliser** la méthode **ALLOW\_USER\_SRP\_AUTH** à la place (car celle-ci n'envoie jamais le mot de passe sur le réseau).\
Cette **méthode n'est PAS activée** par défaut.

La principale **différence** avec la **méthode d'authentification précédente** dans le code est que vous **n'avez pas besoin de connaître l'ID du pool d'utilisateurs** et que vous **n'avez pas besoin de permissions supplémentaires** dans le pool d'utilisateurs Cognito.

Pour **se connecter**, vous **devez** connaître :

* identifiant client
* nom d'utilisateur
* mot de passe
* secret client (uniquement si l'application est configurée pour utiliser un secret)

<div data-gb-custom-block data-tag="hint" data-style='info'>

Pour **pouvoir se connecter avec cette méthode**, cette application doit permettre de se connecter avec ALLOW\_USER\_PASSWORD\_AUTH.

</div>

```python
aws cognito-idp initiate-auth  --client-id <client-id> \
--auth-flow USER_PASSWORD_AUTH --region <region> \
--auth-parameters 'USERNAME=<username>,PASSWORD=<password>,SECRET_HASH=<hash_if_needed>'

# Check the python code to learn how to generate the secret_hash

REFRESH_TOKEN_AUTH & REFRESH_TOKEN

Cette méthode sera toujours valide (elle ne peut pas être désactivée) mais vous devez avoir un jeton de rafraîchissement valide.

aws cognito-idp initiate-auth \
--client-id 3ig6h5gjm56p1ljls1prq2miut \
--auth-flow REFRESH_TOKEN_AUTH \
--region us-east-1 \
--auth-parameters 'REFRESH_TOKEN=<token>'

Last updated