Aqua Blog

AWS CDK Risk: Exploiting a Missing S3 Bucket Allowed Account Takeover

AWS CDK Risk: Exploiting a Missing S3 Bucket Allowed Account Takeover

In June 2024, we uncovered a security issue related to the AWS Cloud Development Kit (CDK), an open-source project. This discovery adds to the six other vulnerabilities we discovered within AWS services.  The impact of this issue could, in certain scenarios (outlined in the blog), allow an attacker to gain administrative access to a target AWS account, resulting in a full account takeover. 

Our research, covering over 38,000 well-known account IDs, identified several cases where AWS CDK users were susceptible to this attack vector due to the manual deletion of their deployment artifact S3 bucket(s). AWS later confirmed that approximately 1% of CDK users were susceptible to this security issue. 

This blog post expands on the findings from our previous research, “Bucket Monopoly”, and examines how the techniques from our previous research are applicable to open-source projects. 

We reported this security issue to AWS, and they promptly addressed it by ensuring that assets are only uploaded to buckets within the user’s account. 

However, user action is required if you’ve used CDK version v2.148.1 or earlier. 

For detailed mitigation steps, please refer to the Mitigation section of this blog. 

AWS directly notified potentially affected customers and added messages to the CLI terminal, alerting users to upgrade their bootstrap resources. 

We appreciate AWS security team’s collaboration on this matter. 

Disclosure timeline: 

  • 27 June 2024 – Reported the security issue in CDK to the AWS security team. 
  • 27 June 2024 – AWS acknowledged the report and began investigating the issue. 
  • 11 July 2024 – A pull request was opened to fix the issue by adding a condition to the bootstrap file-publish role, ensuring it only trusts buckets within the user’s account. 
  • 09 August 2024 – AWS confirmed the completion of their remediation efforts and updated the documentation to explicitly call out customizing bootstrapping resources. 
  • 15 October 2024 – AWS notified impacted customers. 
  • 16 October 2024 – AWS added messages to the CLI terminal, alerting users to upgrade their bootstrap resources. 

Understanding AWS CDK and Infrastructure as code (IaC) 

Infrastructure as Code (IaC) is a way to manage and provision computing infrastructure by using code instead of manually configuring physical hardware. This approach allows you to define essential infrastructure components, such as servers, databases, and networks through machine-readable files. 

The AWS Cloud Development Kit (CDK) is a framework that empowers developers to define cloud infrastructure using familiar programming languages like Python, TypeScript, or JavaScript. CDK translates this code into configurations that AWS, can interpret and deploy efficiently. 

In other words, AWS CDK makes building and managing cloud infrastructure more intuitive by letting you write it in the same programming languages you use for your applications. This means you can create, customize, and deploy your cloud resources all from your codebase. 

The discovery 

In our previous blog, we labeled the term shadow resources, which are resources that are created by service/application and the user is unaware of them. In this instance, AWS CDK users are made aware of the default resources created during the bootstrapping process. 

AWS CDK bootstrap 

To use the AWS Cloud Development Kit (AWS CDK), users need to bootstrap their environment to prepare it for CDK stack deployments. 

This is done using the cdk bootstrap command. 

When bootstrapping the CDK, it deploys a CloudFormation template file that automatically creates essential infrastructure components, including IAM roles, policies, an S3 bucket, and more – all of which are used by the CDK during deployments. 

This one-time setup ensures that your CDK applications have the necessary permissions and resources to function properly. 

For example, the bootstrapping process creates several IAM roles, including: 

  • FilePublishingRole: This role grants permission to interact with the bootstrapped S3 bucket, including uploading and deleting assets. It is assumed by CDK during deployments. 
The bootstrap process assigns the CloudFormationExecutionRole with Administrator access by default

The bootstrap process assigns the CloudFormationExecutionRole with Administrator access by default

Additional IAM roles are created during the bootstrap process, which you can explore in more detail here. 

An example of resources created during the CDK bootstrap

An example of resources created during the CDK bootstrap

The naming pattern of resources created by AWS CDK follows the following structure: cdk-{Qualifier}-{Description}-{Account-ID}-{Region} 

  1. Qualifier: A unique, nine-character string value. By default, this value is hnb659fds. However, users can customize this value by specifying a qualifier during the bootstrapping process. For example: cdk bootstrap --qualifier abcde0123 
  2. Description: A short description of the resource. For example, cfn-exec-role for the CloudFormationExecutionRole, or file-publishing-role for the FilePublishingRole. 
  3. Account-ID: The AWS account ID of the environment. 
  4. Region: The AWS Region of the environment. 

For instance, if your AWS account ID is 123456789012, your current region is us-east-1, and you used the default CDK bootstrap command (meaning the qualifier for your resources will be hnb659fds), the physical ID of the CloudFormationExecutionRole IAM role would be:  cdk-hnb659fds-cfn-exec-role-123456789012-us-east-1. 

CDK staging S3 bucket as shadow resource: 

During the CDK bootstrap process, AWS CDK creates a staging S3 bucket to store deployment assets, such as CloudFormation templates and other resources. These assets are first generated and saved locally in the cdk.out directory, after which they are uploaded to the S3 staging bucket for use during the deployment process. This ensures that CloudFormation has access to the necessary resources for stack deployment.  

The staging bucket follows this naming pattern: cdk-{qualifier}-assets-{account-ID}-{Region}. 

Since many users run the cdk bootstrap command without customizing the qualifier, the S3 bucket naming pattern of the staging bucket becomes predictable. This is because the default value for the bucket name qualifier is set to hnb659fds, making it easier to anticipate the bucket’s name. 

The structure of the bucket name created by CDK bootstrap

The structure of the bucket name created by CDK bootstrap

As a result, predicting someone else’s CDK staging bucket name only requires knowing their AWS Account-ID and the Region where the CDK was deployed. 

Since the Prefix is always cdk, the Qualifier is by default hnb659fds, and assets is a constant string in the bucket name, the only variables that change are the Account ID and the Region. 

A quick GitHub search reveals thousands of instances where users have used the default qualifier in the bootstrap process to set up their environments. 

Since the bucket name is predictable when a user using the default CDK bootstrap command, and S3 bucket names are globally unique across all AWS accounts, it is possible for an attacker to perform “S3 Bucket Namesquatting” or “Bucket Sniping”. 

This allows the attacker to claim another user’s CDK staging S3 bucket if it does not already exist. 

AWS CDK risk: from partial DoS to account takeover 

If a user hasn’t yet used AWS CDK, an attacker could exploit this by creating the staging S3 bucket that the CDK bootstrap process would attempt to generate. 

The only information the attacker needs is the target’s account ID.  

With that, the attacker can create the staging S3 bucket for the CDK across all AWS regions, as the region name forms part of the staging bucket’s name. 

Furthermore, it’s common for users not to set a custom qualifier, so the default qualifier hnb659fds is often used by the CDK bootstrap process, making the bucket name predictable. 

If the attacker sets up the bucket ahead of time, when the user later tries to bootstrap the CDK from a specific region, they will encounter an error during the process because the CDK bucket that the bootstrap process attempts to create already exists. The documentation advises selecting a non-default qualifier. 

The CDK bootstrap process of the target account fails with an error because the S3 bucket has already been claimed by an attacker

The CDK bootstrap process of the target account fails with an error because the S3 bucket has already been claimed by an attacker

This creates a partial “DoS” scenario, which the victim can easily resolve by simply modifying the qualifier to successfully complete the bootstrap process. 

CDK account takeover 

If there is a scenario where the victim’s CDK both writes and reads data, such as a CloudFormation template, to and from an attacker-controlled staging S3 bucket, the security implications would be severe. 

This could allow the attacker to manipulate the data and execute malicious actions within the victim’s AWS account. 

As a reminder, the CDK staging bucket contains CloudFormation templates. If an attacker gains access to the CDK staging bucket of other users, these files can be easily tampered with and backdoored, enabling the injection of malicious resources into the victim’s account during deployment. For example, the attacker could add an admin role that they could later assume. 

As mentioned earlier, the deploy role of the CloudFormation service, which is the role CloudFormationExecutionRole in CDK has administrative privileges within the account by default. This means that any CloudFormation template written to the attacker’s S3 bucket by the victim’s CDK would be deployed later with administrative privileges in the victim’s account. This would allow the attacker to create privileged resources. 

During our research, we discovered a scenario where users were susceptible to this exact attack vector.  

What happens if a user deletes the CDK staging S3 bucket after bootstrapping? 

In this scenario, the user had previously used AWS CDK and initiated the CDK bootstrap process, which set up all necessary roles, a staging CDK S3 bucket, and other resources. However, at some point, the user deleted the CDK staging S3 bucket. 

Why does this happen? S3 buckets have quota limits – by default, you can create up to 100 buckets per AWS account. While users can request a quota increase, they sometimes delete buckets to free up space or resources. As we’ll explore later, we’ve found cases where users bootstrapped CDK in the past but subsequently deleted the related S3 staging bucket for various reasons. 

When this occurs, an attacker can exploit this situation. Since the name of the CDK staging bucket is predictable, the attacker can create a new bucket with the same name and wait for the victim to use CDK again, particularly during a cdk deploy 

Since all the necessary roles and resources for CDK were already set up during the previous bootstrap process in the victim’s account, CDK will trust the attacker-controlled bucket and proceed to read and write CloudFormation templates to it. This allows the attacker to manipulate the deployment process and inject malicious resources into the victim’s account. 

Before we dive into the attack workflow, the attacker needs to complete the following steps:  

  1. Claim the bucket with the predictable name of the victim. Allow public access and define a permissive resource-based policy for the bucket. 
  2. Create a Lambda function that will inject a resource or backdoor into a given template file. This Lambda function must be set to trigger by the PutBucketNotification event on the attacker’s bucket. So, whenever a file is dropped into this bucket, the Lambda function will modify it. 
Overview of CDK attack vector

Overview of CDK attack vector

Now we can describe the attack and how we can achieve admin role access on a targeted victim’s account:  

  1. A user initializes CDK in a specific AWS region by running the cdk bootstrap command. This process sets up essential roles, such as the admin and publish roles, and creates an S3 bucket to store assets for CDK, using the naming convention: cdk-{qualifier}-assets-{account_id}-{region}. 
  2. At some point, the user deletes the CDK staging S3 bucket from their account, making the bucket name available for others to claim. 
  3. An attacker creates the victim S3 bucket in their own account using the same predictable name of the targeted account. The attacker sets a permissive resource-based policy, allowing public access, and configures a Lambda function that injects a malicious admin role into any CloudFormation template uploaded to the S3 bucket. 
  4. The user runs cdk deploy. 
  5. Since the necessary roles (e.g., CloudFormationExecutionRole) already exist in the victim’s account and the S3 bucket (now in the attacker’s control) also exist, the victim CDK deploy process sends the CloudFormation template file to the attacker’s S3 bucket. 
  6. The attacker’s Lambda function backdoors the CloudFormation template file written by the victim’s CDK to the compromised S3 bucket, injecting an admin role that the attacker can assume. 
  7. The CloudFormation service in the victim’s account will read and deploy resources from attacker’s bucket.  
  8. As a result, the victim’s CloudFormation service deploys the backdoored template, creating a new admin role that the attacker can assume. This is possible because the CloudFormationExecutionRole, which is used to deploy resources in the victim account, has administrative privileges by default. The attacker can then monitor logs for the victim’s account ID and exploit the injected admin role to gain control of the victim’s account. 

Essentially, we were able to create an admin role in a target account if someone deletes the CDK staging S3 bucket that was created during the bootstrap process and then tries to use CDK again. 

However, we should consider the likelihood of someone deleting the CDK S3 bucket after it has been bootstrapped. To assess this, we can enumerate the instances and identify how many users might be exposed to this attack vector. 

Statistics: How many CDK users are estimated to be vulnerable to this attack vector? 

To evaluate whether this scenario is realistic, we can first check if AWS accounts have previously used CDK by looking for roles created during the CDK bootstrap process. Then, we can determine if the associated CDK staging S3 bucket has been deleted. 

Since AWS CDK resources follow a predictable naming pattern – cdk-{Qualifier}-{Description}-{Account-ID}-{Region} We can focus on roles such as CloudFormationExecutionRole, which is identified by the description cfn-exec-role. 

Regarding the qualifier field in the resource name, as previously mentioned, most users are likely to use the default hnb659fds qualifier instead of customizing it. 

We developed a scanner to perform this task, which operates as follows: We started with a list of 38,000 well-known account IDs. For each account ID, we scanned three popular regions: us-east-1, us-west-2, and eu-west-1. 

First, we checked whether CDK had been used in each account by looking for the existence of one of the CDK rolesCloudFormationExecutionRole (cdk-hnb659fds-cfn-exec-role-<Account_ID>-<Region>) – using the AWS AssumeRole Enumeration Script. 

 Then, we checked if the associated CDK staging S3 bucket (cdk-{qualifier}-assets-{Account_ID}-{Region}) existed or not. 

If the CDK staging bucket is missing but the CDK roles still exist in a specific account and region, the account is considered vulnerable to this technique. An attacker could create the missing staging bucket and wait for the victim to use cdk deploy, causing the CloudFormation template to be written to the attacker’s S3 bucket, which the victim’s CDK and CloudFormation services would then read from and write to, enabling the attacker to exploit this attack vector. 

Out of 38,560 accounts analysed, we identified 782 (2%) that had CDK installed. Among those, 81 accounts (10%) were found to be vulnerable to this attack vector. Additionally, we discovered 100+ vulnerable staging buckets, with some accounts having multiple buckets across different regions, indicating that CDK had been bootstrapped in multiple regions. 

Following our report, AWS confirmed that approximately 1% of CDK users were impacted. 

Mitigations 

After we reported this security issue to AWS, they released several fixes: 

  • Starting from CDK version v2.149.0, AWS added a condition to the bootstrap file-publish role (FilePublishingRole), ensuring it only trusts S3 buckets within the user’s account. This prevents the CDK from pushing data to buckets not owned by the account that initiated the bootstrapping process. 
  • AWS also updated the documentation to highlight the importance of customizing bootstrapping resources, encouraging users to use a custom qualifier instead of the default hnb659fds. 

While the AWS patch resolves the issue for new CDK users, it only applies to new bootstraps.  

This means users who bootstrapped with an older version, such as CDK v2.148.1 (July 11, 2024) or earlier, will remain susceptible, even if they update their CDK. 

AWS added messages to the CLI terminal, alerting users to upgrade their bootstrap resources and directly notified potentially affected customers. 

To mitigate this risk, user action is required: 

  • If you’re using CDK version v2.148.1 or earlier, upgrade to version v2.149.0 or later. After upgrading, re-run the cdk bootstrap command. 
  • Alternatively, instead of upgrading the CDK version, you can apply an IAM policy condition to the FilePublishingRole CDK role: cdk-${Qualifier}-file-publishing-role-${AWS::AccountId}-${AWS::Region}, similar to the AWS patch. 

Avishay Bar has released an open-source tool. This tool scans AWS accounts for this CDK security issue, helping identify current risks and protect against future S3 bucket takeover threats in AWS CDK.

Summary and recommendations  

This blog expands on our recent research, Bucket Monopoly, and reinforces key security best practices that we believe cloud admins should adopt: 

  • Consider your AWS Account ID as a secret: Attackers who know your AWS Account ID, in scenarios like this, can potentially cause severe security incidents. AWS Account IDs should be treated as sensitive information, as similar exploits could arise from knowing an account ID. Keep this data secure and avoid sharing it unless absolutely necessary. 
  • Use Conditions in IAM policies: To prevent a user or service role from accessing an untrusted bucket, define a scoped IAM policy for the role used or assumed by the service. By using the `aws:ResourceAccount` condition or other conditions, you can restrict access based on the bucket owner’s AWS account ID, ensuring that only trusted resources are accessed. 
  • Don’t use predictable S3 bucket naming: As mentioned in our previous research, many open source projects rely on S3 buckets for their operations. Avoid using predictable bucket names. Instead, generate unique hashes or random identifiers per region and account, and incorporate them into your S3 bucket names. This strategy helps protect against attackers preemptively claiming your bucket. 

 

Ofek Itach
Ofek Itach is a Senior Security Researcher at Aqua, specializing in cloud research. His work focuses on identifying and analyzing attack vectors in cloud environments, enhancing security measures for cloud platforms and infrastructures.
Yakir Kadkoda
Yakir Kadkoda is a Lead Security Researcher at Aqua's research team, Team Nautilus. He combines his expertise in vulnerability research with a focus on discovering and analyzing new security threats and attack vectors in cloud native environments, supply chain security, and CI/CD processes. Prior to joining Aqua, Yakir worked as a red teamer. Yakir has shared his deep cybersecurity insights at major industry events like Black Hat and RSA.