Privilege Escalation from Pod to Node
The most important idea in the KCSA threat model is that create pods is a privilege escalation primitive. This guide grants a ServiceAccount nothing but Pod creation, proves it cannot read Secrets, and then reads one through the node instead - before making the same grant useless with a namespace label.
Kubernetes Threat Model Guide 29 of 42 Advanced
- Kubernetesapiserver v1.36.4, kubelet v1.36.3
- Runtimecontainerd 2.2.6
- CNICilium 1.18.1
- Built withkubeadm v1.36.3
- TimeAbout 20 min
The escalation uses hostPath with path: /, which baseline Pod Security forbids. Nothing here is a vulnerability - every step is a documented field used as designed.
- Host kernel7.0.0-29
| Server Name | IP Address | OS | Roles | CPU | RAM | HDD |
|---|---|---|---|---|---|---|
| CKA5001 | 192.168.0.41 | Ubuntu 26.04 LTS | Control Plane Node (tainted NoSchedule) | 2 Core | 4 GB | 50 GB |
| CKA5001-NODE01 | 192.168.0.42 | Ubuntu 26.04 LTS | Worker Node | 2 Core | 4 GB | 50 GB |
| CKA5001-NODE02 | 192.168.0.43 | Ubuntu 26.04 LTS | Worker Node | 2 Core | 4 GB | 50 GB |
| CKA5001-NODE03 | 192.168.0.44 | Ubuntu 26.04 LTS | Worker Node | 2 Core | 4 GB | 50 GB |
This guide includes
Use this because it is the most important idea in the KCSA threat model: create pods is effectively node access. This matters because an identity narrow enough to look harmless can mount the host filesystem and read anything on that node.
- building a deployer identity that may create Pods and may not read Secrets
- putting a victim Secret in another namespace, mounted by somebody else's Pod
- creating one Pod as that deployer, with
--as=so the permissions are exactly theirs - reading what it found once it was on the node
- ending the entire escalation with one namespace label
Before you start
-
A ServiceAccount that may create Pods and may not read Secrets
-
A victim Secret, mounted by somebody else's Pod
-
The escalation: one Pod, created as the deployer
-
What it found on the node
-
The control that ends it, in one label