Rhino Security Labs

Privilege Escalation in
Google Cloud Platform – Part 2 (Non-IAM)

Intro to GCP Privilege Escalation (Continued)

This is a continuation of the 2-part blog series on Privilege Escalation in Google Cloud. 

If you haven’t already read Part 1 of this blog series, check it out here.

This part of the blog focuses on non-IAM service privilege escalation methods. For example, none of these permissions belong to the “IAM” family, like most of the methods in Part 1, such as iam.serviceAccounts.update. Since we’ve already touched on all the background information, let’s dive into it.

Non-IAM Methods

orgpolicy.policy.set

This method does not necessarily grant you more IAM permissions, but it may disable some barriers that are preventing certain actions. For example, there is an Organization Policy constraint named appengine.disableCodeDownload that prevents App Engine source code from being downloaded by users of the project. If this was enabled, you would not be able to download that source code, but you could use orgpolicy.policy.set to disable the constraint and then continue with the source code download.

The screenshot above shows that the appengine.disableCodeDownload constraint is enforced, which means it is preventing us from downloading the source code. Using orgpolicy.policy.set, we can disable that enforcement and then continue on to download the source code.

The exploit script for this method can be found here.

storage.hmacKeys.create

There is a feature of Cloud Storage, “interoperability”, that provides a way for Cloud Storage to interact with storage offerings from other cloud providers, like AWS S3. As part of that, there are HMAC keys that can be created for both Service Accounts and regular users. We can escalate Cloud Storage permissions by creating an HMAC key for a higher-privileged Service Account. 

HMAC keys belonging to your user cannot be accessed through the API and must be accessed through the web console, but what’s nice is that both the access key and secret key are available at any point. This means we could take an existing pair and store them for backup access to the account. HMAC keys belonging to Service Accounts can be accessed through the API, but after creation, you are not able to see the access key and secret again.

The screenshot above shows the simple command from the gsutil CLI to create HMAC keys for a Service Account. Now you just need to use those keys to access the Cloud Storage APIs as the Service Account.

The exploit script for this method can be found here.

serviceusage.apiKeys.create

There is another method of authenticating with GCP APIs known as API keys. By default, they are created with no restrictions, which means they have access to the entire GCP project they were created in. We can capitalize on that fact by creating a new API key that may have more privileges than our own user. There is no official API for this, so a custom HTTP request needs to be sent to https://apikeys.clients6.google.com/ (or https://apikeys.googleapis.com/). This was discovered by monitoring the HTTP requests and responses while browsing the GCP web console. For documentation on the restrictions associated with API keys, visit this link.

The following screenshot shows how you would create an API key in the web console.

With the undocumented API that was discovered, we can also create API keys through the API itself.

The screenshot above shows a POST request being sent to retrieve a new API key for the project.

The exploit script for this method can be found here.

serviceusage.apiKeys.list

Another undocumented API was found for listing API keys that have already been created (this can also be done in the web console). Because you can still see the API key’s value after its creation, we can pull all the API keys in the project.

The screenshot above shows that the request is exactly the same as before, it just is a GET request instead of a POST request. This only shows a single key, but if there were additional keys in the project, those would be listed too.

The exploit script for this method can be found here.

Miscellaneous setIamPolicy API Calls

In addition to all the methods above, simple privilege escalation can be performed by updating IAM policies associated with the target resources. The privilege escalation scanner below checks for these as well.

For an example of what privilege escalation on a specific resource may look like when using setIamPolicy, read our past blog post on Google Storage bucket enumeration and escalation: https://rhinosecuritylabs.com/gcp/google-cloud-platform-gcp-bucket-enumeration/

A few that are worth looking into for privilege escalation are listed here:

  • resourcemanager.organizations.setIamPolicy
    • Attach IAM roles to your user at the Organization level.
  • resourcemanager.folders.setIamPolicy
    • Attach IAM roles to your user at the Folder level.
  • resourcemanager.projects.setIamPolicy
    • Attach IAM roles to your user at the Project level.
  • iam.serviceAccounts.setIamPolicy
    • Attach IAM roles to your user at the Service Account level.
  • cloudfunctions.functions.setIamPolicy
    • Modify the policy of a Cloud Function to allow yourself to invoke it.

As of the time of writing this blog post, the following permissions are all the different IAM policies that you can update:

  • accesscontextmanager.accessPolicies.setIamPolicy
  • accesscontextmanager.policies.setIamPolicy
  • apigee.environments.setIamPolicy
  • artifactregistry.repositories.setIamPolicy
  • automl.datasets.setIamPolicy
  • automl.locations.setIamPolicy
  • automl.models.setIamPolicy
  • bigquery.connections.setIamPolicy
  • bigquery.datasets.setIamPolicy
  • bigtable.instances.setIamPolicy
  • bigtable.tables.setIamPolicy
  • billing.accounts.setIamPolicy
  • binaryauthorization.attestors.setIamPolicy
  • binaryauthorization.policy.setIamPolicy
  • cloudfunctions.functions.setIamPolicy
  • cloudiot.registries.setIamPolicy
  • cloudkms.cryptoKeys.setIamPolicy
  • cloudkms.importJobs.setIamPolicy
  • cloudkms.keyRings.setIamPolicy
  • cloudprivatecatalogproducer.catalogs.setIamPolicy
  • cloudsupport.accounts.setIamPolicy
  • cloudtasks.queues.setIamPolicy
  • compute.disks.setIamPolicy
  • compute.globalOperations.setIamPolicy
  • compute.images.setIamPolicy
  • compute.instanceTemplates.setIamPolicy
  • compute.instances.setIamPolicy
  • compute.licenseCodes.setIamPolicy
  • compute.licenses.setIamPolicy
  • compute.maintenancePolicies.setIamPolicy
  • compute.networkEndpointGroups.setIamPolicy
  • compute.nodeGroups.setIamPolicy
  • compute.nodeTemplates.setIamPolicy
  • compute.regionOperations.setIamPolicy
  • compute.securityPolicies.setIamPolicy
  • compute.snapshots.setIamPolicy
  • compute.subnetworks.setIamPolicy
  • compute.zoneOperations.setIamPolicy
  • datacatalog.categories.setIamPolicy
  • datacatalog.entries.setIamPolicy
  • datacatalog.entryGroups.setIamPolicy
  • datacatalog.tagTemplates.setIamPolicy
  • datacatalog.taxonomies.setIamPolicy
  • datafusion.instances.setIamPolicy
  • dataproc.autoscalingPolicies.setIamPolicy
  • dataproc.clusters.setIamPolicy
  • dataproc.jobs.setIamPolicy
  • dataproc.operations.setIamPolicy
  • dataproc.workflowTemplates.setIamPolicy
  • datastore.databases.setIamPolicy
  • datastore.namespaces.setIamPolicy
  • dns.policies.setIamPolicy
  • genomics.datasets.setIamPolicy
  • gkehub.memberships.setIamPolicy
  • healthcare.datasets.setIamPolicy
  • healthcare.dicomStores.setIamPolicy
  • healthcare.fhirStores.setIamPolicy
  • healthcare.hl7V2Stores.setIamPolicy
  • iam.serviceAccounts.setIamPolicy
  • iap.tunnel.setIamPolicy
  • iap.tunnelInstances.setIamPolicy
  • iap.tunnelZones.setIamPolicy
  • iap.web.setIamPolicy
  • iap.webServiceVersions.setIamPolicy
  • iap.webServices.setIamPolicy
  • iap.webTypes.setIamPolicy
  • managedidentities.domains.setIamPolicy
  • ml.jobs.setIamPolicy
  • ml.models.setIamPolicy
  • ml.studies.setIamPolicy
  • networkmanagement.connectivitytests.setIamPolicy
  • notebooks.environments.setIamPolicy
  • notebooks.instances.setIamPolicy
  • proximitybeacon.beacons.setIamPolicy
  • proximitybeacon.namespaces.setIamPolicy
  • pubsub.snapshots.setIamPolicy
  • pubsub.subscriptions.setIamPolicy
  • pubsub.topics.setIamPolicy
  • resourcemanager.folders.setIamPolicy
  • resourcemanager.organizations.setIamPolicy
  • resourcemanager.projects.setIamPolicy
  • run.services.setIamPolicy
  • runtimeconfig.configs.setIamPolicy
  • runtimeconfig.variables.setIamPolicy
  • runtimeconfig.waiters.setIamPolicy
  • secretmanager.secrets.setIamPolicy
  • securitycenter.sources.setIamPolicy
  • servicebroker.bindings.setIamPolicy
  • servicebroker.catalogs.setIamPolicy
  • servicebroker.instances.setIamPolicy
  • servicedirectory.endpoints.setIamPolicy
  • servicedirectory.namespaces.setIamPolicy
  • servicedirectory.services.setIamPolicy
  • servicemanagement.consumerSettings.setIamPolicy
  • servicemanagement.services.setIamPolicy
  • source.repos.setIamPolicy
  • spanner.backups.setIamPolicy
  • spanner.databases.setIamPolicy
  • spanner.instances.setIamPolicy
  • storage.buckets.setIamPolicy
  • storage.objects.setIamPolicy
  • deploymentmanager.deployments.setIamPolicy

If you have any of those permissions above, you may be able to modify the IAM policy of the specified resource to grant yourself a role on it, thus granting you additional privileges at the resource level.

GCP Privilege Escalation Scanner

Individual exploit scripts are nice, but something more useful is a way to check your own GCP environment for existing privilege escalation paths. We wrote a script to do just that. It enumerates members and their permissions on Organizations, Folders, Projects, and Service Accounts, and reports any privilege escalation methods and/or setIamPolicy permissions. Note that the script even enumerates inherited permissions! For example, this means that all permissions you’re granted at the Organization level will also show up under your permissions for the Project level because permissions are inherited down.

The scanner can be found on the Rhino Github.

Essentially, you just run the enumerate_member_permissions.py script to enumerate members and their permissions, then you run the check_for_privesc.py script to check those permissions for privilege escalation. Note that project Owners and Editors may return a lot of results due to their excessive permissions.

Here is a screenshot of the script running and displaying its output. The enumeration script accepts an access token and will enumerate everything in the target project, while the privilege escalation script parses the enumerated permissions offline to look for privilege escalation. The screenshot shows a few users/groups who are vulnerable to some privilege escalation methods in our test project.

Afterwards, there will be three files with your results:

  1. all_org_folder_proj_sa_permissions.json – All members and their associated privileges
  2. privesc_methods.txt – All detected privilege escalation methods
  3. setIamPolicy_methods.txt – All detected setIamPolicy methods

Note that this script also checks for the privilege escalation methods outlined in Part 1 of this post, so check that out if you haven’t already to get a better understanding of all the methods.

Conclusion

This list (and Part 1) of privilege escalation methods is not a definitive list, because there are likely more methods out there (such as in GKE, which wasn’t touched on in this post). Again, these are not vulnerabilities in GCP, they are vulnerabilities in how you have configured your GCP environment, so it is your responsibility to be aware of these attack vectors and to defend against them. It’s also important to remember that privilege escalation does not necessarily need to pass through the IAM service to be effective. Make sure to follow the principle of least-privilege in your environments to help mitigate these security risks.

As always, feel free to open issues and pull requests on GitHub if you’d like and we will try to get to them to support future usage of these tools. Follow us on Twitter for more GCP releases and blog posts: @RhinoSecurity, @SpenGietz