Back

10/11/2026

Kyverno CVE-2026-124440: When the Policy Engine Becomes the Bypass to Cluster Admin

In Kyverno 1.19.1, a tenant allowed to write policies in one namespace can reach cluster admin. How the bypass works and what to check before you upgrade.

Many teams run Kyverno to keep Kubernetes tenants apart. It checks every object against policy before the API server stores it, and it can let namespace owners write their own policies for their own namespace. CVE-2026-124440, published on November 8, 2026, shows how that delegation can fail. In Kyverno 1.19.1, a tenant who may only create policies in its own namespace can make Kyverno act outside that namespace and, step by step, reach cluster admin.

The fix has been available since October 20 in Kyverno 1.19.2. Clusters that skipped that release are still exposed. This article explains what went wrong, how the escalation works, and what to check in your own clusters.

Key takeaways

  • CVE-2026-124440 (GitHub advisory GHSA-39w8-hv5g-f73w) affects Kyverno 1.19.1. It is fixed in 1.19.2. The CVE record rates it 9.4 under CVSS 4.0 and 9.9 under CVSS 3.1.
  • The attacker only needs permission to create a namespaced Kyverno Policy in its own namespace.
  • The policy’s apiCall runs as Kyverno’s admission-controller ServiceAccount, which can read other namespaces and create cluster-wide admission webhooks. That path leads to cluster admin.
  • The advisory lists no workaround. Upgrade to 1.19.2, then review who can create policies and which webhooks exist.
  • No exploitation in the wild has been reported in the sources we reviewed.

What the namespace clamp was supposed to do

Kyverno policies can fetch data from the Kubernetes API while they evaluate a request. This feature is called apiCall: a rule names a urlPath, Kyverno calls the API server, and the result feeds into the rule. The call is made with Kyverno’s own ServiceAccount, not with the permissions of the person who wrote the policy.

For cluster-wide ClusterPolicies, written by platform administrators, that is expected. For namespaced Policies, which tenants can be allowed to write, it is a risk. Earlier advisories (GHSA-c5qq-7g2q-cpqp and GHSA-5qq8-67g6-4h2w) showed that a namespaced Policy could use apiCall to read other namespaces or write cluster-scoped objects. Kyverno 1.19.1, released on September 10, 2026, added a “namespace clamp”: before running the call, Kyverno checks that the path stays inside the policy’s own namespace.

Where the check and the action diverge

The clamp in 1.19.1 did not check the string it sent. According to the advisory, it decoded the percent-encoding in urlPath, normalised the result (so that .. segments are resolved), and checked the namespace in that cleaned copy. The request itself was then sent with the original, raw path.

That works as long as decoding succeeds. But if the path contains a malformed percent-escape, such as a lone % in a trailing query string, decoding fails. In that case the 1.19.1 code fell back to checking the raw, still-encoded string. The API server has no such fallback. It decodes the path fully and routes the request. So the clamp sees the tenant’s own namespace, while the API server sees encoded .. segments that climb out of it, into another namespace or to a cluster-scoped collection.

The advisory stresses that this is a class of inputs, not one payload. Blocking one literal string would not close it. The durable fix is to validate and execute exactly the same path.

That is what 1.19.2 does. In our reading of the 1.19.2 source, Kyverno now builds one canonical form of the path, rejects paths it cannot canonicalise, checks the namespace on that form and sends that same form to the API server. A sibling issue fixed in the same release, CVE-2026-122310 (GHSA-3xfv-vq8x-qhg4), abused the same split between the cleaned check and the raw request by hiding ../ segments behind # or ?. It is a separate CVE with its own record.

The escalation chain, step by step

The reporter demonstrated the chain on Kubernetes 1.31 with Kyverno 1.19.1 installed from the stock Helm chart with default values.

  1. Create a namespaced Policy. The tenant’s only privilege is creating policies.kyverno.io in its own namespace. The policy contains an apiCall whose path passes the clamp but routes elsewhere.
  2. Trigger it. Creating any ordinary object that the policy matches, such as a ConfigMap in the tenant’s namespace, makes Kyverno evaluate the rule and run the call as its admission-controller ServiceAccount.
  3. Read across namespaces. The same technique reads objects in other namespaces, including Secrets if the Kyverno deployment is allowed to read them.
  4. Write a cluster-wide webhook. The ServiceAccount may create MutatingWebhookConfigurations and ValidatingWebhookConfigurations, so the tenant can register its own webhook, which then intercepts and rewrites admission requests in every namespace.
  5. Reach cluster admin. The advisory describes using such a webhook to alter pods created in kube-system and, from there, grant the tenant cluster-admin rights. The result is a total loss of confidentiality, integrity and availability.

No user interaction is needed, and the webhook fires on normal cluster activity.

Who is affected

The CVE record lists Kyverno 1.19.1 as affected and 1.19.2 as unaffected. Versions before 1.19.1 do not have this particular bypass, because they do not have the clamp at all. They are affected by the earlier advisories the clamp was meant to fix, so staying on an older release is not a safe alternative. The risk is highest where namespace owners, CI pipelines or self-service portals are allowed to create Kyverno Policies.

Kyverno 1.19.2 was released on October 20, 2026, and its release notes list 24 GitHub security advisories: 9 critical, 14 high and 1 moderate. Several of them concern API path validation and namespace confinement. This advisory was published three days later, on October 23, but both the advisory and the CVE record name 1.19.2 as the fixed version. Each advisory has its own identifier, so match them one by one rather than treating the batch as a single CVE.

What to check in your own clusters

First, find the Kyverno version that is actually running, not the one in your Git repository:

kubectl get deployments -n kyverno -o jsonpath='{range .items[*]}{.metadata.name}{"\t"}{.spec.template.spec.containers[*].image}{"\n"}{end}'

If any image tag is v1.19.1, plan the upgrade to 1.19.2 now. Then check four things:

  • Who can create namespaced Policies. Test tenant identities directly, and look for roles that grant policies.kyverno.io, including aggregated ClusterRoles that add rights to the built-in admin and edit roles.
  • Existing policies that use apiCall. List them and review every urlPath, especially paths containing %, .., ? or #.
  • Webhook configurations. Compare the list against what you expect. Kyverno creates its own webhooks, so focus on names you do not recognise and on recent creation times.
  • Audit logs. Look for requests from the Kyverno admission-controller ServiceAccount that create webhook configurations or read Secrets outside its usual pattern.
kubectl auth can-i create policies.kyverno.io -n team-a --as=system:serviceaccount:team-a:default
kubectl get policies.kyverno.io -A -o json | jq -r '.items[] | select([.spec.rules[]?.context[]? | select(.apiCall)] | length > 0) | "\(.metadata.namespace)/\(.metadata.name)"'
kubectl get mutatingwebhookconfigurations,validatingwebhookconfigurations -o custom-columns=NAME:.metadata.name,CREATED:.metadata.creationTimestamp

Replace team-a with your own tenant namespaces and identities. If you find a webhook you cannot explain on a cluster that ran 1.19.1, treat it as a possible incident: preserve the audit logs before you remove anything.

The wider lesson: audit the enforcer

Admission controllers, policy engines and operators are often the most privileged components in a cluster. They need broad rights to do their job, and tenants interact with them every day. That makes them an attractive target. In this case the tenancy model depended on one check, and a single decoding step that the check and the request handled differently was enough to break it.

For private-cloud and multi-tenant Kubernetes platforms, three habits help. Treat policy engines as part of the attack surface and track their security releases as closely as the Kubernetes version. Give their ServiceAccounts only the rights they need, for example by not granting Secret reads unless a policy requires them. And review which features tenants can reach through delegation, because a feature that runs with the platform’s privileges turns a tenant’s input into the platform’s action.

Sources