Cognito User Pools

Tip

Ucz się & ćwicz AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Ucz się & ćwicz GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Ucz się & ćwicz Az Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Wspieraj HackTricks

Podstawowe informacje

User pool to katalog użytkowników w Amazon Cognito. Dzięki user pool, Twoi użytkownicy mogą zalogować się do Twojej aplikacji internetowej lub mobilnej za pośrednictwem Amazon Cognito, lub federować przez dostawcę tożsamości (IdP) zewnętrznego. Niezależnie od tego, czy Twoi użytkownicy logują się bezpośrednio, czy przez stronę trzecią, wszyscy członkowie user pool mają profil katalogu, do którego możesz uzyskać dostęp za pośrednictwem SDK.

User pools oferują:

  • Usługi rejestracji i logowania.
  • Wbudowany, dostosowywalny interfejs użytkownika do logowania użytkowników.
  • Logowanie społecznościowe za pomocą Facebooka, Google, Logowanie z Amazon oraz Logowanie z Apple, a także przez dostawców tożsamości SAML i OIDC z Twojego user pool.
  • Zarządzanie katalogiem użytkowników i profilami użytkowników.
  • Funkcje bezpieczeństwa, takie jak uwierzytelnianie wieloskładnikowe (MFA), kontrole dla skompromitowanych poświadczeń, ochrona przed przejęciem konta oraz weryfikacja telefonu i e-maila.
  • Dostosowane przepływy pracy i migracja użytkowników za pomocą wyzwalaczy AWS Lambda.

Kod źródłowy aplikacji zazwyczaj zawiera również ID user pool oraz ID aplikacji klienckiej, (a czasami sekret aplikacji?), które są potrzebne do logowania użytkownika do Cognito User Pool.

Potencjalne ataki

  • Rejestracja: Domyślnie użytkownik może zarejestrować się samodzielnie, więc mógłby stworzyć konto dla siebie.
  • Enumeracja użytkowników: Funkcjonalność rejestracji może być używana do znajdowania nazw użytkowników, które już istnieją. Ta informacja może być przydatna do ataku brute-force.
  • Brute-force logowania: W sekcji Uwierzytelnianie masz wszystkie metody, które użytkownik ma do logowania, możesz spróbować je brutalnie przetestować, aby znaleźć ważne poświadczenia.

Narzędzia do pentestingu

  • Pacu, teraz zawiera moduły cognito__enum i cognito__attack, które automatyzują enumerację wszystkich zasobów Cognito w koncie i oznaczają słabe konfiguracje, atrybuty użytkowników używane do kontroli dostępu itp., a także automatyzują tworzenie użytkowników (w tym wsparcie dla MFA) oraz eskalację uprawnień na podstawie modyfikowalnych atrybutów niestandardowych, używalnych poświadczeń puli tożsamości, ról, które można przyjąć w tokenach id itp.
    Aby uzyskać opis funkcji modułów, zobacz część 2 postu na blogu. Aby uzyskać instrukcje instalacji, zobacz główną stronę 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 to narzędzie CLI w Pythonie, które implementuje różne ataki na Cognito, w tym niechciane tworzenie kont i oracle kont. Sprawdź ten link po więcej informacji.
# Install
pip install cognito-scanner
# Run
cognito-scanner --help
python cognito-attribute-enu.py -client_id 16f1g98bfuj9i0g3f8be36kkrl

Rejestracja

User Pools pozwala domyślnie na rejestrowanie nowych użytkowników.

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

Jeśli ktokolwiek może się zarejestrować

Możesz napotkać błąd wskazujący, że musisz podać więcej szczegółów dotyczących użytkownika:

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

Możesz dostarczyć potrzebne szczegóły w formacie JSON, takim jak:

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

Możesz również użyć tej funkcjonalności do enumeracji istniejących użytkowników. Oto komunikat o błędzie, gdy użytkownik o tej nazwie już istnieje:

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

Note

Zauważ w poprzedniej komendzie, jak niestandardowe atrybuty zaczynają się od “custom:”.
Również wiedz, że podczas rejestracji nie możesz tworzyć nowych niestandardowych atrybutów dla użytkownika. Możesz tylko przypisać wartość do domyślnych atrybutów (nawet jeśli nie są wymagane) oraz określonych niestandardowych atrybutów.

Lub po prostu, aby sprawdzić, czy identyfikator klienta istnieje. Oto błąd, jeśli identyfikator klienta nie istnieje:

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

Jeśli tylko administrator może rejestrować użytkowników

Znajdziesz ten błąd i nie będziesz mógł zarejestrować ani enumerować użytkowników:

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

Weryfikacja rejestracji

Cognito pozwala na weryfikację nowego użytkownika poprzez potwierdzenie jego adresu e-mail lub numeru telefonu. Dlatego podczas tworzenia użytkownika zazwyczaj będziesz musiał podać przynajmniej nazwę użytkownika i hasło oraz adres e-mail i/lub numer telefonu. Po prostu ustaw jeden który kontrolujesz, aby otrzymać kod do weryfikacji nowo utworzonego konta użytkownika w ten sposób:

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

Warning

Nawet jeśli wygląda na to, że możesz użyć tego samego adresu e-mail i numeru telefonu, gdy musisz zweryfikować utworzonego użytkownika, Cognito będzie narzekać na użycie tych samych informacji i nie pozwoli ci zweryfikować konta.

Eskalacja uprawnień / Aktualizacja atrybutów

Domyślnie użytkownik może zmienić wartość swoich atrybutów za pomocą czegoś takiego:

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

Privesc atrybutów niestandardowych

Caution

Możesz znaleźć atrybuty niestandardowe używane (takie jak isAdmin), ponieważ domyślnie możesz zmieniać wartości swoich własnych atrybutów, co może pozwolić ci na eskalację uprawnień poprzez zmianę wartości samodzielnie!

Privesc modyfikacji e-maila/nazwy użytkownika

Możesz użyć tego do modyfikacji e-maila i numeru telefonu użytkownika, ale wtedy, nawet jeśli konto pozostaje zweryfikowane, te atrybuty są ustawione w statusie nieweryfikowanym (musisz je zweryfikować ponownie).

Warning

Nie będziesz mógł zalogować się za pomocą e-maila lub numeru telefonu dopóki ich nie zweryfikujesz, ale będziesz mógł zalogować się za pomocą nazwy użytkownika.
Zauważ, że nawet jeśli e-mail został zmodyfikowany i niezweryfikowany, pojawi się w tokenie ID w polu email a pole email_verified będzie fałszywe, ale jeśli aplikacja nie sprawdza tego, możesz podszyć się pod innych użytkowników.

Ponadto, zauważ, że możesz wpisać cokolwiek w pole name po prostu modyfikując atrybut nazwy. Jeśli aplikacja sprawdza to pole z jakiegoś powodu zamiast email (lub jakiegokolwiek innego atrybutu), możesz być w stanie podszyć się pod innych użytkowników.

W każdym razie, jeśli z jakiegoś powodu zmieniłeś swój e-mail na nowy, do którego masz dostęp, możesz potwierdzić e-mail za pomocą kodu, który otrzymałeś na ten adres e-mail:

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

Użyj phone_number zamiast email, aby zmienić/weryfikować nowy numer telefonu.

Note

Administrator może również włączyć opcję logowania za pomocą preferowanej nazwy użytkownika. Należy pamiętać, że nie będziesz mógł zmienić tej wartości na dowolną nazwę użytkownika lub preferred_username, która jest już używana do podszywania się pod innego użytkownika.

Przywracanie/Zmiana Hasła

Możliwe jest odzyskanie hasła, znając nazwę użytkownika (lub akceptowany jest email lub telefon), a dostęp do niego będzie możliwy, ponieważ kod zostanie tam wysłany:

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

Note

Odpowiedź serwera zawsze będzie pozytywna, jakby nazwa użytkownika istniała. Nie możesz użyć tej metody do enumeracji użytkowników

Za pomocą kodu możesz zmienić hasło na:

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

Aby zmienić hasło, musisz znać poprzednie hasło:

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

Uwierzytelnianie

Pula użytkowników obsługuje różne sposoby uwierzytelniania. Jeśli masz nazwa użytkownika i hasło, obsługiwane są również różne metody logowania.
Ponadto, gdy użytkownik jest uwierzytelniony w Puli, przyznawane są 3 typy tokenów: Token ID, Token dostępu i Token odświeżania.

  • Token ID: Zawiera roszczenia dotyczące tożsamości uwierzytelnionego użytkownika, takie jak name, email i phone_number. Token ID może być również używany do uwierzytelniania użytkowników w serwerach zasobów lub aplikacjach serwerowych. Musisz zweryfikować podpis tokena ID, zanim będziesz mógł zaufać jakimkolwiek roszczeniom wewnątrz tokena ID, jeśli używasz go w aplikacjach zewnętrznych.
  • Token ID to token, który zawiera wartości atrybutów użytkownika, nawet te niestandardowe.
  • Token dostępu: Zawiera roszczenia dotyczące uwierzytelnionego użytkownika, listę grup użytkownika oraz listę zakresów. Celem tokena dostępu jest autoryzacja operacji API w kontekście użytkownika w puli użytkowników. Na przykład, możesz użyć tokena dostępu, aby przyznać swojemu użytkownikowi dostęp do dodawania, zmieniania lub usuwania atrybutów użytkownika.
  • Token odświeżania: Dzięki tokenom odświeżania możesz uzyskać nowe tokeny ID i tokeny dostępu dla użytkownika, dopóki token odświeżania jest ważny. Domyślnie token odświeżania wygasa 30 dni po zalogowaniu się użytkownika do puli użytkowników. Gdy tworzysz aplikację dla swojej puli użytkowników, możesz ustawić czas wygaśnięcia tokena odświeżania aplikacji na dowolną wartość między 60 minutami a 10 latami.

ADMIN_NO_SRP_AUTH & ADMIN_USER_PASSWORD_AUTH

To jest przepływ uwierzytelniania po stronie serwera:

  • Aplikacja po stronie serwera wywołuje operację API AdminInitiateAuth (zamiast InitiateAuth). Ta operacja wymaga poświadczeń AWS z uprawnieniami, które obejmują cognito-idp:AdminInitiateAuth i cognito-idp:AdminRespondToAuthChallenge. Operacja zwraca wymagane parametry uwierzytelniania.
  • Po uzyskaniu parametrów uwierzytelniania przez aplikację po stronie serwera, wywołuje operację API AdminRespondToAuthChallenge. Operacja API AdminRespondToAuthChallenge kończy się sukcesem tylko wtedy, gdy dostarczysz poświadczenia AWS.

Ta metoda NIE jest włączona domyślnie.

Aby zalogować się, musisz znać:

  • identyfikator puli użytkowników
  • identyfikator klienta
  • nazwę użytkownika
  • hasło
  • sekret klienta (tylko jeśli aplikacja jest skonfigurowana do używania sekretu)

Note

Aby móc zalogować się tą metodą, ta aplikacja musi zezwalać na logowanie z ALLOW_ADMIN_USER_PASSWORD_AUTH.
Ponadto, aby wykonać tę akcję, potrzebujesz poświadczeń z uprawnieniami cognito-idp:AdminInitiateAuth i 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
Kod do logowania ```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

Ta metoda to kolejny prosty i **tradycyjny proces uwierzytelniania użytkownika i hasła**. Zaleca się **migrację tradycyjnej** metody uwierzytelniania **do Cognito** i **zaleca się** następnie **wyłączenie** jej oraz **użycie** metody **ALLOW_USER_SRP_AUTH** zamiast (ponieważ ta nigdy nie przesyła hasła przez sieć).\
Ta **metoda NIE jest włączona** domyślnie.

Główna **różnica** w porównaniu do **poprzedniej metody uwierzytelniania** w kodzie polega na tym, że **nie musisz znać identyfikatora puli użytkowników** i że **nie potrzebujesz dodatkowych uprawnień** w Cognito User Pool.

Aby **zalogować się**, musisz znać:

- identyfikator klienta
- nazwę użytkownika
- hasło
- tajny klucz klienta (tylko jeśli aplikacja jest skonfigurowana do używania tajnego klucza)

> [!NOTE]
> Aby **móc zalogować się tą metodą**, ta aplikacja musi zezwalać na logowanie z 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
Kod Python do logowania ```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.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

Ten scenariusz jest podobny do poprzedniego, ale **zamiast wysyłać hasło** przez sieć, aby się zalogować, **przeprowadzana jest autoryzacja wyzwania** (więc żadne hasło nie jest przesyłane, nawet w formie zaszyfrowanej przez sieć).\
Ta **metoda jest włączona** domyślnie.

Aby **zalogować się**, musisz znać:

- identyfikator puli użytkowników
- identyfikator klienta
- nazwę użytkownika
- hasło
- sekret klienta (tylko jeśli aplikacja jest skonfigurowana do używania sekretu)

<details>

<summary>Code to login</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

Ta metoda zawsze będzie ważna (nie można jej wyłączyć), ale musisz mieć ważny token odświeżania.

aws cognito-idp initiate-auth \
--client-id 3ig6h5gjm56p1ljls1prq2miut \
--auth-flow REFRESH_TOKEN_AUTH \
--region us-east-1 \
--auth-parameters 'REFRESH_TOKEN=<token>'
Kod do odświeżenia ```python import boto3 import botocore import hmac import hashlib import base64

client_id = “” token = ‘

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

W tym przypadku **uwierzytelnianie** będzie przeprowadzane poprzez **wykonanie funkcji lambda**.

## Dodatkowe zabezpieczenia

### Zaawansowane zabezpieczenia

Domyślnie jest wyłączone, ale jeśli jest włączone, Cognito może być w stanie **znaleźć przejęcia konta**. Aby zminimalizować prawdopodobieństwo, powinieneś logować się z **sieci w tym samym mieście, używając tego samego agenta użytkownika** (i IP, jeśli to możliwe)**.**

### **MFA Pamiętaj urządzenie**

Jeśli użytkownik loguje się z tego samego urządzenia, MFA może być ominięte, dlatego spróbuj zalogować się z tej samej przeglądarki z tymi samymi metadanymi (IP?), aby spróbować obejść ochronę MFA.

## Role IAM grup użytkowników

Możliwe jest dodanie **użytkowników do grupy User Pool**, które są związane z jedną **rolą IAM**.\
Ponadto, **użytkownicy** mogą być przypisani do **więcej niż 1 grupy z różnymi rolami IAM**.

Zauważ, że nawet jeśli grupa znajduje się w grupie z przypisaną rolą IAM, aby móc uzyskać dostęp do poświadczeń IAM tej grupy, konieczne jest, aby **User Pool był zaufany przez Identity Pool** (i znał szczegóły tego Identity Pool).

Innym wymogiem, aby uzyskać **rolę IAM wskazaną w IdToken**, gdy użytkownik jest uwierzytelniony w User Pool (`aws cognito-idp initiate-auth...`), jest to, że **dostawca uwierzytelniania Identity Provider** musi wskazać, że **rola musi być wybierana z tokena.**

<figure><img src="../../../../images/image (250).png" alt=""><figcaption></figcaption></figure>

**Role**, do których użytkownik ma dostęp, są **w `IdToken`**, a użytkownik może **wybrać, dla której roli chciałby uzyskać poświadczenia** za pomocą **`--custom-role-arn`** z `aws cognito-identity get-credentials-for-identity`.\
Jednak jeśli **domyślna opcja** jest tą **skonfigurowaną** (`use default role`), a ty próbujesz uzyskać dostęp do roli z IdToken, otrzymasz **błąd** (dlatego potrzebna jest wcześniejsza konfiguracja):

An error occurred (InvalidParameterException) when calling the GetCredentialsForIdentity operation: Only SAML providers and providers with RoleMappings support custom role ARN.

> [!WARNING]
> Zauważ, że rola przypisana do **User Pool Group** musi być **dostępna dla Dostawcy Tożsamości**, który **ufa User Pool** (ponieważ **poświadczenia sesji roli IAM będą uzyskiwane z niego**).
```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

Ucz się & ćwicz AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Ucz się & ćwicz GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Ucz się & ćwicz Az Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Wspieraj HackTricks