the_open_door
coffee
ESEN
← Back to index

Abusing a Kubernetes cluster

When the shell lands inside a Kubernetes pod, escalation stops being an operating-system problem and becomes a cluster-permissions one. The first thing is to find out whether the pod has a service account token mounted, which is the credential used to talk to the API:

ls /var/run/secrets/kubernetes.io/serviceaccount/
cat /var/run/secrets/kubernetes.io/serviceaccount/token

With that token, kubectl answers the only question that matters at first:

kubectl auth can-i --list

If the answer includes pods/exec, you can enter any pod in the namespace; if it includes secrets, you read whatever credentials the cluster stores; and if it allows creating pods, you can request a privileged one that mounts the whole node:

kubectl get secrets -A
kubectl exec -it <pod> -- /bin/sh

A second path skips the API and goes through each node's kubelet, listening on 10250. If it accepts anonymous requests, you list its pods and run code inside them with no credential at all:

kubeletctl --server <ip> pods
kubeletctl --server <ip> scan rce
kubeletctl --server <ip> exec "id" -p <pod> -c <container>

Nuances. A service account token does not imply privilege: many are read-only and scoped to a single namespace. auth can-i --list is what separates the anecdotal pod from the one that leads to the whole cluster.

How to detect it. The kube-apiserver audit log is the main source: selfsubjectaccessreviews calls (the auth can-i --list), get/list on secrets, or create of pods with securityContext.privileged: true or a hostPath mounting the node, all from a service account that normally doesn't do that, are the signature. Inside the pod, reading /var/run/secrets/kubernetes.io/serviceaccount/token by a process other than the app's is caught by a Falco rule. On the node, anonymous requests to the kubelet (10250) and the kubeletctl pattern (pod scan + exec) give away the second path.

Defence. Do not mount the token when the pod does not need it (automountServiceAccountToken: false), keep RBAC minimal per service account, and close 10250 to everything but the control plane.