Kubetier
← Permission Reference
T1

patch pods

Most of the Pod spec is immutable after creation, but spec.containers[*].image and spec.initContainers[*].image are officially mutable.

Patch alone (with get, since kubectl fetches current state first) replaces a running container's image with an attacker-controlled one.

The kubelet restarts the container, inheriting whatever ServiceAccount, volumes, and host access the pod already has.

This does not grant pods/ephemeralcontainers access or patch status without the pods/status subresource.

Contextual upgrade to T0:

Target pod already runs with a privileged/high-value ServiceAccount, or with privileged/hostPID/hostPath/hostNetwork access; replacing its image inherits that access immediately, same as the equivalent create-pod path.

Pod command and args are immutable, so an image-only swap changes what runs only if the target does not pin an explicit command.

API Group
(core)
Scope
namespaced
Audit Level
Request

Escalation Paths

  1. Identify a running pod you have patch access to, ideally one already carrying a privileged or high-value ServiceAccount.

  2. Patch spec.containers[*].image.

    Container images are among the few pod spec fields Kubernetes allows mutating after creation.

  3. patch alone is enough.

    A raw JSON Patch naming the container index needs no read, while kubectl patch additionally needs get because it fetches current state to compute the merge.

  4. The kubelet detects the definition change, kills the container, and restarts it running the new attacker-controlled image.

  5. The new container inherits the pod's existing ServiceAccount token, mounted volumes, and any host namespaces or capabilities already granted.

Additional rights needed:

none

Note:

The API requires only patch.

A JSON Patch against a known container index needs no get, which kubectl needs purely for its client-side merge.

The kubelet restarts the container within seconds.

K8s docs ↗
  1. NetworkPolicy podSelector matches on a running Pod's labels, re-evaluated continuously, not fixed at creation

  2. Patch the label a restrictive NetworkPolicy selects on to a value outside its selector

  3. The same running Pod, same IP, same containers, is no longer covered by that policy's ingress/egress rules

  4. Traffic that was blocked moments earlier now flows, with no image or spec change at all

Additional rights needed:

none
K8s docs ↗
  1. Patch a NoExecute toleration onto an already-running Pod.

    Tolerations are one of the few pod spec fields addable after creation

  2. An incident responder later taints the suspect node NoExecute to evict everything running on it

  3. The taint manager evicts every Pod without a matching toleration.

    The pre-patched Pod is skipped and keeps running

  4. A backdoor Pod outlives the exact response action meant to remove it from the node

Additional rights needed:

none
K8s docs ↗
  1. With patch or update on a namespaced object, set metadata.ownerReferences to point at an owner that does not exist, using a resolvable apiVersion and kind with any name and UID.

  2. The garbage collector resolves the reference, confirms the owner is absent, and deletes the object within seconds on the next sync.

  3. No delete permission on the object is required, and no ownership check runs unless the OwnerReferencesPermissionEnforcement admission plugin is enabled, which it is not by default.

  4. Applies to any namespaced resource the principal can patch or update, turning patch rights into a deletion primitive for objects that delete access would otherwise gate.

Additional rights needed:

none

Note:

Distinct from CVE-2025-5187 (node self-patch to a cluster-scoped owner, patched).

This is intended behavior for namespaced objects, not a CVE, gated only by the OwnerReferencesPermissionEnforcement admission plugin, which is not enabled by default.

K8s docs ↗

See Also

K8s docs ↗