Like a building janitor whose master key can be tricked into opening apartments on the wrong floor
CVE-2026-2270 is a confused deputy vulnerability in the Kubernetes StatefulSet controller within kube-controller-manager. A user who holds namespace-scoped write permissions on both StatefulSet and ControllerRevision objects can craft a malicious ControllerRevision whose restoration injects cross-namespace metadata — causing kube-controller-manager to create a pod in a namespace the attacker should not reach. The cross-namespace pod is immediately garbage-collected unless the attacker also knows and references a valid StatefulSet UID in the target namespace, which significantly raises the exploitation bar. Affected versions span kube-controller-manager ≤ v1.34.11, ≤ v1.35.8, ≤ v1.36.4, and v1.37.0. Patches landed on 2026-09-23 in v1.34.12, v1.35.9, v1.36.5, and v1.37.1.
The Kubernetes project scored this CVSS 5.9 Medium (CVSS:3.1/AV:N/AC:H/PR:H/UI:N/S:U/C:H/I:H/A:N), and that assessment is reasonable. The AC:H and PR:H ratings reflect genuine constraints: you need a non-trivial privilege combination (StatefulSet + ControllerRevision write, which many RBAC policies don't grant together) plus knowledge of a target-namespace StatefulSet UID, and the garbage collector acts as a built-in safety net. The bug is real and the namespace boundary violation matters in multi-tenant clusters, but this is not an unauthenticated remote exploit — it is a privilege-escalation primitive for an already-authenticated insider with specific RBAC roles.
5 steps from start to impact.
Obtain StatefulSet + ControllerRevision write permissions
- Authenticated Kubernetes user or service account
- RBAC grants write to StatefulSets in source namespace
- RBAC grants write to ControllerRevisions in source namespace
- Many RBAC policies use restrictive verbs (get/list only) for ControllerRevision
- Managed K8s services (EKS, GKE, AKS) often restrict these permissions to admin roles
- Least-privilege RBAC policies separate StatefulSet management from ControllerRevision access
Craft malicious ControllerRevision with cross-namespace metadata
data field containing pod metadata that specifies a different target namespace. The StatefulSet controller's restoration logic was vulnerable because it restored more than just the .spec fields from the ControllerRevision — it also honored attacker-controlled metadata fields including the namespace. No known public tooling or PoC automates this step as of September 2026.- Understanding of ControllerRevision data structure
- Knowledge of the target namespace name
- No public PoC or weaponized tool exists
- Requires deep knowledge of K8s controller internals
- Crafting the exact payload is non-trivial without reference implementations
Trigger StatefulSet reconciliation to spawn cross-namespace pod
- StatefulSet controller reconciliation triggers on update
- kube-controller-manager is running a vulnerable version
- The pod is immediately targeted for garbage collection unless a valid OwnerReference exists in the target namespace
Stabilize the cross-namespace pod with a valid OwnerReference
- Knowledge of a valid StatefulSet UID in the target namespace
- Read access to target namespace StatefulSets OR ability to enumerate UIDs
- StatefulSet UIDs are UUIDs — brute-forcing is computationally infeasible
- Most RBAC policies don't grant cross-namespace read for StatefulSets
- Even with a valid OwnerReference, the pod runs under the target namespace's constraints
Achieve cross-namespace code execution
- Pod successfully created and stabilized in target namespace
- Target namespace contains valuable resources (secrets, services, databases)
- Pod runs under default ServiceAccount in the target namespace — which may have minimal RBAC
- NetworkPolicies in the target namespace may restrict the pod's connectivity
- Pod Security Standards/Admission may reject non-compliant pod specs
The supporting signals.
| In-the-Wild Exploitation | No confirmed exploitation. Kubernetes project reports no known attacks. Not listed in CISA KEV. |
|---|---|
| Proof-of-Concept | No public PoC as of 2026-09-25. No weaponized tooling found on GitHub, ExploitDB, or security research blogs. Attack requires deep K8s controller-internals knowledge. |
| EPSS Score | Not yet scored — CVE disclosed 2026-09-23, EPSS typically lags 48–72 hours for new CVEs. Expected to be low given the high-privilege prerequisite and complexity. |
| CISA KEV Status | Not listed. No KEV entry as of 2026-09-25. |
| CVSS Vector | CVSS:3.1/AV:N/AC:H/PR:H/UI:N/S:U/C:H/I:H/A:N (5.9 Medium) — Network-accessible but requires high privileges and high complexity. Scope unchanged (no escape from namespace to node). No availability impact. |
| Affected Versions | kube-controller-manager ≤ v1.34.11, ≤ v1.35.8, ≤ v1.36.4, v1.37.0 |
| Fixed Versions | kube-controller-manager v1.34.12, v1.35.9, v1.36.5, v1.37.1 (all released 2026-09-23) |
| Scanning / Exposure | Not applicable for external scanning — this is a control-plane logic flaw, not an exposed service. Shodan/Censys show ~400K exposed K8s API servers globally, but exploitation requires authenticated cluster access with specific RBAC grants. |
| Disclosure Date | 2026-09-23 (coordinated disclosure via Kubernetes Security Response Committee) |
| Reporter | ImanOracle — coordinated by Maciej Szulik, Filip Křepinský, Verónica López, Jeremy Rickard, Nathan Herz (Kubernetes SRC) |
Sources.
Why this verdict
- High privilege prerequisite: The attacker must already hold namespace-scoped write access to both StatefulSets *and* ControllerRevisions — a combination most least-privilege RBAC policies don't grant to regular users. This is PR:H territory and implies an insider or compromised service account, not an external threat.
- Garbage collection safety net: Cross-namespace pods are immediately deleted unless the attacker knows a valid StatefulSet UID (a UUID) in the target namespace. This built-in defense makes blind exploitation impractical — brute-forcing UUIDs is infeasible, and reading target-namespace StatefulSets requires separate RBAC grants the attacker may not hold.
- No PoC, no exploitation, no tooling: Two days post-disclosure, there is no public proof-of-concept, no observed exploitation, and no weaponized tooling. The attack requires deep K8s controller-internals knowledge to execute manually.
- Scope is Unchanged (S:U): Successful exploitation creates a pod in another namespace but does not escape the cluster, gain node-level access, or achieve cluster-admin. The blast radius is bounded to one target namespace per exploitation attempt.
- Role multiplier: Kubernetes is a canonically high-value orchestration component. In multi-tenant clusters (~30–40% of enterprise K8s deployments), cross-namespace pod creation violates tenant isolation and could expose namespace-scoped secrets or internal services. However, the outcome is *tenant boundary breach* — not *cluster takeover / fleet compromise / supply-chain pivot*. The chain also requires the attacker to already be inside the cluster with elevated RBAC, compounding friction. The high-value-role floor for HIGH requires domain-takeover or fleet-compromise outcomes, which this chain does not deliver without significant additional pivoting beyond the CVE scope. Verdict floor remains MEDIUM.
Why not higher?
Upgrading to HIGH would require the chain to produce fleet-scale, domain-scale, or supply-chain-scale impact without additional pivoting. Here, the maximum single-exploitation outcome is one pod in one other namespace — meaningful in multi-tenant scenarios but not fleet compromise. The PR:H + AC:H prerequisites, the garbage collection defense, the Scope:Unchanged designation, and the complete absence of exploitation evidence or tooling all argue against HIGH. The vulnerability does not transcend the Kubernetes namespace boundary into node-level or infrastructure-level access.
Why not lower?
Dropping to LOW would understate the risk for multi-tenant Kubernetes clusters where namespace isolation is a security boundary. The confidentiality and integrity impacts are rated HIGH in the CVSS vector because a successful exploit gives the attacker full control over the cross-namespace pod's spec — including potential access to mounted secrets and cluster-internal network services. Kubernetes is a canonically critical infrastructure component, and namespace boundaries are a contractual trust boundary in shared clusters.
create, update, or patch verbs on controllerrevisions in the apps API group. Most workload deployers do not need direct ControllerRevision write — the StatefulSet controller manages these automatically. Remove this permission from developer and CI/CD service account roles. This breaks the attack chain at step 1 by eliminating one of the two required RBAC grants. No noisgate mitigation SLA for MEDIUM — go straight to the 365-day remediation window, but this RBAC hardening is a quick win that should be done immediately.metadata.ownerReferences point to objects in a different namespace than the pod itself. This blocks step 4 of the attack path — the attacker cannot stabilize the cross-namespace pod because the admission controller rejects the invalid OwnerReference. Deploy within the 365-day noisgate remediation SLA.create, update, and patch operations on ControllerRevision objects at the RequestResponse level. Alert on ControllerRevision mutations from non-system service accounts. This provides detection but not prevention — useful as a compensating detective control while patching is scheduled within the 365-day noisgate remediation SLA..spec fields only, eliminating the cross-namespace metadata injection. For managed K8s (EKS, GKE, AKS), check your provider's patch timeline — control plane patches are typically auto-applied within days. Self-managed clusters should patch within the 365-day noisgate remediation SLA for MEDIUM verdicts.- NetworkPolicies alone — while NetworkPolicies can limit what a cross-namespace pod can reach, they don't prevent the pod from being created in the first place. The attacker still achieves namespace boundary violation and the pod can access any service the NetworkPolicy allows for that namespace.
- Pod Security Standards / Pod Security Admission — PSA controls what *kind* of pod can run (privileged, host networking, etc.) but does not validate whether the pod's namespace matches the creating controller's namespace. A restricted PSA profile won't block this attack if the pod spec is otherwise compliant.
- Disabling StatefulSets entirely — while this would prevent exploitation, it breaks legitimate workloads. StatefulSets are a core K8s primitive used by databases, message queues, and stateful applications. This is not a viable compensating control in production.
Crowdsourced verification payload.
Run this script on any host with kubectl configured to reach the target cluster. Invoke with: bash check_cve_2026_2270.sh. Requires read access to the cluster's server version endpoint (no special RBAC needed for kubectl version). Works for both self-managed and managed K8s — for EKS/GKE/AKS the server version reflects the control plane version.
#!/usr/bin/env bash
# CVE-2026-2270 Checker
# Kubernetes StatefulSet cross-namespace pod creation (confused deputy)
# Output: VULNERABLE / PATCHED / UNKNOWN
# Exit codes: 1=VULNERABLE, 0=PATCHED, 2=UNKNOWN
set -euo pipefail
echo "[*] CVE-2026-2270 Vulnerability Check"
echo "[*] Affected: kube-controller-manager <= 1.34.11, <= 1.35.8, <= 1.36.4, == 1.37.0"
echo ""
if ! command -v kubectl &>/dev/null; then
echo "[!] kubectl not found in PATH"
echo "UNKNOWN"
exit 2
fi
# Get full server version
FULL_VER=$(kubectl version --output=json 2>/dev/null | \
python3 -c "
import sys, json
try:
v = json.load(sys.stdin).get('serverVersion', {})
print(v.get('gitVersion', '').lstrip('v'))
except Exception:
print('')
" 2>/dev/null) || true
if [[ -z "$FULL_VER" || "$FULL_VER" == "" ]]; then
echo "[!] Could not determine Kubernetes server version."
echo "[!] Ensure kubectl is configured and the cluster is reachable."
echo "UNKNOWN"
exit 2
fi
echo "[*] Detected server version: v${FULL_VER}"
# Extract major, minor, patch
MAJOR=$(echo "$FULL_VER" | cut -d. -f1)
MINOR=$(echo "$FULL_VER" | cut -d. -f2 | tr -d '+')
PATCH=$(echo "$FULL_VER" | cut -d. -f3 | grep -oE '^[0-9]+' || echo "0")
if [[ -z "$MAJOR" || -z "$MINOR" || -z "$PATCH" ]]; then
echo "[!] Could not parse version components from v${FULL_VER}"
echo "UNKNOWN"
exit 2
fi
echo "[*] Parsed: major=${MAJOR} minor=${MINOR} patch=${PATCH}"
# Version is newer than all affected branches
if [[ "$MINOR" -ge 38 ]]; then
echo "[+] v${FULL_VER} is newer than all affected branches."
echo "PATCHED"
exit 0
fi
# Version is older than tested range
if [[ "$MINOR" -lt 34 ]]; then
echo "[?] v${FULL_VER} is older than the tested range (1.34+)."
echo "[?] This version may be vulnerable but was not tested by the advisory."
echo "UNKNOWN"
exit 2
fi
# Check each supported branch
case "$MINOR" in
34)
if [[ "$PATCH" -ge 12 ]]; then
echo "[+] PATCHED — v${FULL_VER} >= v1.34.12"
echo "PATCHED"
exit 0
else
echo "[-] VULNERABLE — v${FULL_VER} < v1.34.12"
echo "VULNERABLE"
exit 1
fi
;;
35)
if [[ "$PATCH" -ge 9 ]]; then
echo "[+] PATCHED — v${FULL_VER} >= v1.35.9"
echo "PATCHED"
exit 0
else
echo "[-] VULNERABLE — v${FULL_VER} < v1.35.9"
echo "VULNERABLE"
exit 1
fi
;;
36)
if [[ "$PATCH" -ge 5 ]]; then
echo "[+] PATCHED — v${FULL_VER} >= v1.36.5"
echo "PATCHED"
exit 0
else
echo "[-] VULNERABLE — v${FULL_VER} < v1.36.5"
echo "VULNERABLE"
exit 1
fi
;;
37)
if [[ "$PATCH" -ge 1 ]]; then
echo "[+] PATCHED — v${FULL_VER} >= v1.37.1"
echo "PATCHED"
exit 0
else
echo "[-] VULNERABLE — v1.37.0 is the only affected 1.37 release"
echo "VULNERABLE"
exit 1
fi
;;
*)
echo "[?] Minor version 1.${MINOR} is not in the advisory's affected range."
echo "UNKNOWN"
exit 2
;;
esac