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
| CVE | CVE-2026-33105 |
| Product | Microsoft Azure Kubernetes Service (AKS) |
| Class | Improper Authorization — CWE-285 / CWE-863 |
| CVSS | 10.0 — Critical (network vector, low complexity, no privileges, no user interaction, scope changed) |
| Impact | Elevation of privilege → potential full cluster compromise |
| MITRE ATT&CK | T1078 Valid Accounts · T1548 Abuse Elevation Control · TA0004 Privilege Escalation |
| Boundary | Layer 2 Cluster (control-plane authZ) → blast radius Layer 3 Node, Layer 1 Cloud IAM |
| Crosses boundary | YES — trust position escalates across the managed authorization boundary |
| Vendor status | Mitigated 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:
169.254.169.254).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):
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:
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.
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# 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 yamlaz 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# 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)"'# 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:
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:
list/get on secrets cluster-wide when it historically never did. Confidence: high.clusterrolebindings / rolebindings create or patch binding a subject to cluster-admin. Confidence: high.169.254.169.254 IMDS token endpoint from workloads that never call Azure APIs. Confidence: medium.B. Detection rule — AKS diagnostic (KQL) for the Secrets-sweep signal:
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 descFalco catches the in-cluster escalation attempt at runtime:
- 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):
# 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
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):
# 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-identityapiVersion: 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: 0Rollback: 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
| State | Risk | Reason |
|---|---|---|
| Control plane on a current AKS version + node images past fix rollout | Low | The vulnerable managed authZ path is patched by Microsoft. |
| Auto-upgrade channel + NodeImage/SecurityPatch OS channel enabled | Low | Prevents silent re-drift after remediation. |
| IMDS reachable from pods, broad node MSI | High | Cloud-pivot leg open — any future authZ slip reaches Azure. |
| cluster-admin bound to SAs / add-ons | High | Ready-made landing pad that turns escalation into total compromise. |
| kube-audit disabled | Blind | The 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 K8SECSources: 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.
