gcp-gke-rbac-deploy-update-image
K8s RBAC deployments/update (or equivalent patch verb) on a Deployment lets a principal change the container image reference - direct CanModifyCode into the workload, triggering a rollout with attacker-controlled image.
match (effective permission)
{
"action": "k8s:deployments/update",
"resource_type": "k8s/Deployment"
}
where
?principal holds deployments/update (or deployments/patch) via a ClusterRoleBinding or RoleBinding in the deployment's namespace or cluster-wide
?deployment is within the scope of the RBAC grant
emit
| source type | Identity |
|---|---|
| target type | KubernetesWorkload |
| source | <principal> |
| target | <Deployment / KubernetesWorkload> |
| permissions | k8s:deployments/update k8s:deployments/patch |
| conditions | iam_permission service_state |
| state logic | ACTIVE if the ClusterRoleBinding or RoleBinding granting deployments/update/patch is confirmed and the Deployment controller is running to reconcile spec changes. CONDITIONAL(service_state) if the Deployment controller is not running or the cluster is paused. This CanModifyCode(principal -> workload) feeds container-chains poisoned-image-executes-as + can-execute-as execute-as-via-code-modify to roll up to CanExecuteAs the workload's identity. |
Narrative
{principal.name} can update the Deployment {deployment.name} (k8s deployments/update or deployments/patch), allowing it to change the container image reference to a poisoned image - the Deployment controller immediately rolls out the new image to all pods, executing attacker code as each pod's identity.
Raw rule rules/derived/gcp/gke.yaml
id: gcp-gke-rbac-deploy-update-image
emits: CanModifyCode
description: "K8s RBAC deployments/update (or equivalent patch verb) on a Deployment lets a principal\
\ change the container image reference \u2014 direct CanModifyCode into the workload, triggering a rollout\
\ with attacker-controlled image."
match_effective_permission:
action: k8s:deployments/update
resource_type: k8s/Deployment
where:
- ?principal holds deployments/update (or deployments/patch) via a ClusterRoleBinding or RoleBinding in
the deployment's namespace or cluster-wide
- ?deployment is within the scope of the RBAC grant
emit:
source_type: Identity
target_type: KubernetesWorkload
source: <principal>
target: <Deployment / KubernetesWorkload>
permissions:
- k8s:deployments/update
- k8s:deployments/patch
conditions:
- iam_permission
- service_state
state_logic: ACTIVE if the ClusterRoleBinding or RoleBinding granting deployments/update/patch is confirmed
and the Deployment controller is running to reconcile spec changes. CONDITIONAL(service_state) if
the Deployment controller is not running or the cluster is paused. This CanModifyCode(principal ->
workload) feeds container-chains poisoned-image-executes-as + can-execute-as execute-as-via-code-modify
to roll up to CanExecuteAs the workload's identity.
confidence: 0.93
derived_from:
- <RBAC ClusterRoleBinding / RoleBinding granting deployments/update or deployments/patch on ?deployment>
false_positive_note: "Scope the update/patch verb PRECISELY to the namespace and resource. A RoleBinding\
\ in namespace A does not grant update in namespace B. A ClusterRoleBinding with resourceNames restricts\
\ to specific Deployments \u2014 honor it. The rollout is ACTIVE only if the Deployment controller\
\ is running; if the cluster is paused or the controller is crashed, the image change is CONDITIONAL(service_state).\
\ This rule emits the direct image-mutation path independent of the registry-push supply chain (E1).\
\ Digest-pinned image references are not relevant here \u2014 the attacker directly overwrites the\
\ image field, bypassing all registry immutability controls."
narrative: "{principal.name} can update the Deployment {deployment.name} (k8s deployments/update or\
\ deployments/patch), allowing it to change the container image reference to a poisoned image \u2014\
\ the Deployment controller immediately rolls out the new image to all pods, executing attacker code\
\ as each pod's identity."