Cognito User Pools

Reading time: 17 minutes

tip

Aprenda e pratique Hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Aprenda e pratique Hacking GCP: HackTricks Training GCP Red Team Expert (GRTE) Aprenda e pratique Hacking Azure: HackTricks Training Azure Red Team Expert (AzRTE)

Support HackTricks

Informações Básicas

Um pool de usuários é um diretório de usuários no Amazon Cognito. Com um pool de usuários, seus usuários podem fazer login em seu aplicativo web ou móvel através do Amazon Cognito, ou federar através de um provedor de identidade de terceiros (IdP). Se seus usuários fizerem login diretamente ou através de um terceiro, todos os membros do pool de usuários têm um perfil de diretório que você pode acessar através de um SDK.

Os pools de usuários fornecem:

  • Serviços de registro e login.
  • Uma interface web personalizável integrada para fazer login de usuários.
  • Login social com Facebook, Google, Login com Amazon e Sign in with Apple, e através de provedores de identidade SAML e OIDC do seu pool de usuários.
  • Gerenciamento de diretório de usuários e perfis de usuários.
  • Recursos de segurança, como autenticação multifatorial (MFA), verificações de credenciais comprometidas, proteção contra tomada de conta e verificação de telefone e e-mail.
  • Fluxos de trabalho personalizados e migração de usuários através de gatilhos AWS Lambda.

O código-fonte das aplicações geralmente também conterá o ID do pool de usuários e o ID da aplicação cliente, (e às vezes o segredo da aplicação?) que são necessários para um usuário fazer login em um Pool de Usuários Cognito.

Ataques Potenciais

  • Registro: Por padrão, um usuário pode se registrar, então ele poderia criar um usuário para si mesmo.
  • Enumeração de usuários: A funcionalidade de registro pode ser usada para encontrar nomes de usuários que já existem. Essa informação pode ser útil para um ataque de força bruta.
  • Força bruta de login: Na seção Autenticação você tem todos os métodos que um usuário tem para fazer login, você poderia tentar forçar a entrada para encontrar credenciais válidas.

Ferramentas para pentesting

  • Pacu, agora inclui os módulos cognito__enum e cognito__attack que automatizam a enumeração de todos os ativos Cognito em uma conta e sinalizam configurações fracas, atributos de usuário usados para controle de acesso, etc., e também automatizam a criação de usuários (incluindo suporte a MFA) e escalonamento de privilégios com base em atributos personalizados modificáveis, credenciais de pool de identidade utilizáveis, funções assumíveis em tokens de id, etc.
    Para uma descrição das funções dos módulos, veja a parte 2 do post do blog. Para instruções de instalação, veja a página principal do Pacu.
bash
# 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 é uma ferramenta CLI em python que implementa diferentes ataques ao Cognito, incluindo criação indesejada de contas e oracle de contas. Confira this link para mais informações.
bash
# Install
pip install cognito-scanner
# Run
cognito-scanner --help
bash
python cognito-attribute-enu.py -client_id 16f1g98bfuj9i0g3f8be36kkrl

Registro

User Pools permite por padrão registrar novos usuários.

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

Se alguém pode se registrar

Você pode encontrar um erro indicando que você precisa fornecer mais detalhes sobre o usuário:

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

Você pode fornecer os detalhes necessários com um JSON como:

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

Você também pode usar essa funcionalidade para enumerar usuários existentes. Esta é a mensagem de erro quando um usuário já existe com esse nome:

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

note

Note no comando anterior como os atributos personalizados começam com "custom:".
Também saiba que ao registrar você não pode criar novos atributos personalizados para o usuário. Você só pode atribuir valor a atributos padrão (mesmo que não sejam obrigatórios) e atributos personalizados especificados.

Ou apenas para testar se um client id existe. Este é o erro se o client-id não existir:

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

Se apenas o administrador pode registrar usuários

Você encontrará esse erro e não poderá registrar ou enumerar usuários:

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

Verificando Registro

Cognito permite verificar um novo usuário verificando seu e-mail ou número de telefone. Portanto, ao criar um usuário, geralmente será necessário pelo menos o nome de usuário e a senha e o e-mail e/ou número de telefone. Basta definir um que você controla para que você receba o código para verificar sua nova conta de usuário assim:

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

warning

Mesmo que pareça que você pode usar o mesmo e-mail e número de telefone, quando você precisar verificar o usuário criado, o Cognito reclamará sobre o uso das mesmas informações e não permitirá que você verifique a conta.

Escalação de Privilégios / Atualizando Atributos

Por padrão, um usuário pode modificar o valor de seus atributos com algo como:

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

Privesc de atributo personalizado

caution

Você pode encontrar atributos personalizados sendo usados (como isAdmin), pois por padrão você pode mudar os valores dos seus próprios atributos, você pode ser capaz de escalar privilégios mudando o valor você mesmo!

Privesc de modificação de email/nome de usuário

Você pode usar isso para modificar o email e o número de telefone de um usuário, mas então, mesmo que a conta permaneça verificada, esses atributos estão definidos como não verificados (você precisa verificá-los novamente).

warning

Você não poderá fazer login com email ou número de telefone até que os verifique, mas você poderá fazer login com o nome de usuário.
Note que mesmo que o email tenha sido modificado e não verificado, ele aparecerá no ID Token dentro do campo email e o campo email_verified será falso, mas se o app não estiver verificando isso, você pode se passar por outros usuários.

Além disso, note que você pode colocar qualquer coisa dentro do campo name apenas modificando o atributo name. Se um app estiver verificando aquele campo por algum motivo em vez do email (ou qualquer outro atributo), você pode ser capaz de se passar por outros usuários.

De qualquer forma, se por algum motivo você mudou seu email, por exemplo, para um novo que você pode acessar, você pode confirmar o email com o código que recebeu naquele endereço de email:

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

Use phone_number em vez de email para alterar/verificar um novo número de telefone.

note

O administrador também pode habilitar a opção de login com um nome de usuário preferido pelo usuário. Note que você não poderá alterar este valor para qualquer nome de usuário ou preferred_username já em uso para se passar por um usuário diferente.

Recuperar/Alterar Senha

É possível recuperar uma senha apenas sabendo o nome de usuário (ou email ou telefone é aceito) e tendo acesso a ele, pois um código será enviado lá:

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

note

A resposta do servidor sempre será positiva, como se o nome de usuário existisse. Você não pode usar este método para enumerar usuários

Com o código, você pode alterar a senha com:

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

Para mudar a senha, você precisa saber a senha anterior:

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

Autenticação

Um pool de usuários suporta diferentes maneiras de autenticar. Se você tiver um nome de usuário e senha, também há diferentes métodos suportados para login.
Além disso, quando um usuário é autenticado no Pool, 3 tipos de tokens são fornecidos: O ID Token, o Access token e o Refresh token.

  • ID Token: Ele contém declarações sobre a identidade do usuário autenticado, como name, email e phone_number. O ID token também pode ser usado para autenticar usuários em seus servidores de recursos ou aplicações de servidor. Você deve verificar a assinatura do ID token antes de confiar em qualquer declaração dentro do ID token se você usá-lo em aplicações externas.
  • O ID Token é o token que contém os valores dos atributos do usuário, mesmo os personalizados.
  • Access Token: Ele contém declarações sobre o usuário autenticado, uma lista dos grupos do usuário e uma lista de escopos. O propósito do access token é autorizar operações de API no contexto do usuário no pool de usuários. Por exemplo, você pode usar o access token para conceder acesso ao seu usuário para adicionar, alterar ou excluir atributos de usuário.
  • Refresh Token: Com os refresh tokens, você pode obter novos ID Tokens e Access Tokens para o usuário até que o refresh token se torne inválido. Por padrão, o refresh token expira 30 dias após o usuário da sua aplicação fazer login no seu pool de usuários. Quando você cria uma aplicação para seu pool de usuários, pode definir a expiração do refresh token da aplicação para qualquer valor entre 60 minutos e 10 anos.

ADMIN_NO_SRP_AUTH & ADMIN_USER_PASSWORD_AUTH

Este é o fluxo de autenticação do lado do servidor:

  • O aplicativo do lado do servidor chama a AdminInitiateAuth API operation (em vez de InitiateAuth). Esta operação requer credenciais da AWS com permissões que incluem cognito-idp:AdminInitiateAuth e cognito-idp:AdminRespondToAuthChallenge. A operação retorna os parâmetros de autenticação necessários.
  • Depois que o aplicativo do lado do servidor tem os parâmetros de autenticação, ele chama a AdminRespondToAuthChallenge API operation. A operação AdminRespondToAuthChallenge só tem sucesso quando você fornece credenciais da AWS.

Este método NÃO está habilitado por padrão.

Para fazer login, você precisa saber:

  • id do pool de usuários
  • id do cliente
  • nome de usuário
  • senha
  • segredo do cliente (somente se a aplicação estiver configurada para usar um segredo)

note

Para poder fazer login com este método, essa aplicação deve permitir login com ALLOW_ADMIN_USER_PASSWORD_AUTH.
Além disso, para realizar essa ação, você precisa de credenciais com as permissões cognito-idp:AdminInitiateAuth e cognito-idp:AdminRespondToAuthChallenge

python
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
Código para Login
python
import boto3
import botocore
import hmac
import hashlib
import base64


client_id = "<client-id>"
user_pool_id = "<user-pool-id>"
client_secret = "<client-secret>"
username = "<username>"
password = "<pwd>"

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))

USER_PASSWORD_AUTH

Este método é outro fluxo simples e tradicional de autenticação de usuário e senha. É recomendado migrar um método de autenticação tradicional para o Cognito e recomendado então desativá-lo e usar o método ALLOW_USER_SRP_AUTH em vez disso (já que esse nunca envia a senha pela rede).
Este método NÃO está habilitado por padrão.

A principal diferença em relação ao método de autenticação anterior dentro do código é que você não precisa saber o ID do pool de usuários e que você não precisa de permissões extras no Cognito User Pool.

Para fazer login você precisa saber:

  • client id
  • username
  • password
  • client secret (apenas se o aplicativo estiver configurado para usar um segredo)

note

Para poder fazer login com este método, esse aplicativo deve permitir login com ALLOW_USER_PASSWORD_AUTH.

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
Código Python para Login
python
import boto3
import botocore
import hmac
import hashlib
import base64


client_id = "<client-id>"
user_pool_id = "<user-pool-id>"
client_secret = "<client-secret>"
username = "<username>"
password = "<pwd>"

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.initiate_auth(
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))

USER_SRP_AUTH

Este cenário é semelhante ao anterior, mas em vez de enviar a senha pela rede para fazer login, uma autenticação de desafio é realizada (portanto, nenhuma senha é transmitida, mesmo que criptografada, pela rede).
Este método está habilitado por padrão.

Para fazer login, você precisa saber:

  • id do pool de usuários
  • id do cliente
  • nome de usuário
  • senha
  • segredo do cliente (apenas se o aplicativo estiver configurado para usar um segredo)
Código para login
python
from warrant.aws_srp import AWSSRP
import os

USERNAME='xxx'
PASSWORD='yyy'
POOL_ID='us-east-1_zzzzz'
CLIENT_ID = '12xxxxxxxxxxxxxxxxxxxxxxx'
CLIENT_SECRET = 'secreeeeet'
os.environ["AWS_DEFAULT_REGION"] = "<region>"

aws = AWSSRP(username=USERNAME, password=PASSWORD, pool_id=POOL_ID,
client_id=CLIENT_ID, client_secret=CLIENT_SECRET)
tokens = aws.authenticate_user()
id_token = tokens['AuthenticationResult']['IdToken']
refresh_token = tokens['AuthenticationResult']['RefreshToken']
access_token = tokens['AuthenticationResult']['AccessToken']
token_type = tokens['AuthenticationResult']['TokenType']

REFRESH_TOKEN_AUTH & REFRESH_TOKEN

Este método sempre será válido (não pode ser desativado), mas você precisa ter um token de atualização válido.

bash
aws cognito-idp initiate-auth \
--client-id 3ig6h5gjm56p1ljls1prq2miut \
--auth-flow REFRESH_TOKEN_AUTH \
--region us-east-1 \
--auth-parameters 'REFRESH_TOKEN=<token>'
Código para atualizar
python
import boto3
import botocore
import hmac
import hashlib
import base64

client_id = "<client-id>"
token = '<token>'

boto_client = boto3.client('cognito-idp', region_name='<region>')

def refresh(client_id, refresh_token):
try:
return boto_client.initiate_auth(
ClientId=client_id,
AuthFlow='REFRESH_TOKEN_AUTH',
AuthParameters={
'REFRESH_TOKEN': refresh_token
}
)
except botocore.exceptions.ClientError as e:
return e.response


print(refresh(client_id, token))

CUSTOM_AUTH

Neste caso, a autenticação será realizada através da execução de uma função lambda.

Segurança Extra

Segurança Avançada

Por padrão, está desativado, mas se ativado, o Cognito pode ser capaz de encontrar tomadas de conta. Para minimizar a probabilidade, você deve fazer login de uma rede dentro da mesma cidade, usando o mesmo agente de usuário (e IP, se possível).

MFA Lembrar dispositivo

Se o usuário fizer login do mesmo dispositivo, o MFA pode ser contornado, portanto, tente fazer login do mesmo navegador com os mesmos metadados (IP?) para tentar contornar a proteção MFA.

Grupos de User Pool Funções IAM

É possível adicionar usuários a grupos de User Pool que estão relacionados a uma função IAM.
Além disso, usuários podem ser atribuídos a mais de 1 grupo com diferentes funções IAM anexadas.

Observe que, mesmo que um grupo esteja dentro de um grupo com uma função IAM anexada, para poder acessar as credenciais IAM desse grupo, é necessário que o User Pool seja confiável por um Identity Pool (e conhecer os detalhes desse Identity Pool).

Outro requisito para obter a função IAM indicada no IdToken quando um usuário é autenticado no User Pool (aws cognito-idp initiate-auth...) é que o provedor de autenticação do Identity Provider precisa indicar que a função deve ser selecionada do token.

As funções às quais um usuário tem acesso estão dentro do IdToken, e um usuário pode selecionar qual função gostaria de obter credenciais com o --custom-role-arn do aws cognito-identity get-credentials-for-identity.
No entanto, se a opção padrão for a configurada (usar função padrão), e você tentar acessar uma função do IdToken, você receberá erro (é por isso que a configuração anterior é necessária):

An error occurred (InvalidParameterException) when calling the GetCredentialsForIdentity operation: Only SAML providers and providers with RoleMappings support custom role ARN.

warning

Observe que o papel atribuído a um Grupo de User Pool precisa ser acessível pelo Provedor de Identidade que confia no User Pool (já que as credenciais de sessão do papel IAM serão obtidas a partir dele).

json
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Federated": "cognito-identity.amazonaws.com"
},
"Action": "sts:AssumeRoleWithWebIdentity",
"Condition": {
"StringEquals": {
"cognito-identity.amazonaws.com:aud": "us-east-1:2361092e-9db6-a876-1027-10387c9de439"
},
"ForAnyValue:StringLike": {
"cognito-identity.amazonaws.com:amr": "authenticated"
}
}
}
]
}js

tip

Aprenda e pratique Hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Aprenda e pratique Hacking GCP: HackTricks Training GCP Red Team Expert (GRTE) Aprenda e pratique Hacking Azure: HackTricks Training Azure Red Team Expert (AzRTE)

Support HackTricks