Cognito User Pools

支持 HackTricks

基本信息

用户池是 Amazon Cognito 中的用户目录。通过用户池,您的用户可以通过 Amazon Cognito 登录到您的网页或移动应用或通过 第三方 身份提供者 (IdP) 进行联合身份验证。无论您的用户是直接登录还是通过第三方,用户池的所有成员都有一个可以通过 SDK 访问的目录配置文件。

用户池提供:

  • 注册和登录服务。

  • 内置的、可自定义的网页 UI 用于用户登录。

  • 通过 Facebook、Google、Amazon 登录和 Apple 登录,以及通过用户池中的 SAML 和 OIDC 身份提供者进行社交登录。

  • 用户目录管理和用户配置文件。

  • 安全功能,如多因素身份验证 (MFA)、对被泄露凭证的检查、账户接管保护,以及电话和电子邮件验证。

  • 通过 AWS Lambda 触发器自定义工作流和用户迁移。

应用程序的 源代码 通常还会包含 用户池 ID客户端应用程序 ID(有时还包括 应用程序密钥?),这些都是 用户登录 Cognito 用户池所需的。

潜在攻击

  • 注册:默认情况下,用户可以自我注册,因此他可以为自己创建一个用户。

  • 用户枚举:注册功能可用于查找已存在的用户名。这些信息对于暴力攻击可能很有用。

  • 登录暴力攻击:在 身份验证 部分,您可以找到用户 登录 的所有 方法,您可以尝试暴力破解它们以 找到有效凭证

渗透测试工具

  • Pacu,现在包括 cognito__enumcognito__attack 模块,自动枚举账户中的所有 Cognito 资产并标记弱配置、用于访问控制的用户属性等,同时还自动创建用户(包括 MFA 支持)和基于可修改自定义属性、可用身份池凭证、可假设角色的 ID 令牌等进行权限提升。 有关模块功能的描述,请参见 博客文章 的第二部分。有关安装说明,请参见主 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 是一个用 Python 编写的 CLI 工具,实施对 Cognito 的不同攻击,包括不必要的账户创建和账户 oracle。有关更多信息,请查看 this link

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

注册

User Pools 默认允许 注册新用户

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

如果任何人都可以注册

您可能会发现一个错误,指示您需要提供更多关于用户的详细信息

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

您可以提供所需的详细信息,格式为 JSON,例如:

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

您还可以使用此功能来枚举现有用户。 当已存在该名称的用户时,错误消息为:

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

注意在之前的命令中,自定义属性以 "custom:" 开头。 还要知道,在注册时,您无法为用户创建新的自定义属性。您只能为默认属性(即使它们不是必需的)和指定的自定义属性赋值。

或者只是测试客户端 ID 是否存在。如果 client-id 不存在,则会出现此错误:

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

如果只有管理员可以注册用户

您将会发现此错误,您将无法注册或枚举用户:

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

验证注册

Cognito 允许通过验证用户的 电子邮件或电话号码验证新用户。因此,在创建用户时,通常至少需要用户名和密码,以及 电子邮件和/或电话号码。只需设置一个 您控制的,这样您就会收到代码来 验证您的 新创建的用户 帐户,如下所示:

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

即使看起来你可以使用相同的电子邮件和电话号码,当你需要验证创建的用户时,Cognito会抱怨使用相同的信息,并且不会让你验证账户

权限提升 / 更新属性

默认情况下,用户可以修改其属性的值,例如:

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

自定义属性权限提升

您可能会发现使用了自定义属性(例如isAdmin),因为默认情况下您可以更改自己属性的值,您可能能够通过自己更改值来提升权限

电子邮件/用户名修改权限提升

您可以使用此功能修改用户的电子邮件和电话号码,但是,即使帐户仍然保持验证状态,这些属性也会被设置为未验证状态(您需要再次验证它们)。

无法使用电子邮件或电话号码登录,直到您验证它们,但您将能够使用用户名登录。 请注意,即使电子邮件已被修改且未验证,它仍会出现在ID令牌中的**email** 字段中,并且字段**email_verified将为false**,但如果应用程序没有检查,您可能会冒充其他用户

此外,请注意,您可以在**name字段中放入任何内容,只需修改name属性**。如果应用程序出于某种原因检查该字段而不是email(或任何其他属性),您可能能够冒充其他用户

无论如何,如果由于某种原因您将电子邮件更改为可以访问的新电子邮件,您可以使用您在该电子邮件地址收到的代码确认电子邮件

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

使用 phone_number 而不是 email 来更改/验证 新电话号码

管理员还可以启用 使用用户首选用户名登录 的选项。请注意,您将无法将此值更改为 任何已被使用的用户名或首选用户名 以冒充其他用户。

恢复/更改密码

只需 知道用户名(或接受电子邮件或电话)并访问它,因为代码将发送到那里,可以恢复密码:

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

服务器的响应总是会是积极的,就像用户名存在一样。您无法使用此方法来枚举用户

使用代码可以更改密码:

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

要更改密码,您需要知道之前的密码

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

认证

用户池支持不同的认证方式。如果您有用户名和密码,也支持不同的方法进行登录。 此外,当用户在池中被认证时,会发放3种类型的令牌ID令牌访问令牌刷新令牌

  • ID令牌:它包含关于已认证用户身份的声明,例如nameemailphone_number。ID令牌也可以用于对您的资源服务器或服务器应用程序进行用户认证。如果您在外部应用程序中使用ID令牌,您必须验证ID令牌的签名,才能信任ID令牌内部的任何声明。

  • ID令牌是包含用户属性值的令牌,甚至包括自定义属性。

  • 访问令牌:它包含关于已认证用户的声明、用户组列表作用域列表。访问令牌的目的是在用户池中授权API操作。例如,您可以使用访问令牌授予用户访问添加、修改或删除用户属性的权限。

  • 刷新令牌:使用刷新令牌,您可以为用户获取新的ID令牌和访问令牌,直到刷新令牌失效。默认情况下,刷新令牌在应用程序用户登录到用户池后30天过期。当您为用户池创建应用程序时,可以将应用程序的刷新令牌过期时间设置为60分钟到10年之间的任何值

ADMIN_NO_SRP_AUTH & ADMIN_USER_PASSWORD_AUTH

这是服务器端认证流程:

  • 服务器端应用程序调用**AdminInitiateAuth API操作**(而不是InitiateAuth)。此操作需要具有包括**cognito-idp:AdminInitiateAuthcognito-idp:AdminRespondToAuthChallenge**权限的AWS凭证。该操作返回所需的认证参数。

  • 在服务器端应用程序获得认证参数后,它调用**AdminRespondToAuthChallenge API操作**。只有在提供AWS凭证时,AdminRespondToAuthChallenge API操作才会成功。

方法默认情况下未启用

登录,您需要知道:

  • 用户池ID

  • 客户端ID

  • 用户名

  • 密码

  • 客户端密钥(仅在应用程序配置为使用密钥时)

为了能够使用此方法登录,该应用程序必须允许使用ALLOW_ADMIN_USER_PASSWORD_AUTH进行登录。 此外,要执行此操作,您需要具有权限的凭证**cognito-idp:AdminInitiateAuthcognito-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
登录代码

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

此方法是另一种简单的**传统用户和密码认证**流程。建议将**传统**认证方法**迁移到Cognito**,并建议随后**禁用**它,改为使用**ALLOW\_USER\_SRP\_AUTH**方法(因为该方法从不通过网络发送密码)。\
此**方法默认情况下未启用**。

与代码中的**前一种认证方法**的主要**区别**在于您**不需要知道用户池ID**,并且您**不需要额外的权限**在Cognito用户池中。

要**登录**,您**需要**知道:

* 客户端ID
* 用户名
* 密码
* 客户端密钥(仅在应用程序配置为使用密钥时)

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

为了**能够使用此方法登录**,该应用程序必须允许使用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

方法始终有效(无法禁用),但您需要拥有有效的刷新令牌。

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

Last updated