Kubetier
← Permission Reference
T1

create pods

Standard pods with no host namespaces still allow serviceAccountName token theft and cloud IMDS access from the pod network.

Where admission controls permit, hostNetwork set to true also bypasses all NetworkPolicies.

API Group
(core)
Scope
namespaced
Audit Level
Request

Escalation Paths

  1. Create a workload with serviceAccountName set to a privileged ServiceAccount in the same namespace.

    Any controller-backed kind works, since the controller creates the pod.

  2. The token is mounted at /var/run/secrets/kubernetes.io/serviceaccount/token, or projected explicitly by the manifest.

  3. Reading it back needs a channel, and the cheapest costs no RBAC at all: the container sends the token outbound itself.

    This is why removing pods/log does not close the path.

  4. With get on pods/log, the container prints the token to stdout and the attacker reads it back.

  5. With get on pods, the container writes the token to /dev/termination-log and the attacker reads terminated.message from the pod's status, with no pods/log needed.

  6. Authenticate as the ServiceAccount with the stolen token.

Additional rights needed:

none
K8s docs ↗
  1. Create a PriorityClass with a very high value and preemptionPolicy PreemptLowerPriority.

  2. Create a pod referencing that PriorityClass, with requests large enough that it only fits by evicting a running pod.

  3. The scheduler preempts a lower-priority victim to make room.

  4. This crosses namespaces, because priority is compared node-wide and the scheduler has no notion of which namespace owns the capacity.

  5. The gain is scheduled capacity taken from another team, not credentials or API permissions.

Additional rights needed:

  • · a PriorityClass preempts nothing until a workload references it, and a controller-backed workload verb can stand in for create pods

K8s docs ↗
  1. An IAM role must already exist whose trust policy names the cluster OIDC provider and conditions on sub system:serviceaccount:<namespace>:<name> plus aud sts.amazonaws.com

  2. The IAM role trusts the cluster OIDC provider and evaluates whatever token claims its trust policy names, most commonly the subject and the audience

  3. Those conditions are policy specific rather than built into IRSA, and the ServiceAccount annotation takes no part in the authorization decision

  4. A pod running under that ServiceAccount, projecting a serviceAccountToken with audience sts.amazonaws.com, receives an assertion STS accepts

  5. AssumeRoleWithWebIdentity is unauthenticated, so the exchange needs no AWS credentials and returns whatever the role itself grants

  6. create serviceaccounts/token mints the same assertion with no pod involved, and the credentials work from outside the cluster

  7. The eks.amazonaws.com/role-arn annotation only drives the pod-identity-webhook, which injects at admission and so never reaches running pods

  8. That annotation is also the discovery path, since it names the role and the ServiceAccount it trusts, and reading it needs only list serviceaccounts

Additional rights needed:

none

Note:

Not a defect and nothing to patch.

The IAM trust policy is the anchor, so an audit that looks only for the eks.amazonaws.com/role-arn annotation misses every variant that bypasses the webhook.

K8s docs ↗
  1. A federated identity credential must already exist on the managed identity, naming the cluster OIDC issuer and system:serviceaccount:<namespace>:<name> as its subject

  2. Entra matches that credential on issuer and subject alone, so the ServiceAccount name is the whole secret and no annotation takes part in the trust decision

  3. A pod running under that ServiceAccount, projecting a serviceAccountToken with audience api://AzureADTokenExchange, receives an assertion Entra accepts

  4. Exchanging the assertion returns an access token for the managed identity, carrying every Azure role assigned to it

  5. create serviceaccounts/token mints the same assertion with no pod involved, and the token it buys is usable from outside the cluster

  6. The pod label azure.workload.identity/use and the ServiceAccount client-id annotation only drive the addon webhook, a convenience for applications rather than the control

Additional rights needed:

none

Note:

Not a defect and nothing to patch.

The federated credential is the trust anchor, so an audit that looks only for the azure.workload.identity annotation misses every variant that does not use the addon webhook.

K8s docs ↗
  1. Create a hostPath PersistentVolume mounting node / (or use an existing one via a PVC)

  2. Claim the volume from an otherwise restricted-compliant pod.

    PSA does not inspect the PV behind a PVC, so even the restricted profile does not block it

  3. Read or write arbitrary node filesystem paths.

    This is T0 on control plane nodes

  4. Or set the PV's claimRef to a victim's PVC name and namespace, including one not yet created, so the victim's future workload silently binds to attacker-controlled storage

Additional rights needed:

CVE-2021-25741↗

Note:

The base hostPath PV path is not version-patched, and Pod Security Admission does not block it at any profile.

PSA checks the pod's own volume types, never the PV behind a PVC.

runAsNonRoot only limits which host files are readable, not the mount.

CVE-2021-25741 is a separate, patched issue.

K8s docs ↗
  1. Create a pod with spec.hostNetwork set to true.

  2. The pod shares the node's network namespace and uses the node's address, so it is no longer on the pod network.

  3. NetworkPolicy selects pods by their pod-network identity, so an ordinary namespace policy does not constrain it.

    Some CNIs provide a separate host-level firewall that can.

  4. Node-local services become reachable, including the kubelet on 10250 and anything bound to the node's loopback or link-local addresses.

  5. The dependable control is Pod Security Admission at baseline or restricted, which rejects spec.hostNetwork at admission.

Additional rights needed:

none

Note:

Namespace NetworkPolicy does not select host-network pods.

Whether host traffic can be policed at all is a property of the CNI, not of Kubernetes.

The effective control is Pod Security Admission at baseline or restricted, which rejects spec.hostNetwork.

K8s docs ↗
  1. Create a kubernetes.io/service-account-token Secret annotated with the target ServiceAccount name.

  2. The token controller populates .data.token with a signed JWT for that ServiceAccount which does not expire.

  3. Population happens after the create response returns, so the token is never in the create result.

  4. Retrieval therefore needs get, list, or watch on secrets, or a pod that mounts the Secret.

    create alone mints a credential the caller cannot read.

  5. Authenticate as the target ServiceAccount with the retrieved token.

Additional rights needed:

Note:

The token controller still populates kubernetes.io/service-account-token Secrets created by hand, and long-lived token Secrets remain supported.

Restrict create on secrets and audit for hand-made token Secrets in kube-system.

K8s docs ↗
  1. Create a RuntimeClass whose scheduling.tolerations and scheduling.nodeSelector target a control-plane node.

  2. Create a pod that sets only runtimeClassName.

    The RuntimeClass admission plugin merges the toleration and nodeSelector into the pod spec during that same request.

  3. The pod's own manifest carries neither field, so manifest review, GitOps diffs, and static scanning miss it.

    Validating admission does see the merged spec and can still catch it.

  4. Placement alone is a scheduling-policy bypass.

    A standard pod sitting on a control-plane node reads nothing it could not read elsewhere.

  5. It becomes host compromise only with a pod spec that reaches the host, such as privileged or a hostPath mount of /etc/kubernetes.

Additional rights needed:

K8s docs ↗
  1. An IAM policy must already bind a role to the principal identifier for this namespace and ServiceAccount name in the cluster workload identity pool

  2. No annotation and no cloud service account are involved, which is the current recommended configuration rather than a legacy one

  3. IAM never checks that the ServiceAccount exists, so the binding can predate the object and the name can be squatted

  4. Creating a ServiceAccount with the bound name, or running a pod under it, claims the identity

  5. Pods then obtain tokens for that principal carrying the roles bound to it, with no annotation anywhere in the chain

Additional rights needed:

none

Note:

Not a defect and nothing to fix.

Binding IAM directly to the pool principal is the documented current approach, so the annotation-based path is the legacy one.

Auditing only for the annotation misses this entirely.

K8s docs ↗
  1. The metadata service answers any caller that can reach it, and returns credentials for the node identity rather than for any workload identity

  2. Whether an ordinary pod can reach it is a platform and provisioning property, not a Kubernetes one, so the required pod shape differs per cluster

  3. Where the address is redirected per pod network namespace, as GKE does with GKE_METADATA, a hostNetwork pod runs in the host namespace and reaches the real service

  4. Where nothing intercepts it, which is the AKS default and the eksctl default on EKS, a plain unprivileged pod reaches it with no host access at all

  5. On EKS the barrier is the IMDSv2 hop limit of 1, and it holds only while tokens are required.

    An IMDSv1 GET is not hop limited and returns the credentials anyway

  6. Workload identity does not prevent this.

    It changes which credential the SDK resolves, not which address the pod can reach, and a hostNetwork pod always reaches it

  7. Mounting the host root as root is an independent second route, reading the kubelet credentials and the cloud provider config from disk

  8. The node identity ceiling varies from no cloud permissions at all to full control of the node resource group, so read its role bindings before rating impact

Additional rights needed:

none

Note:

Working as designed rather than a fixable defect.

Whether a plain pod is blocked depends on the platform and on how the node group was set up.

On EKS the control is the hop limit together with required tokens, not either one alone.

K8s docs ↗
K8s docs ↗

Appears in 1 threat path