medium

CVE-2026-54247

Go · github.com/zalando/skipper

Summary

Skipper: Unbounded Request Body Read in Admission Webhook Causes Memory Exhaustion DoS

Severity
medium
CVSS
4.3
EPSS
0.3% (p22)
CWE
CWE-770
Also known as
GHSA-cwxq-rc9x-2jvv
Published
2026-07-17
Updated
2026-07-17

Advisory details

Summary

The Kubernetes admission webhook handler reads the entire request body using io.ReadAll(r.Body) without any size limit. Any client that can reach the webhook port within the cluster can send a multi-GB payload, causing the skipper process to exhaust memory and be OOM-killed. This disrupts all Kubernetes admission control, potentially blocking all pod creation and updates.

Vulnerable Code

// dataclients/kubernetes/admission/admission.go:76
body, err := io.ReadAll(r.Body)  // <-- NO SIZE LIMIT
if err != nil {
    log.Errorf("Failed to read request: %v", err)
    w.WriteHeader(http.StatusInternalServerError)
    invalidRequests.WithLabelValues(admitterName).Inc()
    return
}

var review admissionReview
err = json.Unmarshal(body, &review)

For comparison, the OPA filter has a body size limit:

// filters/openpolicyagent/openpolicyagent.go:68-70
const DefaultMaxRequestBodySize = 1 << 20 // 1MB

// OPA uses a bufferedBodyReader with size limits

Attack Path

  1. Attacker identifies the admission webhook endpoint (default: :9443/admission or configured path)
  2. Attacker sends: POST /admission HTTP/1.1, Content-Type: application/json with a multi-GB request body
  3. io.ReadAll(r.Body) allocates unbounded memory for the entire body
  4. Skipper process is OOM-killed by the Kubernetes kubelet

Permission Boundary Analysis

Evidence

File Lines Description
dataclients/kubernetes/admission/admission.go 76 io.ReadAll(r.Body) without size limit
filters/openpolicyagent/openpolicyagent.go 68-70 OPA filter has DefaultMaxRequestBodySize = 1MB
filters/openpolicyagent/openpolicyagent.go 1333-1336 OPA uses bufferedBodyReader with size limits

Tests

Impact

The admission webhook handler reads the entire request body using io.ReadAll(r.Body) without a size limit. An attacker with in-cluster network access and a valid Kubernetes client certificate can send a multi-GB payload to the webhook endpoint, causing the skipper process to exhaust memory and be OOM-killed. This disrupts admission control for Ingress and RouteGroup resources until the process is automatically restarted by the kubelet.

Scope of impact: Ingress and RouteGroup admission only — not pod creation or other admission controllers.

Recovery: Kubernetes automatically restarts the OOM-killed process, limiting downtime.

Prerequisites: (1) In-cluster network access to the webhook port, (2) valid Kubernetes client certificate.

Mitigation

  1. Add http.MaxBytesReader or equivalent body size limit before io.ReadAll
  2. Follow the OPA filter pattern: define DefaultMaxRequestBodySize and use a buffered reader with size limits
  3. Add a configurable --admission-max-body-size flag

References

Related advisories

Is your project exposed to this? Stateward checks every dependency on every pull request and flags it only if your code actually reaches it.

Check my repo

Summarize with AI

ChatGPTClaudePerplexity

Sources: CISA KEV (public domain), OSV.dev & GitHub Advisory Database (CC-BY-4.0), FIRST EPSS, NVD/CWE (public domain). Served live from the Stateward advisory database.