In recent years, there has been a significant rise in the adoption of cloud services as they are being perceived as convenient, powerful, and relatively secure.
The security of cloud services will always need to follow the Shared Responsibility model whereby it is the customer's responsibility to ensure that their cloud environment is properly configured.
In this article, we will be exploring two misconfigurations of AWS Cognito which, sometimes, may be overlooked by both customers and pentesters alike. You will learn not only how attackers can exploit them but also how to remediate them.
Before we dive into the vulnerabilities, what is exactly AWS Cognito and what role does it have in the AWS Cloud ecosystem?
According to the vendor, "Amazon Cognito provides authentication, authorisation, and user management for your web and mobile apps" but what does that mean exactly? Let’s see it in practice to understand it better.
The following diagram, taken from Amazon, represents a typical scenario for Amazon Cognito where the goal is to authenticate a user and to grant them access to a specific web resource. For authentication and authorisation purposes, AWS Cognito offers two different methods for different scenarios which are the use of User and Identity Pools.
A user pool can be imagined as a user directory in Amazon Cognito. Users and groups can be onboarded to a User Pool in order to give them access to a bespoke web application either directly (with the credentials defined in the user pool) or via social identity providers like Apple, Google, etc through the use of the SAML protocol.
During a recent web application test, a misconfiguration was identified in the permission configuration of a custom attribute which allowed us to successfully perform a privilege escalation attack. Based on further investigation in our AWS lab, it was possible to determine that the misconfiguration was due to AWS default settings and for this reason, it is believed that many other companies and services may be affected by the same issue. Let’s start by understanding what the attributes are and why they are important.
During the onboarding of a user in a User Pool, it is possible to specify which attributes should be enabled for that user and what their permissions should be (e.g. read/write permissions). The only attributes for which it is not possible to set Write permissions are the ones related to MFA policies and those involving the sign-up and verification process for a given user. For example, the “email_verified” attribute, which can have a “yes” or “no” value, cannot be modified as to allow write access for security reasons:
It is important to notice that, whilst the attribute's name/type are defined on a User Pool level, their permissions can be configured on App Client level meaning that different applications can be configured with a different set of permissions regarding attributes. This is also very important to know as all users belonging to the same App Client, which is the most common scenario, will have the same set of attribute permissions.
Most importantly, the AWS Cognito User Pool allows users to add custom attributes. This is performed by AWS prepending “custom:” to the name that was chosen for the attribute. Let’s consider the following as an example of a vulnerable web application.
Let’s suppose that a fictitious Trading web application was configured in a way to allow certain actions based on a user-role setting. As such option is not natively supported in User Pools, it would be possible to define a custom attribute that will include the details of the user’s role and some backend code responsible for parsing the token and the role associated to a specific user. Let’s suppose that the roles available in the web application are only the following:
- Read only - The user only has read recent trading transactions
- Admin - The user has admin rights and can read but also issue new trading transactions
A viable option would be adding a custom attribute to a user containing the name of the user role. What could possibly go wrong, right?
According to our research, the permissions set for any custom attribute during the creation of a User Pool is Read and Write by default.
For this reason, we have reached out to the AWS Security Team which recognised our submission and proposed a change in their document to better reflect the Cognito behaviour.
Please refer to the following videos for a quick overview of AWS Console’s new/old UI default settings when creating a user pool:
AWS Cognito attributes - New UI default settings
AWS Cognito attributes - Old UI default settings
However, if a custom role is added after a User Pool has been created, it would have neither Read or Write permissions by default:
In this example, let’s suppose that the user John was given read-only access to the Trading platform. This is the type of information the user will be able to get about the current AWS Cognito context by issuing the following AWS CLI command:
aws cognito-idp get-user --region us-east-1 --access-token <TOKEN>
Although in our example the name of the custom role is already known, in real-life scenarios it might not be possible to correctly guess the name of a role or more generally of a user attribute that the attacker intends to update.
In a vulnerable scenario, this would translate to any read-only user being able to successfully update their own role by issuing a command like the following:
aws cognito-idp update-user-attributes --region us-east-1 --access-token <TOKEN> --user-attributes Name="custom:role",Value="admin-role"
If successful, the custom user attribute would now reflect the change with the updated role:
Edit - 27 Mar 2023:
Since AWS Cognito tokens can sometimes be overlooked and mistaken for generic JWT Tokens, we have developed a simple yet effective passive Burp Suite Pro extension to detect whether a JWT token is actually a Cognito session token. To download the extension and for more additional information please refer to the following link:
To mitigate the issue, it would be recommended to ensure that the permissions being set on Cognito users are matching both business and security requirements.
In particular, to update a custom attribute permission’s setting it is possible to follow this procedure (in the new AWS web UI):
- Browse to the Cognito section of AWS
- Select the current User Pool and click on the App client section
- Expand the Edit attribute read and write permission and proceed to change them as shown in the following screenshot
In contrast to User Pools ,Cognito Identity Pool IDs are utilised to allow users to fetch temporary AWS credentials. If the AWS Credentials obtained have excessive AWS permissions, it might be possible for an unauthenticated user to access sensitive AWS services.
In reality though, how commonly could this be exploited? I would argue that it would not be uncommon for a web application to leak its pool id which should look like the following:
Although the leakage of such information may not be an issue in itself, it would be recommended to avoid it since it may introduce unnecessary risk especially if the AWS credentials’ permissions are not following the principle of least privilege.
To derive the set of AWS credentials from a PoolID, an attacker would only need a simple Boto3 script to generate them. For readers that are not experts in AWS, Boto3 is the official Python SDK for interacting with an AWS environment.
Once the credentials have been generated, they can be stored under the ~/.aws/credentials folder as shown in the following screenshot:
After this last step, an attacker would face no constraints apart from restrictive permissions from interacting with the AWS environment via the AWS CLI SDK:
The acceleration of adoption of Cloud environments as the industry’s “gold standard” for handling companies’ most sensitive assets is just getting started and it does not hint at stopping any time soon. On-premise migrations to PaaS services, the ever-availability of services such as corporate emails, cloud-managed security services such as EDR, zero trust and conditional policies are redefining the current landscape in what can be considered a "threat" and how the modern threat actor operates. In the current Cloud multi-verse, it may sometimes be a daunting task to be prepared to new emerging threats that do not follow the "on-premise" paradigm.
Even if it was for only a fraction of this, we wanted to raise awareness about some of the emerging threats that attackers are nowadays taking advantage of and with a simple but practical approach. This way, whether it is for your next offensive Security Assessment or to better defend against new attacks, we hope you found something you didn't know about. If that is the case, it means it has served its purpose as awareness is sometimes the invisible ingredient for better tackling new threats.