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.

derived gcp emits CanModifyCode

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 typeIdentity
target typeKubernetesWorkload
source<principal>
target<Deployment / KubernetesWorkload>
permissionsk8s:deployments/update k8s:deployments/patch
conditionsiam_permission service_state
state logicACTIVE 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."
move · open · esc close