Back to Blog
Managed Kubernetes · Authorization Boundary · CVSS 10.0

AKS Privilege Escalation
CVE-2026-33105

A CVSS 10.0 improper-authorization flaw in Azure Kubernetes Service. The interesting part is not the score — it is the boundary. Managed Kubernetes sells you the promise that the control plane, the node bootstrap, and the platform identities are Microsoft's problem. This CVE is what happens when an authorization check inside that promise fails: a low-trust position turns into cluster-wide privilege, and none of your RBAC YAML was in the loop.

Riad DAHMANIk8sec Security ResearchJuly 202618 min readAzure AKSCVE-2026-33105
Minimalist AKS privilege escalation CVE-2026-33105 article hero image showing the attack path from unauthenticated network actor to cluster admin

The Uncomfortable Truth

Most Azure Kubernetes Service threat models stop at the shared-responsibility line. You harden your workloads, write your Role and RoleBinding objects, tighten network policy — and you trust Microsoft to keep the control plane and the managed identity plumbing honest. CVE-2026-33105 is a reminder that the managed side of that line is still code, and code has authorization bugs.

This is an improper authorization flaw (CWE-285 / CWE-863) inside Azure Kubernetes Service, scored CVSS 10.0. Microsoft's advisory states an unauthorized attacker can elevate privileges over a network. Microsoft has mitigated it on the managed control-plane side — but your node images and your detection posture are still yours to verify.

Because Microsoft resolved this server-side and did not publish the internal component or a proof-of-concept, this article does two things and keeps them clearly separated. First, the confirmed facts from the vendor advisory. Second, a defender-grade analysis of the AKS trust boundary the bug lives on — so you can audit, detect, and prove your own clusters are on a fixed platform. Where something is inference rather than vendor-confirmed, it is labelled Analysis. There is no exploit string in this post, because there is no public one, and inventing one would be dishonest.

Vulnerability Identity Card

CVECVE-2026-33105
ProductMicrosoft Azure Kubernetes Service (AKS)
ClassImproper Authorization — CWE-285 / CWE-863
CVSS10.0 — Critical (network vector, low complexity, no privileges, no user interaction, scope changed)
ImpactElevation of privilege → potential full cluster compromise
MITRE ATT&CKT1078 Valid Accounts · T1548 Abuse Elevation Control · TA0004 Privilege Escalation
BoundaryLayer 2 Cluster (control-plane authZ) → blast radius Layer 3 Node, Layer 1 Cloud IAM
Crosses boundaryYES — trust position escalates across the managed authorization boundary
Vendor statusMitigated by Microsoft on the managed side; node image / cluster hygiene still customer-verified

Where the Boundary Actually Sits

Analysis. To reason about an authorization bug you can't see the source of, you reason about the trust boundary it must live on. In AKS, four managed trust relationships stack on top of each other, and every one of them is an authorization decision Microsoft makes on your behalf:

Node bootstrap identity
Each node authenticates to the managed control plane and to Azure using a platform-issued identity (kubelet client cert + the node's managed identity via IMDS at 169.254.169.254).
TokenRequest / projected tokens
The API server mints short-lived ServiceAccount tokens for pods. The authorization on who may request which identity is a control-plane decision.
Node authorizer & NodeRestriction
A kubelet is supposed to only read/write objects tied to its own node and pods. That scoping is enforced by the managed API server, not by you.
Managed add-on identities
CNI, CSI, metrics, and the AKS reconciler run with platform identities that frequently hold broad Azure and in-cluster permissions.

An improper authorization defect scored at 10.0 with a network vector means one of these checks accepted a request it should have denied. The practical shape of such a class of bug is always the same: a caller in a lower-trust position is treated as if it were in a higher-trust position — a workload pod treated as a node, a node treated as a control-plane component, or an out-of-scope identity accepted as in-scope. That is why the CVSS scope flag is Changed: the impact lands outside the security scope of the vulnerable component.

Exploitation Prerequisites

The single most useful thing a defender can extract from an opaque managed CVE is what has to be true for it to matter to me. Based on the advisory metrics (AV:N / AC:L / PR:N / UI:N):

Network reachA position able to reach the vulnerable AKS authorization surface over the network — from a workload pod, an adjacent tenant, or the control-plane endpoint.
No prior privilegePR:N — the attacker does not need valid cluster credentials to begin. This is what makes 10.0 defensible.
Unpatched platformA cluster whose managed control plane and node images predate Microsoft's fix rollout.
Blast surface presentOver-privileged managed identities / broad node-pool IAM that make the escalation worth chaining onward.
Prerequisite logic is the real-world risk filter CVSS cannot give you. AKS is managed, so you cannot patch the control-plane component yourself — but you can confirm your control-plane version and node images are past the fix, and you can shrink the blast surface so that even a future authZ slip lands on nothing valuable.

The Escalation Path (Analysis)

Analysis — reconstructed attack path for this class of managed-authZ bug, not a vendor-confirmed exploit. The pattern that turns an authorization slip into a breach in managed Kubernetes is a well-worn one:

ENABLER — CVE-2026-33105 · improper authorization check accepts a request it should deny INITIAL · L6/L5 Pod / unauth actor reaches authZ surface AUTHZ SLIP · L2 AuthZ check fails treated as higher-trust ESCALATION · L2 Escalated identity scope not entitled to IMPACT · L2→L1 Cluster + cloud Secrets → RBAC → node BLOCKER — fixed platform · least-privilege node identity · IMDS egress control · kube-audit visibility
Risk propagation across boundary layers — read it right-to-left as a defender: the impact tail is what makes a single authorization slip catastrophic.

Read the chain right to left as a defender: the reason a single authorization bug is catastrophic is the Secrets → RBAC → node → cloud tail. If the escalated identity can read every Secret in the cluster, it can mint or impersonate service credentials; if it reaches a node's managed identity through IMDS, it steps out of Kubernetes entirely into Azure Resource Manager. The CVE is the door; that tail is the house.

Audit: Prove Your Clusters Are on a Fixed Platform

These are real, runnable commands. None of them are exploits — they establish whether your AKS estate is past the fix and how large your blast surface is. Run them per subscription.

1. Enumerate AKS clusters and their control-plane version
az account list --query '[].{name:name,id:id}' -o table

for SUB in $(az account list --query '[].id' -o tsv); do
  az account set --subscription "$SUB"
  az aks list -o table \
    --query '[].{name:name,rg:resourceGroup,k8s:kubernetesVersion,power:powerState.code}'
done
2. Check node image versions against the current AKS release
# Per node pool: nodeImageVersion is the field that carries the platform patch level
az aks nodepool list -g "$RG" --cluster-name "$CLUSTER" -o table \
  --query '[].{pool:name,os:osType,nodeImage:nodeImageVersion,k8s:orchestratorVersion}'

# Compare to what Azure currently offers for your region
az aks nodepool get-upgrades -g "$RG" --cluster-name "$CLUSTER" -n "$POOL" \
  --query '{latestNodeImage:latestNodeImageVersion}' -o yaml
3. Confirm auto-upgrade so you don't re-drift after patching
az aks show -g "$RG" -n "$CLUSTER" \
  --query '{autoUpgrade:autoUpgradeProfile.upgradeChannel, nodeOsChannel:autoUpgradeProfile.nodeOsUpgradeChannel}' -o yaml
# Want: upgradeChannel not null (e.g. patch/stable) and nodeOsUpgradeChannel = NodeImage or SecurityPatch
4. Measure the blast surface — over-privileged identities the escalation would inherit
# In-cluster: who can read every Secret in the cluster?
kubectl get clusterrolebindings -o json | jq -r '
  .items[] | select(
    .roleRef.name=="cluster-admin" or (.roleRef.name|test("secret";"i"))
  ) | [.roleRef.name, (.subjects//[])[]?.kind, (.subjects//[])[]?.name] | @tsv'

# Any ServiceAccount granted cluster-admin (the ideal landing pad for an escalation)?
kubectl get clusterrolebindings -o json | jq -r '
  .items[] | select(.roleRef.name=="cluster-admin")
  | .subjects[]? | select(.kind=="ServiceAccount")
  | "\(.namespace)/\(.name)"'
5. Can workloads reach the node identity via IMDS? (the cloud-pivot leg)
# From a throwaway pod — if this returns a token, a compromised workload can assume the node MSI
kubectl run imds-probe --rm -it --image=curlimages/curl --restart=Never -- \
  sh -c 'curl -s -H "Metadata:true" \
   "http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01&resource=https://management.azure.com/" \
   | head -c 120; echo'
# Reachable = the Secrets→RBAC→node→Azure tail is open. Block it (see Remediation).

Vulnerability Chaining

A 10.0 rarely breaches alone — it breaches because of what it connects to. The realistic chain in a managed cluster:

CHAIN: managed authZ slip → cloud takeover
Vuln 1: CVE-2026-33105          — improper authorization → escalated in-cluster identity
   +
Vuln 2: [Misconfig] cluster-admin bound to a default/add-on ServiceAccount
   +
Vuln 3: [Misconfig] node managed identity reachable via IMDS from pods (no hop-limit/egress control)
   =
RESULT: read all Secrets → assume node MSI → Azure Resource Manager actions in the node RG

Individual risk:   CRITICAL + MEDIUM + MEDIUM
Combined risk:     CLUSTER + CLOUD COMPROMISE
Why:               the CVE supplies trust; the two misconfigs supply reach and a cloud exit.

Two of the three links are things you own. That is the good news buried in a vendor-side CVE: you cannot fix the authorization bug, but you can sever the chain on either side of it.

Real-World Impact

Scenario (constructed — no public in-the-wild report at time of writing). A multi-tenant SaaS runs customer workloads as pods across shared AKS node pools. A CI service account, for convenience, was bound to cluster-admin two years ago and never revisited. IMDS is reachable from every pod. An attacker who gains the escalated identity through CVE-2026-33105 reads the cluster's Secrets — including the storage-account keys and a mounted kubeconfig for a second cluster — then pulls the node's managed-identity token from IMDS and enumerates the resource group. Nothing in the tenant's own RBAC ever authorized any of it. This is precisely the profile CISA-style advisories describe for managed-Kubernetes privilege escalation: the initial defect is the vendor's, the blast radius is the customer's hygiene.

Detection

A. Indicators of compromise — minimum three, all observable in AKS with diagnostic logging on:

log · k8s-audit
A ServiceAccount or node identity performing list/get on secrets cluster-wide when it historically never did. Confidence: high.
log · k8s-audit
clusterrolebindings / rolebindings create or patch binding a subject to cluster-admin. Confidence: high.
network
Pod-sourced traffic to 169.254.169.254 IMDS token endpoint from workloads that never call Azure APIs. Confidence: medium.
log · Azure
ARM activity from a node managed identity outside its normal reconcile pattern (new role assignments, storage key list). Confidence: medium.

B. Detection rule — AKS diagnostic (KQL) for the Secrets-sweep signal:

Log Analytics — anomalous cluster-wide Secret reads
AzureDiagnostics
| where Category == "kube-audit"
| extend a = parse_json(log_s)
| where a.verb in ("get","list","watch")
| where a.objectRef.resource == "secrets"
| where isempty(tostring(a.objectRef.namespace))          // cluster-scoped sweep
| extend user = tostring(a.user.username)
| where user startswith "system:serviceaccount:" or user startswith "system:node:"
| summarize hits=count(), ns=make_set(a.objectRef.namespace) by user, bin(TimeGenerated, 5m)
| where hits > 20
| order by hits desc

Falco catches the in-cluster escalation attempt at runtime:

falco-aks-privesc.yaml
- rule: ClusterAdmin binding created at runtime
  desc: A ClusterRoleBinding to cluster-admin was created - classic post-escalation move
  condition: >
    kevt and ka.verb in (create, update)
    and ka.target.resource = clusterrolebindings
    and ka.req.binding.role = cluster-admin
  output: >
    cluster-admin binding created (user=%ka.user.name subject=%ka.req.binding.subjects
    binding=%ka.target.name)
  priority: CRITICAL
  source: k8s_audit
  tags: [k8s, privilege_escalation, cve-2026-33105]

- rule: Workload reached IMDS token endpoint
  desc: A pod fetched an Azure managed-identity token via IMDS
  condition: >
    outbound and fd.sip = "169.254.169.254"
    and container and not proc.name in (azure-cni, csi-azuredisk, csi-azurefile)
  output: >
    Pod contacted IMDS token endpoint (pod=%k8s.pod.name ns=%k8s.ns.name
    proc=%proc.name connection=%fd.name)
  priority: WARNING
  source: syscall
  tags: [k8s, cloud_pivot, cve-2026-33105]

C. Logging requirements. None of the above is visible at default settings. You must enable AKS diagnostic settings with the kube-audit (and ideally kube-audit-admin) categories shipped to Log Analytics, and deploy Falco (or Defender for Containers) with k8s_audit as a source. Attacks against the authorization plane are audit-log events first and syscall events second — if kube-audit is off, this class of intrusion is effectively invisible.

Remediation

Immediate — stop the bleeding (production-safe, reversible):

1. Upgrade node images so worker OS is past the fix rollout
# Reversible: node-image upgrade is a rolling, surge-based operation
az aks nodepool upgrade -g "$RG" --cluster-name "$CLUSTER" -n "$POOL" \
  --node-image-only --max-surge 33%

# Verify control-plane / cluster is on a currently-supported, patched version
az aks get-upgrades -g "$RG" -n "$CLUSTER" -o table
2. Sever the cloud-pivot leg — block pod egress to IMDS
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata: { name: deny-imds, namespace: default }
spec:
  podSelector: {}
  policyTypes: [Egress]
  egress:
    - to:
        - ipBlock:
            cidr: 0.0.0.0/0
            except: [169.254.169.254/32]   # workloads keep egress, lose the node MSI

Permanent — eliminate the blast surface (root-cause hygiene):

3. Revoke standing cluster-admin from ServiceAccounts; use Workload Identity for Azure
# Find and remove convenience cluster-admin bindings on SAs
kubectl get clusterrolebindings -o json | jq -r '
  .items[] | select(.roleRef.name=="cluster-admin")
  | .subjects[]? | select(.kind=="ServiceAccount") | .name' \
| xargs -r -I{} echo "review/remove binding for SA {}"

# Replace node-MSI reliance with scoped AKS Workload Identity (federated, per-workload)
az aks update -g "$RG" -n "$CLUSTER" --enable-oidc-issuer --enable-workload-identity
4. Kyverno — prevent the post-escalation move and re-drift
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: block-cluster-admin-to-serviceaccounts
spec:
  validationFailureAction: Enforce
  background: true
  rules:
    - name: no-cluster-admin-for-sa
      match:
        any:
          - resources:
              kinds: ["rbac.authorization.k8s.io/v1/ClusterRoleBinding"]
      validate:
        message: "Binding cluster-admin to a ServiceAccount is forbidden (CVE-2026-33105 blast-surface control)."
        deny:
          conditions:
            all:
              - key: "{{ request.object.roleRef.name }}"
                operator: Equals
                value: "cluster-admin"
              - key: "{{ request.object.subjects[?kind=='ServiceAccount'] | length(@) }}"
                operator: GreaterThan
                value: 0

Rollback: the IMDS NetworkPolicy is removable with kubectl delete netpol deny-imds; node-image upgrades roll forward only, but are surge-based and drain-aware, so they are safe to stage pool-by-pool.

What "Fixed" Means Here

StateRiskReason
Control plane on a current AKS version + node images past fix rolloutLowThe vulnerable managed authZ path is patched by Microsoft.
Auto-upgrade channel + NodeImage/SecurityPatch OS channel enabledLowPrevents silent re-drift after remediation.
IMDS reachable from pods, broad node MSIHighCloud-pivot leg open — any future authZ slip reaches Azure.
cluster-admin bound to SAs / add-onsHighReady-made landing pad that turns escalation into total compromise.
kube-audit disabledBlindThe whole class of attack is invisible without control-plane audit logs.

Final Word

Managed Kubernetes moves the control plane out of your hands — it does not move the consequences. CVE-2026-33105 is a vendor-side authorization bug you cannot patch and could not have prevented. What you own is everything on both sides of it: whether your node images are current, whether a compromised identity would find cluster-admin waiting, whether it could step out to Azure through IMDS, and whether your audit logs would ever tell you it happened.

My view: treat every managed-platform CVE as a drill for blast-radius discipline. The bug will be fixed upstream within days. The over-privileged ServiceAccount you left bound to cluster-admin will still be there next quarter — and it is the reason a 10.0 becomes a breach instead of a footnote.

— Riad DAHMANI, k8sec.io

See the escalation tail before an attacker does.

K8SEC correlates RBAC, managed identities, IMDS reachability, and runtime signals so you can spot the Secrets → cluster-admin → node → cloud path that turns a vendor CVE into a breach.

Explore K8SEC

Sources: Microsoft Security Response Center advisory for CVE-2026-33105; NVD/vendor CVSS metrics; Microsoft Learn AKS vulnerability-management and node-image upgrade documentation. Confirmed facts are drawn from the vendor advisory; sections marked Analysis or Scenario are k8sec's defender-side reconstruction and are not vendor-confirmed exploit details.

On this page

Contents

The uncomfortable truthIdentity cardWhere the boundary sitsPrerequisitesEscalation pathAudit commandsChainingReal-world impactDetectionRemediationWhat "fixed" meansFinal word