Sponsored

GCP - Container Privesc

container

container.clusters.get

With container.clusters.get (and, for credential retrieval, container.clusters.getCredentials), you can gather credentials for the Kubernetes cluster using something like:[1][2]

Get Kubernetes cluster credentials
gcloud container clusters get-credentials <cluster_name> --zone <zone>

Without extra Kubernetes permissions, the resulting identity might only be able to list some resources, but those credentials are still useful for finding misconfigurations in the environment. The Kubernetes API continues to enforce the identity's available authorization.[1][5]

[!NOTE] Note that Kubernetes clusters might be configured to be private or restricted with authorized networks; that can disallow access to the Kubernetes API server from the Internet.[4]

If you don't have this permission, you may still access the cluster if you obtained its endpoint, CA, and a valid Kubernetes credential through another authorized path. You then need to create your own kubectl config file with the cluster info. A kubeconfig separates cluster, user, and context records; a generated one looks like this:[3]

Example kubectl config file for GKE cluster
apiVersion: v1
clusters:
  - cluster:
      certificate-authority-data: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUVMRENDQXBTZ0F3SUJBZ0lRRzNaQmJTSVlzeVRPR1FYODRyNDF3REFOQmdrcWhraUc5dzBCQVFzRkFEQXYKTVMwd0t3WURWUVFERXlRMk9UQXhZVEZoWlMweE56ZGxMVFF5TkdZdE9HVmhOaTAzWVdFM01qVmhNR05tTkdFdwpJQmNOTWpJeE1qQTBNakl4T1RJMFdoZ1BNakExTWpFeE1qWXlNekU1TWpSYU1DOHhMVEFyQmdOVkJBTVRKRFk1Ck1ERmhNV0ZsTFRFM04yVXROREkwWmkwNFpXRTJMVGRoWVRjeU5XRXdZMlkwWVRDQ0FhSXdEUVlKS29aSWh2Y04KQVFFQkJRQURnZ0dQQURDQ0FZb0NnZ0dCQU00TWhGemJ3Y3VEQXhiNGt5WndrNEdGNXRHaTZmb0pydExUWkI4Rgo5TDM4a2V2SUVWTHpqVmtoSklpNllnSHg4SytBUHl4RHJQaEhXMk5PczFNMmpyUXJLSHV6M0dXUEtRUmtUWElRClBoMy9MMDVtbURwRGxQK3hKdzI2SFFqdkE2Zy84MFNLakZjRXdKRVhZbkNMMy8yaFBFMzdxN3hZbktwTWdKVWYKVnoxOVhwNEhvbURvOEhUN2JXUTJKWTVESVZPTWNpbDhkdDZQd3FUYmlLNjJoQzNRTHozNzNIbFZxaiszNy90RgpmMmVwUUdFOG90a0VVOFlHQ3FsRTdzaVllWEFqbUQ4bFZENVc5dk1RNXJ0TW8vRHBTVGNxRVZUSzJQWk1rc0hyCmMwbGVPTS9LeXhnaS93TlBRdW5oQ2hnRUJIZTVzRmNxdmRLQ1pmUFovZVI1Qk0vc0w1WFNmTE9sWWJLa2xFL1YKNFBLNHRMVmpiYVg1VU9zMUZIVXMrL3IyL1BKQ2hJTkRaVTV2VjU0L1c5NWk4RnJZaUpEYUVGN0pveXJvUGNuMwpmTmNjQ2x1eGpOY1NsZ01ISGZKRzZqb0FXLzB0b2U3ek05RHlQOFh3NW44Zm5lQm5aVTFnYXNKREZIYVlZbXpGCitoQzFETmVaWXNibWNxOGVPVG9LOFBKRjZ3SURBUUFCbzBJd1FEQU9CZ05WSFE4QkFmOEVCQU1DQWdRd0R3WUQKVlIwVEFRSC9CQVV3QXdFQi96QWRCZ05WSFE0RUZnUVU5UkhvQXlxY3RWSDVIcmhQZ1BjYzF6Sm9kWFV3RFFZSgpLb1pJaHZjTkFRRUxCUUFEZ2dHQkFLbnp3VEx0QlJBVE1KRVB4TlBNbmU2UUNqZDJZTDgxcC9oeVc1eWpYb2w5CllkMTRRNFVlVUJJVXI0QmJadzl0LzRBQ3ZlYUttVENaRCswZ2wyNXVzNzB3VlFvZCtleVhEK2I1RFBwUUR3Z1gKbkJLcFFCY1NEMkpvZ29tT3M3U1lPdWVQUHNrODVvdWEwREpXLytQRkY1WU5ublc3Z1VLT2hNZEtKcnhuYUVGZAprVVl1TVdPT0d4U29qVndmNUsyOVNCbGJ5YXhDNS9tOWkxSUtXV2piWnZPN0s4TTlYLytkcDVSMVJobDZOSVNqCi91SmQ3TDF2R0crSjNlSjZneGs4U2g2L28yRnhxZWFNdDladWw4MFk4STBZaGxXVmlnSFMwZmVBUU1NSzUrNzkKNmozOWtTZHFBYlhPaUVOMzduOWp2dVlNN1ZvQzlNUk1oYUNyQVNhR2ZqWEhtQThCdlIyQW5iQThTVGpQKzlSMQp6VWRpK3dsZ0V4bnFvVFpBcUVHRktuUTlQcjZDaDYvR0xWWStqYXhuR3lyUHFPYlpNZTVXUDFOUGs4NkxHSlhCCjc1elFvanEyRUpxanBNSjgxT0gzSkxOeXRTdmt4UDFwYklxTzV4QUV0OWxRMjh4N28vbnRuaWh1WmR6M0lCRU8KODdjMDdPRGxYNUJQd0hIdzZtKzZjUT09Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K
      server: https://34.123.141.28
    name: gke_security-devbox_us-central1_autopilot-cluster-1
contexts:
  - context:
      cluster: gke_security-devbox_us-central1_autopilot-cluster-1
      user: gke_security-devbox_us-central1_autopilot-cluster-1
    name: gke_security-devbox_us-central1_autopilot-cluster-1
current-context: gke_security-devbox_us-central1_autopilot-cluster-1
kind: Config
preferences: {}
users:
  - name: gke_security-devbox_us-central1_autopilot-cluster-1
    user:
      auth-provider:
        config:
          access-token: <access token>
          cmd-args: config config-helper --format=json
          cmd-path: gcloud
          expiry: "2022-12-06T01:13:11Z"
          expiry-key: "{.credential.token_expiry}"
          token-key: "{.credential.access_token}"
        name: gcp

container.roles.escalate | container.clusterRoles.escalate

Kubernetes by default prevents principals from being able to create or update Roles and ClusterRoles with more permissions than the ones the principal has. However, a GCP principal with the corresponding container.roles.escalate or container.clusterRoles.escalate permission can create/update Roles/ClusterRoles with more permissions than the ones it holds, effectively bypassing this Kubernetes protection.[1][5][6]

container.roles.create and/or container.roles.update OR container.clusterRoles.create and/or container.clusterRoles.update, respectively, are also necessary to perform those privilege escalation actions.[1]

container.roles.bind | container.clusterRoles.bind

Kubernetes by default prevents principals from being able to create or update RoleBindings and ClusterRoleBindings to give more permissions than the ones the principal has. However, a GCP principal with the corresponding container.roles.bind or container.clusterRoles.bind permission can create/update RoleBindings/ClusterRoleBindings with more permissions than the ones it has, effectively bypassing this Kubernetes protection.[1][5][6]

container.roleBindings.create and/or container.roleBindings.update OR container.clusterRoleBindings.create and/or container.clusterRoleBindings.update, respectively, are also necessary to perform those privilege escalation actions.[1]

container.cronJobs.create | container.cronJobs.update | container.daemonSets.create | container.daemonSets.update | container.deployments.create | container.deployments.update | container.jobs.create | container.jobs.update | container.pods.create | container.pods.update | container.replicaSets.create | container.replicaSets.update | container.replicationControllers.create | container.replicationControllers.update | container.scheduledJobs.create | container.scheduledJobs.update | container.statefulSets.create | container.statefulSets.update

These permissions allow you to create or update a resource with a controllable Pod template. In that template you can specify the ServiceAccount that is attached and the image that is run. If admission policy allows selecting a privileged ServiceAccount and its token is mounted, a malicious image can exfiltrate that ServiceAccount's token to your server and let you act as that identity; this does not automatically grant access to every ServiceAccount.[1][6][9]
For more information check Kubernetes Service Accounts.[9]

As we are in a GCP environment, a Pod may also be able to get the node pool GCP service account from the metadata service and escalate privileges in GCP. On GKE, the Compute Engine default service account is used for nodes by default, but custom node accounts and Workload Identity Federation for GKE change this behavior; verify metadata reachability and the actual node identity before relying on this path.[7][8]

container.secrets.get | container.secrets.list

As explained in this page, with these permissions you can read Kubernetes Secrets in their scope. A kubernetes.io/service-account-token Secret contains a legacy long-lived token for its referenced ServiceAccount; if such a Secret exists and that account is privileged, reading it can let you act as that account. Modern Kubernetes versions normally use short-lived projected TokenRequest tokens and no longer auto-create one token Secret for every ServiceAccount, so this permission does not automatically expose all ServiceAccount tokens.[1][9][10]

container.pods.exec

With this permission you will be able to exec into pods and run commands in their containers. This gives you access to credentials of the Kubernetes ServiceAccounts assigned to pods you can reach, when those credentials are mounted or otherwise available, which may escalate privileges within K8s. On GKE Standard clusters without Workload Identity Federation, a Pod that can reach the underlying metadata service may also steal the GCP Service Account of the node pool, escalating privileges in GCP; Autopilot, Workload Identity, and metadata settings can change this exposure.[1][7][8][9][11]

container.pods.portForward

As explained in this page, these permissions let you access local services running in Pods by forwarding a local port. Those services might allow you to escalate privileges in Kubernetes (and in GCP if you can reach a metadata endpoint or another cloud-credential service).[1][8][12]

container.serviceAccounts.createToken

Because of the name of the permission, it looks like that it will allow you to generate tokens of the K8s ServiceAccounts, so you may be able to privesc to an SA inside Kubernetes. Kubernetes documents TokenRequest on the serviceaccounts/token subresource, but I could not find an official GKE mapping that confirms how this IAM permission authorizes that subresource or whether it can mint tokens for arbitrary ServiceAccounts; test it in a controlled cluster before relying on it.[5][6][13]

container.mutatingWebhookConfigurations.create | container.mutatingWebhookConfigurations.update

These permissions might allow you to escalate privileges in Kubernetes, but more probably, you could abuse them to persist in the cluster. Mutating admission webhooks can inspect admitted objects and change them, so a malicious configuration may provide persistence or privilege escalation depending on its matching rules and backend.[6][13]
For more information follow this link.[13]

References

[!TIP] Learn & practice AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Learn & practice GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Learn & practice Az Hacking: HackTricks Training Azure Red Team Expert (AzRTE)
Browse the full HackTricks Training catalog.

Support HackTricks