Compromising S3 Buckets through Misconfigured AWS Cognito

Mehul Bhatt
5 min readOct 18, 2020

In a recent engagement, I came across a misconfigured Cognito service that allowed me to compromise S3 buckets through an unauthenticated user role, and in this blog I will discuss how I was able to achieve that.

What is Amazon Cognito?

Amazon Cognito is a service that provides authentication, authorization, and user management for your web and mobile apps. Your users can sign in directly with a user name and password, or through a third party such as Facebook, Amazon, Google or Apple.

There are 2 main components to it:

  1. User pools: These are user directories that provide sign-up and sign-in options for your app (mobile or web) users.
  2. Identity pools: These enable you to grant your users (authenticated and unauthenticated) access to other AWS services

TL;DR - User Pool stores all users, and Identity Pool enables those users to access AWS services.

The diagram below depicts a high a level authentication and authorization flow.

  1. User authenticates against a user pool
  2. The user pool assigns 3 JWT tokens (Id, Access, and Refresh) to the user
  3. The ID JWT is passed to the identity pool in order to receive temporary AWS credentials with roles assigned to the identity provider
  4. The user can then make calls to AWS services based on their privileges.

The Hack

A little background on the application that I was testing:

  • It’s a healthcare and patient management app. There were a plethora of user roles from the doctors, to dieticians to patients and many more.
  • Tech stack of the app - NodeJS, React, and MongoDB, hosted over a S3 bucket

While playing with the authentication module, I found that the application was using AWS Cognito for authentication and authorization through the JavaScript SDK (The AWS Amplify Framework). JavaScript config files disclosed data such as App Client ID, User Pool ID, Identity Pool ID, and Region information.

The disclosure of App Client ID, User Pool ID, Identity Pool ID, and Region information is not a misconfiguration since these are not confidential values

Since, Amazon Cognito has authenticated and unauthenticated mode to generate AWS credentials for users, unauthenticated access rights can be obtained by anyone using a specific API call:

AWSCognitoIdentityService.GetId

Using the IdentityPoolID from the JavaScript file we can generate our IdentityID:

Generating IdentityID

From the IdentityID, I generated my temporary AWS Access Key, Secret Key and Session Token using another API call:

AWSCognitoIdentityService.GetCredentialsIdentity

And it worked!

Generating temporary AWS credentials

To avoid the Burp Suite + API calls hoopla, we can also generate the temporary credentials through this script

Run the script…

python get_creds.py "<region_id>" "<identity_pool_id>"
Generating credentials through the script

We can now use the AWS Command Line Interface(CLI) to interact with the AWS services.

First save the Access Key, Secret Key, Session Token and the Region in the ~/.aws/credentials file.

Using the sts get-caller-identity command, we can confirm that the tokens were working fine

Andrés Riancho’s ​enumerate-iam​ tool helped in enumerating the list of functions which were available to the privilege of the unauthenticated user:

s3.list_buckets()
iam.get_account_password_policy()
sts.get_caller_identity()
dynamodb.describe_endpoints()

Out of these thes3.list_buckets() functions looks like something that could disclose some juicy stuff.

And…. bang! I was able to access 260 S3 buckets..

Although public access to these buckets were blocked, i.e, I was not able to list the contents of these buckets through a browser, I now had full access to them.

On further enumeration, I found that the level of permission granted to this unauthenticated user role was FULL_CONTROL for both the buckets and the objects.

Bucket Permission
Object Permission

The Impact

What does FULL_CONTROL mean? Let’s refer the AWS docs

Set of permissions that Amazon S3 supports in an ACL

Basically, I can:

  • Dump the entire S3 bucket on my local system
  • Transfer data between other buckets
  • Access and delete sensitive data such as production logs, sensitive healthcare data of users, configuration files of the server, database, etc., backup code, API keys for third-party services, and what not
  • Overwrite the existing files, and add malicious files to the bucket
  • Delete the entire bucket

Given that this was a healthcare application, this could’ve been a goldmine for an adversary!

The Fix

  • Developers should always follow the principle of ​least privilege ​when configuring the IAM roles associated with AWS Cognito authenticated and unauthenticated roles.
  • The application already knows the bucket names to use to read and write information, there is no need for the application to list all buckets in the AWS account.
  • Remember object level permissions. Not all users should be able to read all objects in the S3 bucket.

Andrés Riancho’s white-paper and NotSoSecure’s blog helped immensely. A huge thanks to them!

--

--