A Server-Side Request Forgery (SSRF) vulnerability in Kyverno allows authenticated users to induce the admission controller to send arbitrary HTTP requests to attacker-controlled endpoints.
When a ClusterPolicy uses apiCall.service.url with variable substitution (e.g. {{request.object.*}}), user-controlled input can influence the request target. The Kyverno admission controller executes these requests from its privileged network position without enforcing any validation or network restrictions.
The issue becomes non-blind SSRF, as response data from internal services can be reflected back to the user via admission error messages.
Kyverno supports variable substitution in apiCall.service.url, a documented feature intended to enable dynamic external lookups during admission control.
However, the current implementation lacks fundamental safeguards in the HTTP execution path:
No URL validation
User-controlled input is directly embedded into the request URL without validation or normalization.
No IP filtering
Requests can target:
127.0.0.1)169.254.0.0/16)Redirect handling not restricted
The Go HTTP client uses default redirect behavior (CheckRedirect == nil), allowing up to 10 redirects without re-validation of the target.
Response data reflection in admission errors
Response bodies are propagated back to the user in admission responses under certain conditions.
The vulnerability is non-blind through two mechanisms:
Non-2xx responses
Response body is returned in admission error messages (e.g. executor.go:98-101)
2xx responses with non-JSON content
Parsing failures (JSON/JMESPath) include response snippets in error output
This allows attackers to retrieve data from internal services directly via kubectl output.
ClusterPolicy using:apiCall:
service:
url: "http://{{ request.object.metadata.annotations.target }}"
apiVersion: v1
kind: Pod
metadata:
name: ssrf-test
annotations:
target: "169.254.169.254/latest/meta-data/iam/security-credentials/"
spec:
containers:
- name: test
image: nginx
kubectl apply -f pod.yaml
Example output:
Error from server: admission webhook "kyverno" denied the request:
failed to process apiCall: <response body from metadata service>
apiCall.service.url and variable substitutionThis issue can be combined with automatic ServiceAccount token forwarding (reported separately) to form a critical attack chain.
{
"cwe_ids": [
"CWE-918"
],
"github_reviewed": true,
"github_reviewed_at": "2026-04-14T20:05:52Z",
"nvd_published_at": null,
"severity": "HIGH"
}