Cognito User Pools
Reading time: 14 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 깃허브 리포지토리에 PR을 제출하여 해킹 트릭을 공유하세요.
기본 정보
사용자 풀은 Amazon Cognito의 사용자 디렉토리입니다. 사용자 풀을 사용하면 사용자가 Amazon Cognito를 통해 웹 또는 모바일 앱에 로그인하거나 제3자 신원 공급자(IdP)를 통해 연합할 수 있습니다. 사용자가 직접 로그인하든 제3자를 통해 로그인하든, 사용자 풀의 모든 구성원은 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에 대한 다양한 공격을 구현하는 파이썬 기반 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
필드에 무엇이든 넣을 수 있습니다. 어떤 이유로 앱이
어쨌든, 어떤 이유로 이메일을 새 이메일로 변경한 경우, 해당 이메일 주소로 받은 코드로 이메일을 확인할 수 있습니다:
aws cognito-idp verify-user-attribute \
--access-token <access_token> \
--attribute-name email --code <code> \
--region <region> --no-sign-request
전화번호
대신 **이메일
**을 사용하여 새 전화번호를 변경/확인합니다.
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 토큰은 사용자의 속성 값을 포함하는 토큰으로, 사용자 정의 속성도 포함됩니다.
- 액세스 토큰: 인증된 사용자에 대한 주장, 사용자의 그룹 목록, 범위 목록을 포함합니다. 액세스 토큰의 목적은 사용자 풀 내에서 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
이 경우 인증은 람다 함수의 실행을 통해 수행됩니다.
추가 보안
고급 보안
기본적으로 비활성화되어 있지만, 활성화되면 Cognito는 계정 탈취를 발견할 수 있습니다. 가능성을 최소화하려면 같은 도시 내의 네트워크에서, 같은 사용자 에이전트를 사용하여 로그인해야 합니다 (가능하다면 IP도).
MFA 기억 장치
사용자가 같은 장치에서 로그인하면 MFA가 우회될 수 있으므로, MFA 보호를 우회하기 위해 같은 메타데이터(IP?)로 같은 브라우저에서 로그인해 보십시오.
사용자 풀 그룹 IAM 역할
사용자 풀 그룹에 사용자를 추가할 수 있으며, 이는 하나의 IAM 역할과 관련이 있습니다.
또한, 사용자는 다른 IAM 역할이 연결된 1개 이상의 그룹에 할당될 수 있습니다.
그룹이 IAM 역할이 연결된 그룹 내에 있더라도, 해당 그룹의 IAM 자격 증명에 접근하려면 사용자 풀이 신뢰할 수 있는 Identity Pool이어야 하며 (그 Identity Pool의 세부 정보를 알아야 함),
사용자가 사용자 풀에서 인증될 때 IdToken에 표시된 IAM 역할을 얻기 위한 또 다른 요구 사항은 Identity Provider Authentication provider가 역할이 토큰에서 선택되어야 한다고 표시해야 합니다.
.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
사용자 풀 그룹에 할당된 역할은 사용자 풀을 신뢰하는 ID 공급자에 의해 접근 가능해야 합니다(왜냐하면 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 깃허브 리포지토리에 PR을 제출하여 해킹 트릭을 공유하세요.