# kyverno apicall servicecall implicit bearer token injection leaks kyverno serviceaccount token

Kyverno's apiCall servicecall auto-injects the controller serviceaccount bearer token into policy-controlled URLs, letting a ClusterPolicy author exfiltrate it to an attacker endpoint.

- Payout: Research
- Severity: high
- Program: Kyverno
- Found by: 1seal, copethomas
- Disclosed: 2026-04-14
- CVE: CVE-2026-40868
- Write-up quality: 7/10
- Original: https://github.com/advisories/GHSA-q93q-v844-jrqp
- Library page: http://bugbountylibrary.com/r/ghsa%3AGHSA-q93q-v844-jrqp
- Tags: area:cloud domain:cloud domain:infra impact:data-leak impact:internal-access kind:research platform:ghsa sev:high tech:go tech:kubernetes technique:source-review vuln:auth-bypass vuln:secrets-leak vuln:ssrf

**Target:** Kubernetes admission controller apiCall servicecall HTTP client
**Root cause:** In (*executor).addHTTPHeaders, when the outgoing request has no Authorization header, Kyverno reads the controller serviceaccount token from /var/run/secrets/kubernetes.io/serviceaccount/token and adds it as a Bearer token, even though the destination URL is policy-controlled.
**Steps:**
1. Create or update a ClusterPolicy (or GlobalContextEntry) using `context.apiCall.service.url` pointing at an attacker-controlled endpoint, with no explicit Authorization header.
2. Trigger the policy so the apiCall executes.
3. Observe the receiver logs a non-empty Authorization header containing the Kyverno controller SA token.
4. Control: set an explicit Authorization header in the policy; auto-injection is skipped and no token is received.
**How found:** Code review of the apiCall executor's header handling: noticed the implicit Authorization injection path in addHTTPHeaders combined with policy-controlled servicecall URLs, then built an in-cluster receiver PoC with a negative control.
**Exploitation:** Attacker with ClusterPolicy write (or a compromised GitOps policy repo feeding Kyverno) sets servicecall URL to their listener; Kyverno's controller SA token is sent as Bearer, which the attacker replays against the API server with kubectl auth can-i to enumerate privileges.
**Escalation:** Replay exfiltrated token against the Kubernetes API; impact scales with RBAC bound to the Kyverno controller serviceaccount.
**Insight:** Admission controllers and policy engines that make outbound calls on behalf of user-authored config are confused-deputy candidates: grep for implicit credential attachment (SA token, cloud metadata creds) in HTTP client wrappers where the URL comes from policy/CRD fields.

## Proof of concept (copied exactly from the original report)

**PoC run commands (make test/canonical/control)**

```
unzip poc.zip -d poc
cd poc
make test

canonical (expected: implicit token injection):

unzip poc.zip -d poc
cd poc
make canonical

expected output includes:

[CALLSITE_HIT]: executor.addHTTPHeaders Authorization=="" -> read_serviceaccount_token=true
[PROOF_MARKER]: authorization_header_injected=true token_nonempty=true

control (expected: explicit Authorization header disables auto-injection):

unzip poc.zip -d poc
cd poc
make control

expected output includes:
```

**Vulnerable token injection code snippet**

```
if req.Header.Get("Authorization") == "" {
  token := a.getToken()
  if token != "" {
    req.Header.Add("Authorization", "Bearer "+token)
```

