Kyverno's ConfigMap context loader accepts arbitrary namespace values with zero validation, letting a namespace admin read ConfigMaps cluster-wide via Kyverno's privileged service account.
Kyverno is a security tool that companies use to enforce rules across their cloud infrastructure. Think of it like a bouncer checking IDs at a nightclub, making sure only authorized people access sensitive areas.
Researchers discovered that this bouncer has a critical flaw. When companies divide their cloud systems into separate sections called "namespaces" — basically different departments or projects — Kyverno was supposed to keep them isolated from each other. It failed to do that.
Here's the problem in plain terms: Someone with administrator privileges in one department can trick Kyverno into grabbing sensitive information from completely different departments. It's like a bouncer who's supposed to check IDs carefully but instead just lets anyone flash a badge and walk into the VIP section.
The vulnerability works because Kyverno uses a powerful service account — think of it as a master key — to access configuration files. When checking what information someone can see, it forgot to verify that the person actually had permission to access that specific department's files. So an attacker with basic admin powers in one area can exploit this weakness to raid another area's secrets.
This is especially risky for companies with multiple teams sharing the same cloud infrastructure. Database passwords, API keys, and other sensitive credentials stored in these configuration files could be exposed. Smaller companies and startups using shared Kubernetes clusters are most vulnerable since they're more likely to have teams with namespace-level access.
What to do: First, check if you're running Kyverno and update immediately when patches become available. Second, limit who gets namespace administrator permissions — not everyone needs that level of access. Third, monitor your system logs for suspicious attempts to access ConfigMaps across namespaces. If you're unsure whether this affects you, ask your cloud infrastructure team today.
Want the full technical analysis? Click "Technical" above.
▶ Privilege escalation — CVE-2026-41068
Vulnerability Overview
CVE-2026-41068 is a privilege escalation vulnerability in Kyverno's policy engine affecting multi-tenant Kubernetes clusters. The patch for CVE-2026-22039 addressed cross-namespace privilege escalation in the apiCall context entry by validating the URLPath field — but the fix was surgically narrow. The ConfigMap context loader (configMap context entry type) carries the identical design flaw: the configMap.namespace field is accepted verbatim from policy authors with no namespace boundary enforcement whatsoever.
In a multi-tenant cluster where namespace admins can create or modify Kyverno ClusterPolicy or Policy resources, an attacker-controlled namespace field causes Kyverno's own privileged service account — which has cluster-wide get on ConfigMaps — to fetch secrets from arbitrary namespaces and expose them as policy context variables. CVSS 7.7 (HIGH), no public exploitation observed as of publication.
Root cause: The ConfigMap context loader in Kyverno's policy engine passes the policy-author-supplied configMap.namespace field directly to a privileged Kubernetes API call without validating it against the requesting policy's own namespace, enabling a complete RBAC bypass via cross-namespace ConfigMap reads.
Affected Component
The vulnerable code lives in the context loader pipeline, specifically the configMapLoader within Kyverno's engine context package. The relevant path in the Kyverno source tree is pkg/engine/context/loaders/configmap.go (and its callers in pkg/engine/context/loaders/loader.go). The Kyverno service account used for these fetches typically holds a ClusterRole binding with broad ConfigMap read access across all namespaces — a necessary privilege for legitimate cross-namespace policy evaluation, but catastrophic when the namespace selector is attacker-controlled.
Affected: all versions prior to 1.17.2. Fixed: Kyverno 1.17.2 (GHSA-cvq5-hhx3-f99p).
Root Cause Analysis
The apiCall loader fix for CVE-2026-22039 added a validateURLPath() check. The ConfigMap loader was never updated to mirror this — it calls the Kubernetes client directly with the caller-supplied namespace string:
// pkg/engine/context/loaders/configmap.go
// Simplified Go pseudocode rendered as C-style for analysis clarity
typedef struct {
char *name; // configMap.name — policy-author controlled
char *namespace; // configMap.namespace — BUG: never validated
} ConfigMapRef;
ConfigMapData* loadConfigMap(KubeClient *client, ConfigMapRef *ref, Policy *policy) {
// BUG: ref->namespace is taken verbatim from the policy spec.
// No check that ref->namespace == policy->namespace or
// that the requesting principal has rights in ref->namespace.
// The call is made under Kyverno's own ClusterRole, not the
// policy author's RBAC identity.
ConfigMap *cm = client->CoreV1()
->ConfigMaps(ref->namespace) // BUG: attacker-controlled namespace
->Get(ctx, ref->name, GetOptions{});
if (cm == NULL) return NULL;
return buildContextData(cm);
}
Compare this to the patched apiCall loader, which introduced namespace scoping after CVE-2026-22039:
// pkg/engine/context/loaders/apicall.go (post CVE-2026-22039 patch)
int validateURLPath(char *urlPath, char *policyNamespace) {
// Rejects paths that reference resources outside policyNamespace
// e.g. /api/v1/namespaces//configmaps where X != policyNamespace
if (crossNamespaceReference(urlPath, policyNamespace)) {
return ERR_CROSS_NAMESPACE; // enforced
}
return OK;
}
// configmap.go equivalent — THIS CHECK WAS NEVER ADDED:
int validateConfigMapNamespace(char *cmNamespace, char *policyNamespace) {
// BUG: function does not exist. No analog to validateURLPath()
// was implemented for the ConfigMap context loader.
}
The asymmetry is the vulnerability. Two sibling loaders, one guarded, one not. The fix for the first CVE created a false sense of completeness.
Exploitation Mechanics
Prerequisites: attacker has create or update on Policy or ClusterPolicy resources in at least one namespace. This is a realistic privilege in multi-tenant clusters where namespace admins self-manage policies.
EXPLOIT CHAIN:
1. Attacker (namespace admin in "tenant-evil") creates a Kyverno ClusterPolicy
with a configMap context entry pointing to a target namespace:
context:
- name: stolenData
configMap:
name: aws-credentials # target ConfigMap
namespace: kube-system # BUG: arbitrary namespace accepted
2. Policy rule references context variable: {{ stolenData.data.AWS_SECRET_KEY }}
3. Kyverno engine processes a matching admission request (easily triggered by
creating any Pod in tenant-evil — attacker controls their own namespace).
4. configMapLoader calls:
client.CoreV1().ConfigMaps("kube-system").Get(ctx, "aws-credentials", ...)
using Kyverno's own service account (ClusterRole: configmaps get/list/watch).
5. ConfigMap data is loaded into policy evaluation context. Attacker causes
the value to be reflected in a policy-generated annotation, label,
mutating patch, or denial message — all of which are returned to the
API caller in the AdmissionReview response or on the mutated object.
6. Attacker reads the secret value from the mutated object or event log.
Full RBAC bypass: attacker never needed RBAC access to kube-system.
A minimal malicious policy demonstrating the read primitive:
After applying the policy, the attacker creates any Pod in trigger_ns. The mutated Pod returned by the API server carries the ConfigMap contents in metadata.annotations["debug/exfil"]. No kube-system RBAC required.
Memory Layout
This is a logic/authorization vulnerability rather than a memory corruption bug, so the "memory" we care about is the Kubernetes authorization decision graph and the Kyverno context variable store. The following shows how context data flows from the privileged fetch into the policy evaluation scope:
KYVERNO CONTEXT VARIABLE STATE — during policy evaluation:
PolicyContext {
policy: "exfil-configmap"
namespace: "tenant-evil" ← attacker's namespace
resource: Pod/tenant-evil/evil-pod
context_entries: [
ContextEntry {
name: "stolen"
type: ConfigMap
source_ns: "kube-system" ← BUG: crosses namespace boundary
source_nm: "aws-credentials" ← target secret
// Fetched under Kyverno SA identity, NOT attacker identity:
// ServiceAccount: kyverno / kyverno
// ClusterRoleBinding: kyverno -> ClusterRole: kyverno
// rules: [{apiGroups:[""], resources:["configmaps"], verbs:["get","list","watch"]}]
resolved_data: { ← lands in JMESPath evaluation scope
"AWS_ACCESS_KEY_ID": "AKIAIOSFODNN7EXAMPLE",
"AWS_SECRET_ACCESS_KEY": "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"
}
}
]
}
JMESPATH EVALUATION:
expression: "{{ stolen.data.AWS_SECRET_ACCESS_KEY }}"
result: "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"
written to: Pod.metadata.annotations["debug/exfil"]
visible to: attacker via kubectl get pod evil-pod -o yaml
Patch Analysis
The fix in Kyverno 1.17.2 mirrors what was done for apiCall — enforce that the configMap.namespace field does not escape the policy's own namespace when the policy is a namespaced Policy resource. For ClusterPolicy, the fix restricts which namespaces are reachable based on the matched resource namespace.
// BEFORE (vulnerable — pkg/engine/context/loaders/configmap.go):
ConfigMapData* loadConfigMap(KubeClient *client, ConfigMapRef *ref,
EvalContext *ctx) {
// ref->namespace is directly from policy spec — no validation
ConfigMap *cm = client->CoreV1()
->ConfigMaps(ref->namespace)
->Get(ctx, ref->name, GetOptions{});
return buildContextData(cm);
}
// AFTER (patched — Kyverno 1.17.2):
ConfigMapData* loadConfigMap(KubeClient *client, ConfigMapRef *ref,
EvalContext *ctx, Policy *policy) {
// For namespaced Policy resources: enforce namespace == policy namespace
if (policy->kind == POLICY_NAMESPACED) {
if (strcmp(ref->namespace, policy->namespace) != 0) {
// BUG class eliminated: cross-namespace reads rejected
logError("configMap.namespace '%s' not permitted for Policy in '%s'",
ref->namespace, policy->namespace);
return ERR_CROSS_NAMESPACE;
}
}
// For ClusterPolicy: enforce namespace == admission request resource namespace
if (policy->kind == CLUSTER_POLICY) {
char *allowedNS = ctx->admissionRequest->namespace;
if (allowedNS != NULL && strcmp(ref->namespace, allowedNS) != 0) {
logError("configMap.namespace '%s' not permitted; request namespace is '%s'",
ref->namespace, allowedNS);
return ERR_CROSS_NAMESPACE;
}
}
ConfigMap *cm = client->CoreV1()
->ConfigMaps(ref->namespace)
->Get(ctx, ref->name, GetOptions{});
return buildContextData(cm);
}
The patch is structurally identical to the apiCall fix: gate the privileged fetch behind a namespace boundary check using the policy's own namespace (for Policy) or the admission request's resource namespace (for ClusterPolicy). The root issue — two sibling code paths with inconsistent security controls — is resolved by bringing the ConfigMap loader up to parity.
Detection and Indicators
Audit Kyverno audit logs and Kubernetes API server audit logs for the following patterns:
DETECTION SIGNATURES:
1. Kyverno SA performing cross-namespace ConfigMap reads:
audit.log filter:
user.username = "system:serviceaccount:kyverno:kyverno"
verb = "get"
resource = "configmaps"
objectRef.namespace != [expected policy namespaces]
2. Policy spec review — flag any Policy/ClusterPolicy with:
context[].configMap.namespace != metadata.namespace (for Policy)
context[].configMap.namespace present at all (for review)
3. Suspicious annotation exfiltration pattern:
Objects with annotations containing structured data (JSON/YAML)
set by Kyverno mutating webhooks sourced from unexpected namespaces.
FALCO RULE (illustrative):
- rule: Kyverno cross-namespace configmap read
desc: Kyverno SA reads ConfigMap outside its own namespace
condition: >
ka.user.name = "system:serviceaccount:kyverno:kyverno"
and ka.verb = "get"
and ka.target.resource = "configmaps"
and ka.target.namespace != "kyverno"
output: >
Kyverno SA read ConfigMap in %ka.target.namespace%/%ka.target.name%
priority: WARNING
Remediation
Upgrade immediately to Kyverno 1.17.2 or later. This is the only complete fix.
RBAC mitigation (partial): Restrict create/update on ClusterPolicy and Policy resources to trusted principals only. Namespace admins should not have write access to policy resources unless they are fully trusted. Note: this does not fix the vulnerability, it reduces attacker access to the vulnerable code path.
Audit existing policies: Before upgrading, enumerate all Policy and ClusterPolicy resources and inspect spec.rules[].context[].configMap.namespace. Any value that does not match the policy's own namespace should be treated as a potential indicator of exploitation or misconfiguration.
Admission control: Deploy a secondary validating webhook (OPA/Gatekeeper, or a custom webhook) that rejects Policy resources where context[].configMap.namespace crosses namespace boundaries — useful as a defense-in-depth layer during upgrade windows.
Monitor Kyverno SA activity: Enable Kubernetes API server audit logging at RequestResponse level for the kyverno service account and alert on ConfigMap reads outside the kyverno namespace until the upgrade is complete.