Cross-tenant Cloud Function compromise via storage bucket squatting

Date: Sep 20, 2020 01:33AM

Google Cloud Functions accept code from users and build this code into a deployable container via Cloud Build. Before building, the code is uploaded to a cloud storage bucket whose name matches the format gcf-sources-<numeric-project-id>-<location> (e.g. gcf-sources-928967777810-us-central1).

When uploading user provided code to the gcf-sources bucket, the backend does not verify that the bucket owner matches the current project. As the bucket name is not a domain, nor does it contain the word Google, an attacker can register these scratch buckets for any target accounts.

Attack Scenario

An attacker can effectively "squat" bucket names for projects they expect might use Cloud Functions at some point in the future. Requirements for the attack:

  1. attacker must know the project ID of their victim
  2. victim must have no cloud functions in the target region
  3. attacker must create a bucket matching gcf-sources-<numeric-project-id>-<location>
  4. attacker must grant access to the bucket (e.g. grant allUsers the storage.buckets.list, storage.objects.get, storage.objects.list, and storage.objects.create permissions)

When the victim creates a Cloud Function, the backend will upload their code archive to the attacker-controlled bucket. The attacker can then download and view code from the victim account. Additionally, their Cloud Function will proceed with creation and work as expected.

Finally, the attacker can modify the victim's function-source.zip archive and edit the code of the victim's cloud function. This will not be deployed unless the victim performs a new deployment through the UI and does not notice the modification. In larger Cloud Functions, this should be trivial by injecting code either a malicious dependency or a snippet into a large file.

Attacker setup

Create bucket matching victim numeric project ID:

gsutil mb gs://gcf-sources-928967777810-us-central1
gsutil iam ch "allUsers:admin" gs://gcf-sources-928967777810-us-central1

Wait until victim uploads code. Upload backdoor'd version of victim's code to attacker bucket.

Victim setup

Deploy any Cloud Function.

Screenshot of the victim Cloud Function correctly deployed: Screenshot of the victim Cloud Function correctly deployed

Result

Screenshot of the attacker bucket showing source code from the victim: Screenshot of the attacker bucket showing source code from the victim

Screenshot of the victim's edit view after the attacker "backdoors" the function-source.zip file: Screenshot of the victim's edit view after the attacker "backdoors" the function-source.zip file

Screenshot showing simple shell as a result of backdoor'd function: Screenshot showing simple shell as a result of backdoor'd function

Timeline