Cognito 用户池
Tip
学习和实践 AWS 黑客技术:
HackTricks Training AWS Red Team Expert (ARTE)
学习和实践 GCP 黑客技术:HackTricks Training GCP Red Team Expert (GRTE)
学习和实践 Azure 黑客技术:
HackTricks Training Azure Red Team Expert (AzRTE)
支持 HackTricks
- 查看 订阅计划!
- 加入 💬 Discord 群组 或 Telegram 群组 或 在 Twitter 🐦 上关注我们 @hacktricks_live.
- 通过向 HackTricks 和 HackTricks Cloud GitHub 仓库提交 PR 来分享黑客技巧。
基本信息
用户池是 Amazon Cognito 中的用户目录。通过用户池,您的用户可以通过 Amazon Cognito 登录到您的网页或移动应用,或通过 第三方 身份提供者 (IdP) 进行联合身份验证。无论您的用户是直接登录还是通过第三方,所有用户池的成员都有一个可以通过 SDK 访问的目录配置文件。
用户池提供:
- 注册和登录服务。
- 内置的可定制网页 UI 以登录用户。
- 通过 Facebook、Google、Amazon 登录和 Apple 登录的社交登录,以及通过 SAML 和 OIDC 身份提供者的用户池。
- 用户目录管理和用户配置文件。
- 安全功能,如多因素身份验证 (MFA)、对被泄露凭证的检查、账户接管保护,以及电话和电子邮件验证。
- 通过 AWS Lambda 触发器定制的工作流和用户迁移。
应用程序的源代码 通常还会包含 用户池 ID 和 客户端应用程序 ID(有时还有 应用程序密钥?),这些都是 用户登录 Cognito 用户池所需的。
潜在攻击
- 注册:默认情况下,用户可以自我注册,因此他可以为自己创建一个用户。
- 用户枚举:注册功能可用于查找已存在的用户名。这些信息对于暴力破解攻击可能很有用。
- 登录暴力破解:在 身份验证 部分,您可以找到用户 登录 的所有 方法,您可以尝试暴力破解它们以 找到有效凭证。
渗透测试工具
- Pacu 现在包括
cognito__enum和cognito__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
- CognitoAttributeEnum: 该脚本允许枚举用户的有效属性。
python cognito-attribute-enu.py -client_id 16f1g98bfuj9i0g3f8be36kkrl
注册
用户池默认允许注册新用户。
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
Note
注意在之前的命令中,自定义属性以 “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
Warning
即使看起来你可以使用相同的电子邮件和电话号码,当你需要验证创建的用户时,Cognito会抱怨使用相同的信息,并且不会让你验证账户。
权限提升 / 更新属性
默认情况下,用户可以修改其属性的值,使用类似于:
aws cognito-idp update-user-attributes \
--region us-east-1 --no-sign-request \
--user-attributes Name=address,Value=street \
--access-token <access token>
自定义属性权限提升
Caution
您可能会发现使用 custom attributes(例如
isAdmin),因为默认情况下您可以 更改自己属性的值,您可能能够 通过自己更改值来提升权限!
邮箱/用户名修改权限提升
您可以使用此功能 修改用户的电子邮件和电话号码,但即使帐户保持验证状态,这些属性也会 设置为未验证状态(您需要再次验证它们)。
Warning
您 无法使用电子邮件或电话号码登录,直到您验证它们,但您可以 使用用户名登录。
请注意,即使电子邮件已被修改且未验证,它仍会出现在 ID Token 的email_verified将为 false,但如果应用程序 没有检查,您可能会冒充其他用户。
此外,请注意,您可以在
name字段中放入任何内容,只需修改 name attribute。如果应用程序 出于某种原因检查 该字段 而不是
无论如何,如果出于某种原因您将电子邮件更改为可以访问的新电子邮件,您可以 使用您在该电子邮件地址收到的代码确认电子邮件:
aws cognito-idp verify-user-attribute \
--access-token <access_token> \
--attribute-name email --code <code> \
--region <region> --no-sign-request
使用 phone_number 替代 email 来更改/验证 新电话号码。
Note
管理员还可以启用 使用用户首选用户名登录 的选项。请注意,您将无法将此值更改为 任何已被使用的用户名或首选用户名 以冒充其他用户。
恢复/更改密码
只需 知道用户名(或电子邮件或电话也可以接受)并且能够访问它,因为代码将发送到那里:
aws cognito-idp forgot-password \
--client-id <client_id> \
--username <username/email/phone> --region <region>
Note
服务器的响应总是会是积极的,比如用户名存在。您无法使用此方法枚举用户
使用以下代码可以更改密码:
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令牌:它包含关于已认证用户身份的声明,例如
name、email和phone_number。ID令牌还可以用于对您的资源服务器或服务器应用程序进行用户认证。如果您在外部应用程序中使用ID令牌,您必须验证ID令牌的签名,才能信任ID令牌中的任何声明。 - ID令牌是包含用户属性值的令牌,甚至包括自定义属性。
- 访问令牌:它包含关于已认证用户的声明、用户组的列表和作用域的列表。访问令牌的目的是在用户池中授权API操作。例如,您可以使用访问令牌授予用户访问添加、修改或删除用户属性的权限。
- 刷新令牌:使用刷新令牌,您可以为用户获取新的ID令牌和访问令牌,直到刷新令牌失效。默认情况下,刷新令牌在您的应用程序用户登录用户池后30天过期。当您为用户池创建应用程序时,可以将应用程序的刷新令牌过期时间设置为60分钟到10年之间的任何值。
ADMIN_NO_SRP_AUTH & ADMIN_USER_PASSWORD_AUTH
这是服务器端认证流程:
- 服务器端应用调用**
AdminInitiateAuthAPI操作**(而不是InitiateAuth)。此操作需要具有包括**cognito-idp:AdminInitiateAuth和cognito-idp:AdminRespondToAuthChallenge**权限的AWS凭证。该操作返回所需的认证参数。 - 在服务器端应用获得认证参数后,它调用**
AdminRespondToAuthChallengeAPI操作**。只有在提供AWS凭证时,AdminRespondToAuthChallengeAPI操作才会成功。
此方法默认情况下未启用。
要登录,您需要知道:
- 用户池ID
- 客户端ID
- 用户名
- 密码
- 客户端密钥(仅在应用配置为使用密钥时)
Note
为了能够使用此方法登录,该应用程序必须允许使用
ALLOW_ADMIN_USER_PASSWORD_AUTH进行登录。
此外,要执行此操作,您需要具有**cognito-idp:AdminInitiateAuth和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
登录代码
```python import boto3 import botocore import hmac import hashlib import base64client_id = “
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用户池中。
要**登录**,您**需要**知道:
- client id
- username
- password
- client secret(仅在应用程序配置为使用密钥时)
> [!NOTE]
> 为了**能够使用此方法登录**,该应用程序必须允许使用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
登录的Python代码
```python import boto3 import botocore import hmac import hashlib import base64client_id = “
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))
</details>
### USER_SRP_AUTH
这个场景与之前的类似,但**不是通过网络发送密码**来登录,而是**执行挑战认证**(因此没有密码即使加密也不会在网络中传输)。\
这个**方法是默认启用**的。
要**登录**,您**需要**知道:
- 用户池 ID
- 客户端 ID
- 用户名
- 密码
- 客户端密钥(仅在应用程序配置为使用密钥时)
<details>
<summary>登录代码</summary>
```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
此方法始终有效(无法禁用),但您需要拥有有效的刷新令牌。
aws cognito-idp initiate-auth \
--client-id 3ig6h5gjm56p1ljls1prq2miut \
--auth-flow REFRESH_TOKEN_AUTH \
--region us-east-1 \
--auth-parameters 'REFRESH_TOKEN=<token>'
刷新代码
```python import boto3 import botocore import hmac import hashlib import base64client_id = “
boto_client = boto3.client(‘cognito-idp’, region_name=‘
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))
</details>
### CUSTOM_AUTH
在这种情况下,**身份验证**将通过**执行一个lambda函数**来进行。
## 额外安全性
### 高级安全性
默认情况下是禁用的,但如果启用,Cognito可能能够**发现账户接管**。为了最小化这种可能性,您应该从**同一城市的网络登录,使用相同的用户代理**(如果可能的话,使用相同的IP)。
### **MFA 记住设备**
如果用户从同一设备登录,MFA可能会被绕过,因此尝试从相同的浏览器使用相同的元数据(IP?)登录,以尝试绕过MFA保护。
## 用户池组 IAM 角色
可以将**用户添加到与一个**IAM角色**相关的用户池组中。\
此外,**用户**可以被分配到**多个具有不同IAM角色**的组中。
请注意,即使一个组在一个附有IAM角色的组内,要能够访问该组的IAM凭证,需要**用户池被身份池信任**(并了解该身份池的详细信息)。
另一个要求是在用户在用户池中经过身份验证时获取**IdToken中指示的IAM角色**(`aws cognito-idp initiate-auth...`),需要**身份提供者身份验证提供者**指示**角色必须从令牌中选择**。
<figure><img src="../../../../images/image (250).png" alt=""><figcaption></figcaption></figure>
用户可以访问的**角色**在**`IdToken`**中,用户可以使用**`--custom-role-arn`**从`aws cognito-identity get-credentials-for-identity`选择他希望获取凭证的**角色**。\
然而,如果**默认选项**是**配置的**(`使用默认角色`),并且您尝试从IdToken访问一个角色,您将会得到**错误**(这就是为什么需要之前的配置):
An error occurred (InvalidParameterException) when calling the GetCredentialsForIdentity operation: Only SAML providers and providers with RoleMappings support custom role ARN.
> [!WARNING]
> 请注意,分配给 **User Pool Group** 的角色需要 **被信任 User Pool 的身份提供者可访问**(因为 IAM 角色的 **会话凭证将从中获取**)。
```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
学习和实践 AWS 黑客技术:
HackTricks Training AWS Red Team Expert (ARTE)
学习和实践 GCP 黑客技术:HackTricks Training GCP Red Team Expert (GRTE)
学习和实践 Azure 黑客技术:
HackTricks Training Azure Red Team Expert (AzRTE)
支持 HackTricks
- 查看 订阅计划!
- 加入 💬 Discord 群组 或 Telegram 群组 或 在 Twitter 🐦 上关注我们 @hacktricks_live.
- 通过向 HackTricks 和 HackTricks Cloud GitHub 仓库提交 PR 来分享黑客技巧。
HackTricks Cloud

