GHSA-2hw4-c9m4-f2fj: Medium severity go/knative.dev/pkg vulnerability

Published Oct 28, 2026
·
Updated

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

go 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.

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:

python import socket total = 2 << 30 # 2 GiB s = socket.createconnection(('<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):

go 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 (tonghuaroot@gmail.com)

Affected Software

1 affected componentFixes available
go/knative.dev/pkg<0.0.0-20260531000007-52dbd5ece63f
0.0.0-20260531000007-52dbd5ece63f

Remediation

Recommended actions to resolve this vulnerability, in priority order.

  1. Upgrade

    Upgrade go/knative.dev/pkg to a version that resolves this vulnerability.

    Fixed in 0.0.0-20260531000007-52dbd5ece63f
  2. Configuration

    Define maxAdmissionRequestSize as 8 << 20 (8 MiB), wrap r.Body with http.MaxBytesReader(w, r.Body, maxAdmissionRequestSize), and return http.StatusRequestEntityTooLarge when the read fails with an http.MaxBytesError.

    knative.dev/pkg webhook admission handler maxAdmissionRequestSize = 8 MiB

Event History

Oct 28, 2026
Advisory Published
via GitHub·09:20 PM
Data Sourced
via GitHub·09:20 PM
DescriptionSeverityWeaknessAffected Software

Frequently Asked Questions

1

What level of access does an attacker need to trigger the issue?

The CVSS vector indicates network reachability, low privileges, and no user interaction requirement. The attacker must be able to send requests to the affected handler.

2

What resource impact was observed during verification?

In the reported Go proof of concept, streaming a 2 GiB request body caused HeapAlloc to peak at 9.00 GiB from a 0.32 MiB baseline. This was a 4.5× amplification of the request-body size, exceeding the advisory's conservative 2× expectation.

3

How can I check whether the vulnerable request-handling pattern is present?

Review the admission request-body handling in webhook/admission.go for a missing http.MaxBytesReader limit. The verification identifies the missing limit as the relevant flaw, rather than the surrounding webhook or controller scaffolding.

Contact

SecAlerts Pty Ltd.
132 Wickham Terrace
Fortitude Valley,
QLD 4006, Australia
info@secalerts.co
By using SecAlerts services, you agree to our services end-user license agreement. This website is safeguarded by reCAPTCHA and governed by the Google Privacy Policy and Terms of Service. All names, logos, and brands of products are owned by their respective owners, and any usage of these names, logos, and brands for identification purposes only does not imply endorsement. If you possess any content that requires removal, please get in touch with us.
© 2026 SecAlerts Pty Ltd.
ABN: 70 645 966 203, ACN: 645 966 203