Your RSA-2048 keys break in 2030. Find every one of them before attackers do.
🐹
🐹 Go
Not in CISA KEV
MEDIUM severity

knative.dev/pkg admission webhooks allow unbounded request-body memory exhaustionGHSA-2hw4-c9m4-f2fj

MEDIUMFix: knative/pkg#3363

GHSA-2hw4-c9m4-f2fj is a medium-severity (CVSS 6.5) CWE-770 vulnerability in knative.dev/pkg. A fix is available for knative.dev/pkg — see the affected versions and patch details below.

Also known asCVE-2026-54462
Published
Updated
Affected
1 pkg
Patched
1 / 1
Exploits
None indexed
Exploitation data as of Oct 28, 2026 · OSV.dev, NVD, FIRST.org (EPSS)

EPSS Exploitation Probability

via FIRST.org ↗
0.4%probability of exploitation in next 30 days
Lower Risk0.00%
Lower risk than most CVEs31th percentile — riskier than 31% of all scored CVEsHighest risk

Probability of exploitation in the next 30 days, from FIRST.org EPSS.

How urgent is this, really

GHSA-2hw4-c9m4-f2fj by exploitation likelihood (EPSS) against impact (CVSS). Outside the shaded patch-first corner.

Where this sits among everything scored

Of 396,966 CVEs with a current EPSS score, this one falls in the < 10% band (highlighted). Counts from FIRST.org, log-scaled.

Real-World Exposure

1 pkg affected
🐹knative.dev/pkg

Real-time download stats are indexed for npm and PyPI packages. This vulnerability affects Go packages — download data is not available via public APIs for these ecosystems.

Description

⚠️ 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.


Summary

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 — main HEAD on github.com/knative/pkg (verified 2026-05-18 against webhook/admission.go sha 1ad5a1ce0ee7043081cb50fd1acc6d36a9126844).
  • Every release that ships the current webhook/admission.go handler.

Vulnerable code

webhook/admission.go:109-117:

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:

ConsumerRepoWebhook surface
Knative servingknative/servingresource validation / defaulting
Knative eventingknative/eventingresource validation / defaulting
Tekton Pipelinestektoncd/pipelinePipeline / Task / TaskRun validation
Tekton Triggerstektoncd/triggersEventListener / Trigger validation
KEDAkedacore/kedaScaledObject / ScaledJob validation
Sigstore policy-controllersigstore/policy-controllerClusterImagePolicy mutating + validating
Kpackbuildpacks-community/kpackImage / 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.

Impact

  • Class: CWE-770 unbounded allocation. Reachable from any pod-network client.
  • Severity per consumer: depends on how each consumer configures failurePolicy. For projects with failurePolicy=Fail (e.g., Sigstore policy-controller's image-policy enforcement), the consequence is cluster-wide admission outage. For failurePolicy=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.

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.

Suggested fix

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.

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])

Affected Packages

1 total 1 fixed
EcosystemPackageVulnerable rangeFix
🐹Goknative.dev/pkgall versions0.0.0-20260531000007-52dbd5ece63fgo get knative.dev/pkg@v0.0.0-20260531000007-52dbd5ece63f

Detection & mitigation playbook

Open-source dependency
  1. Detect

    Scan your dependency tree (package-lock.json, pnpm-lock.yaml, requirements.txt, go.sum, etc.) for knative.dev/pkg, including transitive dependencies — a direct dependency you never call can still pull in a vulnerable version.

  2. Fix

    Update knative.dev/pkg to 0.0.0-20260531000007-52dbd5ece63f or later, then make sure no transitive (indirect) dependency still pins the vulnerable range — O3 confirms GHSA-2hw4-c9m4-f2fj is resolved across your whole dependency graph.

  3. Workarounds

    Cap what an attacker can consume: apply request size, rate and timeout limits in front of the affected component, and run it with memory and CPU limits so exhaustion degrades one worker rather than the whole service.

Frequently Asked Questions

## ⚠️ 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`](https://github.com/knative/pkg/blob/1ad5a1ce0ee7043081cb50fd1acc6d36a9126844/cmd/poc-c2/main.go)) that runs the exact 8-line vulnerable snippet from [`webhook/admission.go:109-117`](https://github.com/knative/pkg/blob/1ad5a1ce0ee7043081cb50fd1acc6d36a9126844/webhook/admission.go#L109-L117) verbatim under the same `http.HandlerFunc` shape (the full production `admissionHandler` at `:85` requires `*Webhook` +
O3 Security · Impact-Aware SCA

Is GHSA-2hw4-c9m4-f2fj in your dependencies?

Find it across Go, including transitive dependencies.

GHSA-2hw4-c9m4-f2fj: pkg DoS — Fix Available