One Namespace String to kube-system: Cross-Namespace Privilege Escalation in Kyverno (CVE-2026-54523)
Vulnerability Overview
CVE-2026-54523 (GHSA-79gf-7frw-68m9) is a critical privilege escalation in Kyverno, the CNCF Kubernetes policy engine. A tenant who can create a NamespacedGeneratingPolicy in their own namespace can instruct Kyverno's background controller to generate resources in any namespace by passing an arbitrary namespace string to the CEL function generator.apply(namespace, resources). Because that controller runs with cluster-wide RBAC by default, a low-privileged, namespace-scoped user can mint RoleBindings in sensitive namespaces such as kube-system and escalate to admin across the cluster. It is scored CVSS 3.1 9.6 (Critical), affects all versions up to and including v1.18.1, and is fixed in v1.18.2.
If you run Kyverno at or below v1.18.1 and grant any non-cluster-admin user the ability to create NamespacedGeneratingPolicy objects, that user can escalate to admin in any namespace. Upgrade to v1.18.2, and until you do, restrict who can create these policies.
Why This Matters
Kyverno is a widely adopted policy engine that sits inside the Kubernetes control plane, validating, mutating, and generating resources. To do the generation part of its job, its background controller is granted broad, cluster-wide permissions by default. In a multi-tenant cluster, the entire security model depends on namespace boundaries holding: a tenant scoped to their own namespace must not be able to act outside it. This flaw breaks exactly that guarantee. It turns a privileged, trusted internal component into a confused deputy that will carry out a low-privileged tenant's instructions anywhere in the cluster, which is one of the most damaging failure modes in a shared Kubernetes environment.
Technical Analysis
The root cause is a missing namespace check on attacker-controlled input. In pkg/cel/libs/context.go, the function GenerateResources(namespace string, dataList) receives its namespace argument straight from the CEL expression generator.apply("<target-namespace>", [...]) by way of the Kyverno SDK binding, with no validation that the target namespace is the policy's own. The admission validator for NamespacedGeneratingPolicy (pkg/cel/policies/gpol/validate.go) only confirms that the policy compiles and that matchConstraints is non-empty; it never checks the namespace argument at admission or execution time.
What makes this a clear oversight rather than a design tradeoff is that Kyverno already guards the equivalent boundary elsewhere. The ConfigMap loader (pkg/engine/context/loaders/configmap.go) rejects cross-namespace references for namespaced policies, and the API call path (pkg/engine/apicall/apicall.go) enforces a namespace match via regex. GenerateResources has neither guard. Meanwhile the background-controller ClusterRole ships, by default, with cluster-wide create/update/patch/delete on rolebindings and roles, plus configmaps, networkpolicies, and related resources. Combine an unvalidated target namespace with a deputy that holds cluster-wide RBAC, and a tenant's policy becomes a lever on the whole cluster.
Proof of Concept
The advisory includes a complete reproduction. An attacker with create on namespacedgeneratingpolicies in a tenant namespace defines a NamespacedGeneratingPolicy whose generation expression calls generator.apply("kube-system", [...]) to create a RoleBinding that grants the built-in admin ClusterRole to the attacker's own ServiceAccount. The policy is set to trigger on ConfigMap creation, so the attacker simply creates any ConfigMap in their own namespace. Kyverno's background controller then executes the generation and creates the RoleBinding in kube-system, handing the attacker's service account admin rights in that namespace. The escalation requires nothing beyond the tenant-level permission to author the policy.
Impact
A namespace-scoped user with create on NamespacedGeneratingPolicy can create Roles and RoleBindings in any namespace, riding the background controller's cluster-wide RBAC, and on a default Helm install this yields admin in any namespace including kube-system. The same primitive allows creating arbitrary ConfigMaps, NetworkPolicies, and Ingresses cluster-wide, which extends the blast radius to configuration tampering and traffic manipulation. Any installation that grants non-admin users the ability to create NamespacedGeneratingPolicy objects is affected.
Affected & Fixed Versions
| Kyverno Version | Status | Resolution |
|---|---|---|
| ≤ v1.18.1 | Vulnerable | Upgrade to v1.18.2 |
| v1.18.2 | Fixed | Enforces that the generator target namespace matches the policy namespace |
Mitigation & Remediation
Guidance drawn from the Kyverno security advisory:
- Upgrade to v1.18.2. The patched release adds the missing namespace enforcement so a
NamespacedGeneratingPolicycan no longer target a namespace other than its own. This is the complete fix. - Restrict who can create these policies. Until patched, tightly limit RBAC so that only trusted, cluster-admin-equivalent principals can create
NamespacedGeneratingPolicyobjects. The vulnerability is only reachable by users granted that permission. - Audit existing policies. Review current
NamespacedGeneratingPolicyobjects for anygenerator.apply()call whose target namespace differs from the policy's own namespace, which would indicate attempted or actual cross-namespace generation. - Constrain and monitor the controller. Where feasible, review and scope the background-controller ClusterRole, and alert on unexpected
RoleandRoleBindingcreation in sensitive namespaces such askube-system.
The Bigger Picture
CVE-2026-54523 is a clean example of the confused-deputy problem in Kubernetes: the danger is not that the attacker has power, but that a powerful, trusted component will act on the attacker's behalf without checking whether it should. Policy engines are especially exposed to this because their whole purpose is to hold broad privileges and act on cluster resources, so any gap in how they validate tenant-supplied input converts directly into cluster-wide impact. The CEL-driven generation feature made a new, attacker-influenced input (a target namespace) reachable, and the authorization check that existed for sibling features was simply not applied here. The durable lessons: validate every tenant-controlled parameter at the trust boundary, keep privileged controllers on least-privilege RBAC rather than defaulting to cluster-wide grants, and gate powerful policy primitives behind admin-only RBAC in multi-tenant clusters.