Our research
CVE-2026-104776: File Read via kubectl_patch in mcp-server-kubernetes Explained
CVE-2026-104776 is a vulnerability in the open-source project mcp-server-kubernetes: the kubectl_patch tool accepted a file path in its patchFile parameter and had kubectl read that file on the server, even when the server was running over a network transport. The four sibling tools with a comparable path parameter already rejected exactly that; kubectl_patch was missing the check. The GitHub advisory rates the issue CVSS 7.1 (high), lists all versions up to and including 4.1.0 as affected and 4.1.1 as fixed, and credits CyberSec42 as the reporter.
- Through
patchFile, a caller could point at a path on the server host, such as a kubeconfig or a service account token. File content could flow back to the caller through error messages or through a patch that was readable afterwards. - Only SSE and Streamable HTTP deployments were affected. In the default stdio mode the caller is the operator, and the fix leaves that behavior unchanged.
- Rating per the advisory: CVSS 3.1 base score 7.1, vector
AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:L/A:N, weakness CWE-73. - What operators should do: upgrade to 4.1.1 or later. If a network transport was reachable by callers you do not fully trust, we also recommend rotating the credentials stored on the server host.
Background
What the server does and which transports it supports.
Go to sectionThe vulnerability
The mechanism in plain words and in detail.
Go to sectionWho was affected
Versions and deployment modes.
Go to sectionThe fix
What changed and what operators should do.
Go to sectionTimeline
Publicly documented dates only.
Go to sectionThe underlying pattern
When one tool lacks a check its siblings have.
Go to sectionKey facts
| Item | Detail |
|---|---|
| Identifiers | CVE-2026-104776 (assigned), GHSA-6963-p4fp-2wxw |
| Product | mcp-server-kubernetes (npm, repository Flux159/mcp-server-kubernetes) |
| Weakness | Server-side arbitrary file read; CWE-73 “External Control of File Name or Path” |
| Severity | CVSS 3.1: 7.1, high (CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:L/A:N) |
| Affected | all versions up to and including 4.1.0, only when running over SSE or Streamable HTTP |
| Fixed | 4.1.1, released 27 July 2026 |
| Published | GitHub advisory on 2 October 2026; the CVE record at MITRE was not yet published as of 28 October 2026 |
| Credit | Reported by CyberSec42 (GitHub: lwebmedia) |
Background: what mcp-server-kubernetes is
The Model Context Protocol (MCP) is an open standard that lets AI assistants call tools. mcp-server-kubernetes is an MIT-licensed open-source MCP server that, in the words of its repository, provides Kubernetes management commands. It translates tool calls from an AI client into kubectl and helm invocations such as kubectl_get, kubectl_apply or kubectl_patch, and it acts with whatever Kubernetes credentials are present on its host.
What matters here is the transport. The default is stdio: the AI client launches the server as a local process, so caller and operator are the same person on the same machine. Optionally, the environment variables ENABLE_UNSAFE_SSE_TRANSPORT or ENABLE_UNSAFE_STREAMABLE_HTTP_TRANSPORT turn the server into a network service. The project’s advanced documentation describes both options, including a Docker example, and recommends adding header authentication via MCP_AUTH_TOKEN for production use.
On a network transport, one principle applies that the project itself spells out in its source code: a file path supplied by a caller resolves on the server host, not on the caller’s machine. That is why the maintainers blocked path parameters on network transports in two pull requests in July 2026 (#332 and #346), covering kubectl_create, kubectl_apply, kubectl_delete and Helm chart install and upgrade. For general guidance on hardening such servers, see our guide to securing MCP servers; this article covers the specific case.
How we reviewed it
The finding comes from a source-only review of the public repository. According to the advisory, no third-party deployment of mcp-server-kubernetes, no Kubernetes cluster, no kubeconfig and no other third-party system was accessed, scanned or exploited. We reproduced the divergent behavior only on our own machine against the compiled code, without any network request and without a cluster. The report was submitted confidentially through the repository’s private vulnerability reporting on GitHub.
The vulnerability
In plain words
Picture an office where visitors hand in forms. For four types of form there is a rule: outside visitors may not refer to “the document in cabinet drawer 3”; they have to bring their own papers. The fifth form type lacked that rule. A visitor could point to a drawer, and the clerk would take the document out and work with it. If it did not fit the form, the clerk’s reply quoted parts of it back.
That is how kubectl_patch behaved up to version 4.1.0. The cabinet is the server’s file system, and the drawer is the patchFile parameter.
In detail
The kubectl_patch tool accepts a change either as inline data (patchData) or as a path to a file (patchFile). It passed the path on to kubectl as --patch-file, and kubectl runs on the server host with the server’s privileges. The sibling tools call isRemoteTransport() right before that step and reject the path when the server runs over the network. kubectl-patch.ts did not make that call. A second, general safeguard in the project does inspect every argument passed to kubectl, but according to the advisory it only checks the names of certain flags, not the value that follows. It therefore offered no protection for the path.
The advisory describes two ways content could flow back. If the file is not valid JSON or YAML, kubectl fails to parse it, and the error message, including context from the file, was returned verbatim to the caller. If the file is valid, which is plausible for many configuration and credential files, its content is applied as a patch to a Kubernetes resource. The caller could then read that resource in full through kubectl_get.
Reading the vector: PR:L means a caller needs low privileges, here a valid X-MCP-AUTH header value if the operator has set MCP_AUTH_TOKEN. C:H reflects the loss of confidentiality, and I:L the limited integrity impact of the unintended patch. We recalculated the 7.1 base score using the formula in the CVSS specification. The advisory itself notes that the score would be higher without a token, around 8.2, and that an operator who exposes a network transport without a token already gives every reachable caller access to powerful cluster tools. Both scores fall into the high band; the lower one was reported.
Schematic flow
- The operator starts
mcp-server-kuberneteswith SSE or Streamable HTTP. Kubernetes credentials are present on the host. - A caller who can reach the endpoint, and who holds the auth header if one is configured, calls
kubectl_patchwith a path on the server host aspatchFile. - The server does not check the transport and hands the path to
kubectl, which opens the file with the server’s privileges. - If the file is not a valid patch, the returned error message contains context from the file. If it is valid, its content ends up in a resource that can be read through
kubectl_get.
We deliberately publish no exploit code and no sample request. The description is enough to assess your own deployment and to follow the fix.
Who was affected
The advisory lists the npm package mcp-server-kubernetes in all versions up to and including 4.1.0. In the 4.1.0 source, kubectl-patch.ts contains no call to isRemoteTransport(); from 4.1.1 it does. That function decides whether the server runs over the network solely by looking at the two environment variables for SSE and Streamable HTTP.
| Deployment mode | Assessment up to 4.1.0 |
|---|---|
stdio (default, local process) | Not affected. The caller is the operator on their own machine, and the fix does not change this behavior |
SSE or Streamable HTTP with MCP_AUTH_TOKEN set | Affected for anyone who can reach the endpoint and holds a valid auth header |
SSE or Streamable HTTP without MCP_AUTH_TOKEN | Affected for anyone who can reach the endpoint |
With ALLOW_ONLY_READONLY_TOOLS=true | Not reachable according to the advisory, because kubectl_patch is not on the allowed list. ALLOW_ONLY_NON_DESTRUCTIVE_TOOLS=true, by contrast, does not exclude the tool |
How serious the risk was in a given setup depends on who could reach the endpoint and on which files were present on the server host. The advisory names a mounted kubeconfig, a pod’s service account token and the process environment as typical targets.
The fix and what operators should do now
What changed in 4.1.1
Pull request #355 extends the existing rule to kubectl_patch: when the server runs over SSE or Streamable HTTP, patchFile is rejected with an error that points callers to patchData. The parameter description in the tool schema now states the restriction explicitly, just as it does for the sibling tools. Behavior in stdio mode is unchanged. The fix also extends the existing test file for path parameters on network transports to cover kubectl_patch under both transport variables.
The test is the lasting part: it closes the coverage gap that allowed kubectl_patch to drift from its siblings in the first place.
Recommendations for operators
The following steps are our recommendations, not requirements from the maintainers.
| Step | Action |
|---|---|
| 1. Check the version | Upgrade to 4.1.1 or later. The advisory lists 4.1.1 as the first fixed version. |
| 2. Check the transport | Find out whether ENABLE_UNSAFE_SSE_TRANSPORT or ENABLE_UNSAFE_STREAMABLE_HTTP_TRANSPORT was set. If neither was, the path described here did not exist. |
| 3. Rotate credentials | If a network transport was reachable by callers you do not fully trust: rotate kubeconfig credentials and service account tokens, and replace secrets held in the process environment. |
| 4. Shield the endpoint | Set MCP_AUTH_TOKEN, do not expose the endpoint unprotected and terminate TLS in front of it, as the project documentation recommends for production. |
| 5. Limit the tools | Enable only the tools you actually need, for example via ALLOW_ONLY_READONLY_TOOLS or ALLOWED_TOOLS. |
| 6. Limit privileges | Give the server only the cluster permissions its enabled tools require, and keep unrelated secrets off its host. |
Timeline
Only dates that can be verified from public sources are listed.
| Date | Event | Source |
|---|---|---|
| 13 Jul 2026 | Pull request #332: path parameters of kubectl_create blocked on network transports | GitHub commit |
| 18 Jul 2026 | Pull request #346: same rule for kubectl_apply, kubectl_delete and Helm | GitHub commit |
| 26 Jul 2026 | Release 4.1.0, the last affected version | GitHub release, npm |
| 27 Jul 2026 | Source review at the 4.1.0 state, per the advisory | GitHub advisory |
| 27 Jul 2026 | Fix merged (pull request #355), release 4.1.1 | GitHub, npm |
| 2 Oct 2026 | GHSA-6963-p4fp-2wxw published with CVE-2026-104776 | GitHub advisory |
| 28 Oct 2026 | CVE record at MITRE not yet published | Query of the CVE Services API |
The fixed version was therefore available more than two months before the vulnerability was publicly described.
The underlying pattern: when one tool lacks a check its siblings have
CVE-2026-104776 is not a new kind of bug. The maintainers had recognized the risk, named it and fixed it in four places. The gap was the fifth place, which the follow-up sweep missed. We call this sibling asymmetry: several tools take the same shape of input, but only some of them validate it. MCP servers are prone to it because each tool tends to be its own file with its own input handling, and safeguards are often added tool by tool.
In our walkthrough of the OWASP MCP Top 10, the case touches class 10 (insecure file access) and class 7 (credentials that can leak) above all. There is also an element of class 4: a safeguard that sees every argument but only checks flag names can create an impression of complete protection. For MCP server developers, four rules follow:
- Enforce rules centrally. A check like “no server paths on network transports” belongs in one place every tool passes through, not in each tool separately. The advisory proposes exactly this structural fix.
- Find every sibling when extending a rule. When you extend a safeguard to more tools, search by the shape of the input, meaning every parameter that accepts a path, not by known tool names.
- Test the negative case for every tool. A test that checks the rejection for each tool with a path parameter makes divergence visible immediately.
- Do not return raw error output. Unfiltered error messages from external programs can contain content that was never meant for the caller.
For operators, the flip side: an MCP server on a network transport is a network service with access to your cluster and to its host’s file system. It deserves the same treatment as any other API that holds secrets: authentication, least privilege and a deliberately short tool list. You will find more publicly documented findings on our research page.
Thanks to the maintainers
We thank the maintainers of mcp-server-kubernetes for the coordinated collaboration. The report went in confidentially through GitHub’s private vulnerability reporting. The maintainers implemented the fix with tests, shipped the corrected version well ahead of disclosure and published the advisory with a CVE ID and credit to CyberSec42. The project also documents its security decisions in a way that is easy to follow: the reasoning behind the path restriction sits as a comment right in the code and in the pull request descriptions.
Do all tools in your MCP server enforce the same checks?
We assess MCP servers and AI agents within a scope agreed in writing: whether safeguards apply equally to every tool, which files and credentials a tool call can reach, how the transports are secured and what a caller can actually achieve. Every finding comes with reproducible evidence and a concrete remediation proposal. Learn more under AI pentest & MCP security.
Request an AI pentestCyberSec42 is an independent technical security assessment, not an accredited certification body, and does not provide legal advice. CyberSec42 is not affiliated with the maintainers of mcp-server-kubernetes.
Frequently asked questions
What is CVE-2026-104776?
A vulnerability in the MCP server mcp-server-kubernetes. Its kubectl_patch tool let callers read files on the server host through the patchFile parameter, even over network transports where the sibling tools already rejected such paths. The GitHub advisory rates it CVSS 7.1 (high).
Which versions are affected?
According to the advisory, all versions up to and including 4.1.0. The issue is fixed in 4.1.1, released on 27 July 2026. Upgrade to 4.1.1 or later.
Am I affected if I run the server over stdio?
No. The check only applies when the server runs over SSE or Streamable HTTP. In stdio mode the caller is the operator on their own machine, and the fix changes nothing there. You should still upgrade, because the deployment mode may change later.
Why is there no entry on cve.org yet?
The CVE ID has been assigned and is listed in the GitHub advisory. The corresponding record at MITRE was not yet published when we checked on 28 October 2026. Until then, the GitHub advisory is the authoritative source for versions and rating.
Has the vulnerability been exploited?
Public sources say nothing about that. Our review was a source-only analysis in which no third-party system was contacted.
Further reading
- Research: our publicly documented findings
- CVE-2026-15583: SSRF in the Grafana MCP Server Explained
- The OWASP MCP Top 10, Explained in Practice
- Securing MCP Servers: A Practical Guide
- Service: AI pentest & MCP security
Sources
- GitHub advisory: GHSA-6963-p4fp-2wxw (CVE-2026-104776)
- GitHub: mcp-server-kubernetes pull request #355
- GitHub: changes between v4.1.0 and v4.1.1
- GitHub: mcp-server-kubernetes release v4.1.1
- GitHub: pull request #332 and pull request #346
- GitHub: ADVANCED_README.md (transports and authentication)
- npm: mcp-server-kubernetes
- MITRE: CWE-73
- FIRST: CVSS v3.1 Specification
- Model Context Protocol: Transports