Cognito User Pools

htARTE (HackTricks AWS Red Team Expert)에서 AWS 해킹을 처음부터 전문가까지 배우세요!

HackTricks를 지원하는 다른 방법:

기본 정보

사용자 풀은 Amazon Cognito의 사용자 디렉터리입니다. 사용자 풀을 사용하면 사용자들이 Amazon Cognito를 통해 웹 또는 모바일 앱에 로그인하거나 제3자 식별 공급자(IdP)를 통해 연합할 수 있습니다. 사용자가 직접 로그인하든 제3자를 통해 로그인하든 사용자 풀의 모든 구성원은 SDK를 통해 액세스할 수 있는 디렉터리 프로필을 갖습니다.

사용자 풀은 다음을 제공합니다:

  • 가입 및 로그인 서비스.

  • 사용자가 로그인하는 데 사용할 수 있는 내장형 사용자 지정 웹 UI.

  • Facebook, Google, Amazon 로그인, Apple 로그인 및 사용자 풀의 SAML 및 OIDC 식별 공급자를 통한 소셜 로그인.

  • 사용자 디렉터리 관리 및 사용자 프로필.

  • 다중 요소 인증(MFA), 침해된 자격 증명 확인, 계정 탈취 방지, 전화 및 이메일 확인과 같은 보안 기능.

  • AWS Lambda 트리거를 통한 사용자 정의 워크플로 및 사용자 이전.

일반적으로 애플리케이션의 소스 코드에는 사용자 풀 ID와 클라이언트 애플리케이션 ID(그리고 때로는 애플리케이션 비밀?)이 포함되어 있습니다. 이는 Cognito 사용자 풀에 로그인하기 위해 필요합니다.

잠재적인 공격

  • 등록: 기본적으로 사용자는 자체적으로 등록할 수 있으므로 자신을 위해 사용자를 생성할 수 있습니다.

  • 사용자 열거: 등록 기능을 사용하여 이미 존재하는 사용자 이름을 찾을 수 있습니다. 이 정보는 브루트포스 공격에 유용할 수 있습니다.

  • 로그인 브루트포스: 인증 섹션에서 사용자가 로그인해야 하는 모든 방법이 나와 있으므로 이를 브루트포스하여 유효한 자격 증명을 찾을 수 있습니다.

펜테스팅 도구

  • Pacu, AWS 취약점 분석 프레임워크,는 이제 계정의 모든 Cognito 자산을 열거하고 취약한 구성, 액세스 제어에 사용되는 사용자 속성 등을 식별하며 (MFA 지원 포함) 사용자 생성 및 수정 가능한 사용자 지정 속성, 사용 가능한 ID 풀 자격 증명, ID 토큰의 가정 가능한 역할 등을 기반으로 권한 상승을 자동화하는 "cognito__enum" 및 "cognito__attack" 모듈을 포함합니다.

모듈 기능에 대한 설명은 블로그 게시물의 2부를 참조하세요. 설치 지침은 주요 Pacu 페이지를 참조하세요.

사용법

주어진 ID 풀 및 사용자 풀 클라이언트에 대해 사용자 생성 및 모든 권한 상승 벡터를 시도하는 샘플 cognito__attack 사용법:

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

다음은 현재 AWS 계정에서 볼 수 있는 모든 사용자 풀, 사용자 풀 클라이언트, ID 풀, 사용자 등을 수집하기 위한 cognito__enum 사용 예시입니다:

Pacu (new:test) > run cognito__enum
  • Cognito Scanner은 Cognito에 대한 다양한 공격을 구현한 Python CLI 도구로, 원치 않는 계정 생성 및 계정 오라클을 포함합니다.

설치

$ pip install cognito-scanner

사용법

$ cognito-scanner --help

더 많은 정보는 https://github.com/padok-team/cognito-scanner에서 확인하세요.

등록

사용자 풀은 기본적으로 새 사용자를 등록할 수 있습니다.

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가 존재하는지 테스트하려면 이렇게 할 수도 있습니다. 클라이언트 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는 동일한 정보를 사용하는 것에 대해 이의를 제기하고 계정 확인을 허용하지 않을 것입니다.

Privilege Escalation / Updating Attributes

기본적으로 사용자는 다음과 같이 속성 값을 수정할 수 있습니다:

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 필드에 아무 값이나 넣을 수 있습니다. 이름 속성을 수정하기만 하면 됩니다. 앱이 email 대신 해당 필드를 확인하는 경우 (또는 다른 속성) 다른 사용자를 흉내 낼 수 있습니다.

어쨌든, 예를 들어 이메일을 새 이메일로 변경한 경우 해당 이메일 주소로 받은 코드로 이메일을 확인할 수 있습니다:

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

이메일 대신 **전화번호**를 사용하여 새 전화번호를 변경/확인합니다.

관리자는 또한 사용자 선호하는 사용자 이름으로 로그인 옵션을 활성화할 수 있습니다. 이미 사용 중인 사용자 이름 또는 preferred_username을 사용하여 다른 사용자를 표시하는 것은 불가능합니다.

비밀번호 복구/변경

사용자 이름을 알고 있다면(또는 이메일 또는 전화번호가 허용됨) 해당 사용자에게 코드가 전송되어 액세스할 수 있다면 비밀번호를 복구할 수 있습니다:

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 토큰: 인증된 사용자의 신원에 대한 클레임(claim)인 name, email, phone_number 등을 포함합니다. ID 토큰은 또한 리소스 서버나 서버 애플리케이션에 사용자를 인증하는 데 사용될 수 있습니다. 외부 애플리케이션에서 사용하는 경우 ID 토큰의 서명(signature)을 확인해야만 ID 토큰 내의 어떠한 클레임도 신뢰할 수 있습니다.

  • ID 토큰은 사용자의 속성 값, 심지어 사용자 지정 속성까지 포함하는 토큰입니다.

  • 액세스 토큰: 인증된 사용자에 대한 클레임, 사용자의 그룹 목록 및 스코프 목록을 포함합니다. 액세스 토큰의 목적은 사용자 풀의 사용자 컨텍스트에서 API 작업을 승인하는 것입니다. 예를 들어, 액세스 토큰을 사용하여 사용자에게 사용자 속성을 추가, 변경 또는 삭제할 수 있는 권한을 부여할 수 있습니다.

  • 리프레시 토큰: 리프레시 토큰을 사용하면 리프레시 토큰이 무효화될 때까지 사용자에게 새로운 ID 토큰 및 액세스 토큰을 얻을 수 있습니다. 기본적으로, 리프레시 토큰은 사용자가 사용자 풀에 로그인한 후 30일 후에 만료됩니다. 사용자 풀을 위한 응용 프로그램을 만들 때 응용 프로그램의 리프레시 토큰 만료를 60분부터 10년 사이의 값으로 설정할 수 있습니다.

ADMIN_NO_SRP_AUTH 및 ADMIN_USER_PASSWORD_AUTH

이는 서버 측 인증 흐름입니다:

  • 서버 측 앱은 InitiateAuth 대신 AdminInitiateAuth API 작업을 호출합니다. 이 작업은 cognito-idp:AdminInitiateAuth 및 **cognito-idp:AdminRespondToAuthChallenge**을 포함하는 권한이 있는 AWS 자격 증명이 필요합니다. 이 작업은 필요한 인증 매개변수를 반환합니다.

  • 서버 측 앱이 인증 매개변수를 가지고 있는 경우, AdminRespondToAuthChallenge API 작업을 호출합니다. AdminRespondToAuthChallenge API 작업은 AWS 자격 증명을 제공할 때에만 성공합니다.

방법은 기본적으로 활성화되어 있지 않습니다.

로그인하려면 다음을 알아야 합니다:

  • 사용자 풀 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>'

最終更新