GHSA-qw5r-ppcg-f8rj MKP: Unbounded Pod Log Read via Attacker-Controlled `limitBytes`/`tailLines` Causes Memory Exhaustion
Unbounded Pod Log Read via Attacker-Controlled `limitBytes`/`tailLines` Causes Memory ExhaustionSummaryThe MKP (Model Context Protocol for Kubernetes) server exposes a `get_resource` MCP tool that proxies Kubernetes pod log requests. User-supplied `limitBytes` and `tailLines` parameters are parsed as unbounded `int64` values and forwarded directly to the Kubernetes API. The server then reads the entire returned log stream into an in-memory `bytes.Buffer` using `io.Copy` without any application-side size cap. A remote unauthenticated attacker can exploit this to exhaust the MKP server's memory by sending a single crafted `tools/call` request, leading to process termination (OOM kill) and denial of service. Dynamic reproduction confirmed the MKP process RSS grew from 25.8 MB to 1,179.3 MB (+1,153.4 MB) while handling one request with `limitBytes=134217728`.DetailsThe vulnerability exists in `pkg/k8s/subresource.go` in the `buildPodLogOpts()` and `defaultGetPodLogs()` functions.**Source — unbounded parameter parsing (`pkg/k8s/subresource.go:171–181`):**When the `parameters` map is non-nil (always true for attacker-supplied input), `buildPodLogOpts()` is called at `pkg/k8s/subresource.go:94–96` and overwrites the 32 KB default entirely. The attacker can therefore supply any positive `int64` value (up to `2147483647` or `9223372036854775807`) as `limitBytes`.**Sink — unbounded in-memory copy (`pkg/k8s/subresource.go:114–115`):**The stream from Kubernetes is read without limit into a heap-allocated `bytes.Buffer`. Subsequent JSON serialisation and MCP response wrapping create additional copies, meaning the actual RSS increase is a multiple of the raw log size (observed: ~9×).**Attack path (source → sink):**| Step | Location | Description |
|------|----------|-------------|
| 1 | `cmd/server/main.go:30` | Server binds to `:8080` on all interfaces; no authentication by default |
| 2 | `pkg/mcp/server.go:131` | `NewGetResourceTool()` registered unconditionally (no `--read-write` required) |
| 3 | `pkg/mcp/get_resource.go:28–38` | Attacker-controlled `parameters` map parsed from `CallToolRequest` |
| 4 | `pkg/mcp/get_resource.go:76` | `client.GetResource(..., parameters)` called |
| 5 | `pkg/k8s/subresource.go:32–33` | `resource=pods` + `subresource=logs` routes into `getPodLogs` |
| 6 | `pkg/k8s/subresource.go:171–181` | `limitBytes` / `tailLines` parsed without upper bound (source) |
| 7 | `pkg/k8s/subresource.go:114–115` | `io.Copy(buf, podLogs)` loads full stream into `bytes.Buffer` (sink) |The rate limiter (`pkg/ratelimit/config.go:16–17`) caps only request frequency (120 req/min) and places no limit on per-request data volume, providing no meaningful mitigation.**Suggested remediation:**PoC**Prerequisites**Docker (for self-contained reproduction)A running Kubernetes cluster with a pod whose logs are large (for real-environment testing)MKP server accessible on port 8080**Option A — Self-contained Docker reproduction (Phase 2 method)**This method uses a mock Kubernetes API that streams 128 MB of log data:Expected output (condensed):**Option B — Real Kubernetes environment (manual)****Expected observation:** MKP process RSS grows rapidly during request handling. With sufficiently large logs or concurrent requests, the process is OOM-killed and the MCP endpoint becomes unavailable.ImpactThis is an **unauthenticated remote Denial of Service (DoS)** vulnerability affecting any deployment of MKP server accessible over the network.**Who is impacted:**Any operator running `mkp-server` in its default configuration (no `--read-write` flag required; `get_resource` is registered by default on `:8080` without authentication).Kubernetes clusters whose namespaces contain pods with large accumulated logs (e.g., `kube-system` workloads in production clusters almost always satisfy this condition).Downstream consumers of the MCP interface who rely on MKP for cluster observability; an attacker can make the entire MKP service unavailable.A single `tools/call` request is sufficient to trigger the condition. Because the rate limiter does not cap per-request data volume, even the 120 req/min limit provides no protection: one request with `limitBytes=2147483647` (~2 GB) will exhaust memory before any subsequent requests are needed.No authentication, special privileges, or pre-existing access beyond network reachability of port 8080 is required.Reproduction artifacts`Dockerfile``poc.py`
CVSS v3.1 score: 7.5