Kyverno: ServiceAccount token leaked to external servers via apiCall service URL
Date: 2026-04
Affected: go/github.com/kyverno/kyverno
Attack vector: ssrf
Category: vulnerability-disclosure
Severity: High
CVE: CVE-2026-41323
CWE: CWE-200
CWE-918
CVSS: 8.1 (CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:N)

## Summary Kyverno's apiCall feature in ClusterPolicy automatically attaches the admission controller's ServiceAccount token to outgoing HTTP requests. The service URL has no validation — it can point anywhere, including attacker-controlled servers. Since the admission controller SA has permissions to patch webhook configurations, a stolen token leads to full cluster compromise. ## Affected version Tested on Kyverno v1.17.1 (Helm chart default installation). Likely affects all versions with apiCall service support. ## Details There are two issues that combine into one attack chain. The first is in `pkg/engine/apicall/executor.go` around line 138. The service URL from the policy spec goes straight into `http.NewRequestWithContext()`: ```go req, err := http.NewRequestWithContext(ctx, string(apiCall.Method), apiCall.Service.URL, data) ``` No scheme check, no IP restriction, no allowlist. The policy validation webhook (`pkg/validation/policy/validate.go`) only looks at JMESPath syntax. The second is at lines 155-159 of the same file. If the request doesn't already have an Authorization header, Kyverno reads its own SA token and injects it: ```go if req.Header.Get("Authorization") == "" { token := a.getToken() req.Header.Add("Authorization", "Bearer "+token) } ``` The token is the admission controller's long-lived SA token from `/var/run/secrets/kubernetes.io/serviceaccount/token`. With the default Helm install, this SA (`kyverno-admission-controller`) can read


Mitigations:

Architectural layers (MAESTRO):