Written By Rick Alfaro, Neuvik Advanced Assessments Consultant

Intro

On a recent engagement, we were performing a web application assessment for a client. They had recently implemented AWS Cognito as their user directory and wanted to kick the tires a bit. I took on this task from the point of view of someone who had never touched it… because I never had. What I ended up finding was a way to fully take over any other account within this specific web application.

What is AWS Cognito and how is it used?

AWS Cognito provides developers tools to add user sign-up, sign-in, and access control to their web and mobile applications as a service. Cognito offers features like user pools and identity pools. User pools are user directories that handle user registration and authentication. They enable developers to add user sign-up and sign-in flows, as well as social identity providers like Facebook, Google, and Amazon. Identity pools, on the other hand, are used to grant users temporary AWS credentials, allowing them to access other native AWS services.

Cognito sounds great! So, what’s the problem?

There isn’t really a “problem” with AWS Cognito; however, the way it is implemented within an application can end up causing unintended issues. Even with the AWS-provided documentation, implementation issues are still very possible and can be extremely impactful without fully understanding the service and how it is implemented and functions.

Cognito stores attributes for user accounts. Standard attributes include information such as name, email address, and phone number. The stored attributes can also include custom attributes created by application developers and all attributes can be either user-writeable or immutable, which means that they cannot be modified. Once these attributes are stored within Cognito they can then be used to map to user accounts on the applications backend.

The root of the possible disconnect between Cognito and an application’s backend stems from the fact that the Cognito attributes are also case-sensitive. This means that Cognito sees the difference between the “good@user.com” email address and the “GOOD@user.com” email address, but does the application’s backend see that difference? To piggyback on that question, is the application backend using a user-writeable attribute to map to user accounts?

"Master of Disguise" meme of a dog hiding among ducks


TLDR

See the diagram below as a quick breakdown of what’s to come.

Graphic of account takeover in AWS Cognito



What did we actually see and do?

To provide context, I will walk through what we came across during an engagement. We created two accounts, a “Good User” and an “Evil User”. In our walkthrough, we will use “good@user.com” and “bad@user.com” as email addresses. The reason for this was that we needed to be an authenticated user to have the ability to write to our attributes in Cognito and we had to have the knowledge of another user’s email address. This “good” user’s email address would be the target of the account takeover.

When logging into an application using Cognito, users are issued 3 different tokens by AWS: AccessToken, IdToken, and RefreshToken. The AccessToken is the token you can use in the AWS CLI to perform tasks in AWS, including viewing and editing attributes stored in Cognito. We used the following AWS CLI command to view our user’s current attributes.

aws --no-verify-ssl cognito-idp get-user --region us-east-1 --access-token [access token from cookie]


You can expect a successful run of this command to provide a listing of attributes stored in Cognito for the account you are logged in as.

Successful run of the command above providing a listing of attributes stored in Cognito

This access was used to edit the user-writeable “email” attribute in Cognito using the AWS CLI. This functionality makes sense to give users since the application developers wanted users to be able to update their email addresses themselves. The “email” attribute was overwritten with a capitalized version of the known victim’s email address, “GOOD@user.com”.

aws --no-verify-ssl cognito-idp update-user-attributes --region us-east-1 --access-token [access token from cookie] --user-attributes 'Name=email,Value=GOOD@user.com'

You can expect a successful run of this command to echo back a sanitized listing of edited attributes:

Code delivery details list echoing back a sanitized listing of edited attributes

In a backend system that is case-sensitive and properly maps accounts, this would not be an issue; however, on this engagement, the backend did not. To this application’s backend, the “email” attribute was case-insensitive and used as the primary key for mapping to the proper account. With the new capitalized version of “GOOD@user.com” was saved as the “email” attribute for the “Evil User” the stage was set for the account takeover.

The “Evil User” account would then log out and log back in using “GOOD@user.com” and the “Evil” account’s original password. When logged back in, the “Evil User” was now welcomed as the “Good User” and had the ability to do everything that comes with being the “Good User”.

Ok, got it. So, what does that mean?

I have to make a note that this technique is not novel but can be very impactful and easily-overlooked. This issue would allow an authenticated user to fully take over the account of another authenticated user. In the web application we were testing, there was only 1 role, the role of a “user.” However, if administrative accounts were present in the application, the evil user would only need to know the email address of an admin account to take it over.

How bad is this?

As of this date, AWS has listed a total of 88 customer use cases found here.


It’s difficult to say how widespread Cognito adoption actually is, as we would have to create accounts and monitor login traffic for every application we suspected of using Cognito. There may be more effective methods for fingerprinting Cognito use in an application out there but that is a topic I have yet to really dive into. I believe that it is safe to say that there are many more applications using Cognito given the ease of its use when you are already part of the AWS ecosystem. With AWS still leading in terms of market share, this amount of vulnerable applications can be very significant.


There are definitely mitigations that would probably keep this kind of account takeover in check including forcing multi-factor authentication and having your applications backend use an immutable attribute to map accounts. I can’t say for certain that case-sensitivity in your application’s backend would help mitigate risk however, given that there may still be methods for obfuscating the user-writeable string being used in the email attribute.

So where does all this leave us?

Given what this could allow a malicious user with a method to not only take over other accounts but also a possible means for privilege escalation, I would say this is a high impact issue. The good news is that there are already popular and easily implemented mitigations. This issue is also already incorporated into a popular AWS exploitation framework released by Rhino Security, https://github.com/RhinoSecurityLabs/pacu, and is testable using the recently released module “Cognito_Enum”. I would definitely recommend that all organizations and testers consider this to be a primary issue to check for whenever Cognito is used.

References


Acknowledgements

I want to make sure to thank Moses Frost for helping me dive into the inner workings of the application’s backend to figure out how things were breaking. You can catch Moses teaching the Cloud Penetration Testing course for SANS. I also want to thank Brandon Evans for providing the awesome diagram in the TLDR. You can catch him teaching about issues like this in the Cloud Security Controls and Mitigations course for SANS. Last but not least, I want to thank Lauritz Holtman for the original research and writeup.

About the Author:

Rick Alfaro

Rick is an AWS security specialist and an Advanced Assessments Consultant at Neuvik. His former work as a Red Team engineer honed his penetration skills on cloud infrastructure (I.e., AWS, Azure, GCP), which he provides to Neuvik’s clients as part of our Advanced Assessments offering. Previously, Rick was a part of IBM’s X-Force Red running both Red and Purple team engagements, and prior to that was a Sergeant in the US Marine Corp, receiving a US Navy and Marine Corps Achievement Medal.