azure-aks-swap-kubelet-mi

managedClusters/agentPools/write + assign on a target MI allows swapping the kubelet MI, binding all nodes to a more-privileged identity.

derived azure emits CanAttachIdentity

match

A conjunctive graph pattern. Variables (?x) bind node ids; every clause must hold.

{'principal': None} CanModifyCode {'cluster': None} {'principal': None} CanPassIdentity {'target_mi': None}

where

?principal holds Microsoft.ContainerService/managedClusters/agentPools/write (or managedClusters/write) ?principal holds Microsoft.ManagedIdentity/userAssignedIdentities/assign on ?target_mi node_type(?cluster) in [KubernetesCluster, ContainerCluster] node_type(?target_mi) == ManagedIdentity

emit

source typeIdentity
target typeCompute
source?principal
target?cluster
permissionsMicrosoft.ContainerService/managedClusters/agentPools/write Microsoft.ManagedIdentity/userAssignedIdentities/assign
conditionsiam_permission service_state
state logicCONDITIONAL(iam_permission) — requires BOTH agentPools/write AND assign on the target MI. ACTIVE when both permissions are present and the cluster/node pool is running. BLOCKED if a deny assignment covers either permission at cluster/RG/subscription scope.

Narrative

{principal.name} holds agentPools/write on cluster {cluster.name} and assign/action on MI {target_mi.name}; can reconfigure the kubelet identity to {target_mi.name}, binding all node-level code to this identity. Combined with node-level pod access (privileged pod), this enables CanExecuteAs {target_mi.name} via azure-aks-node-imds-executes-as.

Raw rule rules/derived/azure/aks.yaml

id: azure-aks-swap-kubelet-mi
emits: CanAttachIdentity
description: managedClusters/agentPools/write + assign on a target MI allows swapping the kubelet MI,
  binding all nodes to a more-privileged identity.
match:
- - principal: null
  - CanModifyCode
  - cluster: null
- - principal: null
  - CanPassIdentity
  - target_mi: null
where:
- ?principal holds Microsoft.ContainerService/managedClusters/agentPools/write (or managedClusters/write)
- ?principal holds Microsoft.ManagedIdentity/userAssignedIdentities/assign on ?target_mi
- node_type(?cluster) in [KubernetesCluster, ContainerCluster]
- node_type(?target_mi) == ManagedIdentity
emit:
  source_type: Identity
  target_type: Compute
  source: ?principal
  target: ?cluster
  permissions:
  - Microsoft.ContainerService/managedClusters/agentPools/write
  - Microsoft.ManagedIdentity/userAssignedIdentities/assign
  conditions:
  - iam_permission
  - service_state
  state_logic: "CONDITIONAL(iam_permission) \u2014 requires BOTH agentPools/write AND assign on the target\
    \ MI. ACTIVE when both permissions are present and the cluster/node pool is running. BLOCKED if a\
    \ deny assignment covers either permission at cluster/RG/subscription scope."
  confidence: 0.85
  derived_from: []
  false_positive_note: "This escalation requires a two-permission composition: the agentPools write +\
    \ assign on the target MI. The consequence is that every node in the pool runs as the target MI \u2014\
    \ which, if more privileged than the attacker's current context, completes an escalation chain via\
    \ azure-aks-node-imds-executes-as (node-level code reaches node IMDS, mints target-MI token). Verify\
    \ both permissions are held before emitting.  The target_mi privilege_level should be higher than\
    \ the principal's for an escalation; see can-control escalate-via-execute-as for the privilege guard."
  narrative: '{principal.name} holds agentPools/write on cluster {cluster.name} and assign/action on MI
    {target_mi.name}; can reconfigure the kubelet identity to {target_mi.name}, binding all node-level
    code to this identity. Combined with node-level pod access (privileged pod), this enables CanExecuteAs
    {target_mi.name} via azure-aks-node-imds-executes-as.'
move · open · esc close