Read the docs
TI-2026-82239 Aliases GHSA-2hw4-c9m4-f2fj

Unbounded request body reads in knative.dev/pkg admission webhooks

Priority LOW 38CWE-770Published · Updated

Key facts

Attack vector
—
Privileges required
—
User interaction
—
Attack complexity
—
Impact type
Denial of service
Confidentiality · integrity · availability
—
Fix status
Fixed

No CVSS vector from any source. Fix status from GitHub. Impact type from CWE-770.

Summary

Versions of knative.dev/pkg before 0.0.0-20260531000007-52dbd5ece63f are affected. The admission webhook handler reads the full HTTP request body without a size limit, which is CWE-770, allocation of resources without limits. An attacker who can reach an admission webhook port can send an oversized POST that exhausts memory and gets the controller process killed.

Written with Claude Haiku 5.5 from the sources below · 2026-10-29

Description from the GitHub advisory

## ⚠️ 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` + `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`](https://github.com/knative/pkg/blob/1ad5a1ce0ee7043081cb50fd1acc6d36a9126844/webhook/admission.go) sha `1ad5a1ce0ee7043081cb50fd1acc6d36a9126844`). - Every release that ships the current [`webhook/admission.go`](https://github.com/knative/pkg/blob/1ad5a1ce0ee7043081cb50fd1acc6d36a9126844/webhook/admission.go) handler. # Vulnerable code [`webhook/admission.go:109-117`](https://github.com/knative/pkg/blob/1ad5a1ce0ee7043081cb50fd1acc6d36a9126844/webhook/admission.go#L109-L117): ```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.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): ```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 ([email protected])

Why it matters now

Fix Fixed knative.dev/pkg (Go): 0.0.0-20260531000007-52dbd5ece63f

SSVC decision points

Not available: CISA has not published SSVC decision points for this record.

Affected products

VendorProductAffected versionsFixed inStatusSource
—knative.dev/pkg (Go)before 0.0.0-20260531000007-52dbd5ece63f0.0.0-20260531000007-52dbd5ece63fPatch availableGHSA-2hw4-c9m4-f2fj
  • knative.dev/pkg (Go) Vendor— Affected before 0.0.0-20260531000007-52dbd5ece63f Fixed in 0.0.0-20260531000007-52dbd5ece63f Patch available GHSA-2hw4-c9m4-f2fj

Remediation

Update knative.dev/pkg to version 0.0.0-20260531000007-52dbd5ece63f, which is the fixed version.

Written with Claude Haiku 5.5 from the sources below · 2026-10-29

  • Fix · GHSA-2hw4-c9m4-f2fj knative.dev/pkg (Go) Fixed in 0.0.0-20260531000007-52dbd5ece63f Advisory →

Timeline

  1. 2026-10-28 Threatist record created from GHSA-2hw4-c9m4-f2fj

References

Sources

This product uses the NVD API but is not endorsed or certified by the NVD. Sources and licences