概要:10 月上旬,一批新的安全研究把 MCP 推到了风口上。一篇论文用一句「重复我的话」式的提示,绕过了 6 个主流 Agent 框架,在 343 个 MCP 工具里验证出 253 个漏洞;官方 Python SDK 被曝能骗走 OAuth 凭据;还有研究者演示被攻陷的 Agent 可以在可信网络里向其他 Agent 中继恶意指令。
先看这个月发生了什么
如果你这两个月一直在给自己的 Agent 接 MCP 服务器,下面这三件事值得花三分钟看完。
第一件,一篇论文挖出了 253 个漏洞。
10 月 9 日上传的论文《Repeat After Me》,标题直译过来叫「重复我的话」。研究者的做法很简单:让一个 Agent 把恶意指令原样转述给下一个 Agent。就这么一句,6 个主流 Agent 框架的防护被绕过,工具调用变成了「被混淆的代理人」,也就是 Agent 拿着自己的权限,替攻击者干活。
测试规模不小。研究覆盖 273 个 MCP 服务器、3405 个工具,最终在 343 个工具上验证出 253 个漏洞,分布在 118 个服务器里。分类是:反射输出 115 个、路径穿越 64 个、日志注入 58 个、SSRF 7 个、数据外泄 5 个、操作系统命令注入 3 个、残留临时文件 1 个。
有个细节特别说明问题。论文里有个截图类的 MCP 服务器,它的截图工具接受 file:// 开头的网址,于是会老老实实把本地可读文件和目录列表截成图片返回。它的网址解析器校验了语法,但真正去取页面的时候,没有任何一环在执行「只允许网页」这条限制。语法校验代替了语义约束,这句话基本可以概括这整篇论文。
第二件,官方 SDK 自己漏了。
9 月 29 日,安全公司 Cycode 披露了官方 Python SDK 的一个 OAuth 凭据窃取漏洞。恶意 MCP 服务器可以诱导基于官方 SDK 构建的应用,把 OAuth 客户端密钥、授权码和 PKCE 校验值一起交出来,凑齐这三样就足以铸造出有效的访问令牌。修复版本是 1.30.0 和 2.2.0。
还有 10 月 2 日的 CVE-2026-104120,官方的 mcp-server-fetch 存在服务端请求伪造漏洞,CVSS 7.3,远程攻击者可以借它向内网服务横向移动。
第三件,Agent 之间可以互相投毒。
独立研究者 Syed Anas Mohiuddin 演示了一件事:一个已经被攻陷的 Agent,可以在可信网络内部把恶意指令中继给其他 Agent,涉及的组织里有 Google 和摩根大通。整个过程不需要绕过任何认证,只是把权限用在了不该用的地方。
同期还有 CloudSEK 记录的案例,有人在 AI 编程助手里把一个反弹 shell 注册成 MCP 工具,用来入侵,受害者里包括一家医学影像公司。以及 NEBULA 披露的七个恶意 npm 包,伪装成 AI SDK,实际投递远控木马。
MCP 的信任模型,从设计那天就有缝
上面这些漏洞看起来零散,其实指向同一个根因。
工具描述就是提示词。
MCP 的一次典型连接是这样的:客户端启动服务器,握手,然后调用 tools/list 拿到工具清单,每个工具带着名称、描述和参数结构。问题出在下一步,这些描述会被原封不动拼进模型的系统提示或工具描述区。
也就是说,工具描述在字面上是「给人看的说明」,实际身份是「给模型看的指令」,而且和用户输入、系统提示享有几乎同等的话语权。MCP 规范里没有对描述内容做任何完整性或语义约束。这不是谁疏忽了,是设计上的取舍:想做 AI 界的 USB-C,谁都能发布服务器,那协议层就不做准入审查,把信任决策留给了客户端实现者和最终用户。
代价有三条。
一是描述可以被悄悄改。一个服务器可以先上架一个干净的工具,通过你的审核,之后再把描述改掉。你审过的那份和模型实际看到的那份,可能根本不是同一份。
二是所有服务器共享同一个上下文窗口。你同时接了 A 和 B 两个服务器,那么 A 的输出可以指挥模型去调用 B 的工具。跨服务器的提示注入,土壤就是这么来的。
三是工具的输出也不能信。很多人都把 MCP 工具画在架构图的「可信基础设施」这一侧,但它的返回内容应该画在「不可信数据」那一侧。工具可信,不等于工具的输出可信。工单评论、网页摘要、数据库里的一行记录,都可能夹着「忽略之前的指令」。
还有一个更刁钻的新玩法,来自今年的一项研究,叫错误路径注入。攻击者把指令夹在工具的报错信息里,比如报错说操作失败,请按系统恢复说明去访问某个地址并带上提权参数重试。Agent 读到报错会进入纠错推理模式,而针对普通工具输出的那套拒绝逻辑,在错误上下文里往往不会触发。研究测下来,这种手法把提示注入的成功率提高到了三倍,在四款前沿模型上最高达到百分之百的合规率。夹在错误信息中间的结构,是其中效果最好的变异方式。
加固清单,今晚就能跑
下面这套是按「装之前、跑起来之后、观察与检测、团队层面」四层整理的,纯文字,不需要写代码也能照着做。
第一层,装之前
- 先盘点,再决定留谁。大部分开发者说不清自己到底接过哪些 MCP 服务器。打开配置文件,把所有服务器名字列出来,逐个问三个问题:我知道作者是谁吗,我现在还在用它吗,它需要的权限真的需要吗。三个里有一个答不上来,就删掉。
- 升级到已修复的版本。官方 Python SDK 升到 1.30.0 或 2.2.0 之后,然后重新审一遍所有社区服务器。注意,漏洞补了,但只有你真的升级才补上。
- 锁死版本,别让它浮动。MCP 服务器不要跟着最新版走。在配置里写精确版本号,官方提供了校验和或签名的就去验。Agent 工具链的锁文件,和你的应用依赖一样重要。
- 检查包名是不是仿冒的。有一种攻击是靠相似包名完成的,你以为装的是某个知名 MCP 服务器,实际是另一个作者的陌生包。装之前把包名和官方文档里的名字逐个字符对一遍。
- 扫一遍工具描述。看里面有没有这些特征:忽略之前的指令、读取 .env 或私钥文件、把内容外传、把下载内容直接管道给 shell 执行、base64 解码。有个心理准备,这种正则级别的扫描抓不到把指令拆散在描述和返回值里的高级手法,它只是第一道粗筛。
第二层,跑起来之后
- 给本地服务器划真实的边界。不需要联网的服务器,放容器里跑,去掉网络、文件系统只读、丢弃全部能力、限制进程数量。需要联网的,就不去掉网络,改成在出口做目的地白名单。
- 收窄 OAuth 权限,然后轮换密钥。给 MCP 相关应用只开最小范围。出过任何一次意外,或者你装过某个后来不再信任的服务器,就把客户端密钥和令牌轮换掉。前面那个 9 月的漏洞,被偷走的客户端密钥在换掉之前一直是有效的。
- 关掉自动批准。凡是读文件、跑命令、碰网络的工具,逐个调用都要人工确认。这事确实烦,但上面所有攻击,至少都需要一次你没看过的工具调用才能成立。
- 给工具声明留指纹。把审核通过的工具体系结构做哈希固定下来,实际声明和固定值不一致就拒绝执行。每一次工具描述的变更,都当成一次依赖升级来对待,重新审。
- 隔离环境变量。MCP 服务器是进程,进程会继承环境变量。如果你的服务器和数据库连接串、云访问密钥跑在同一个环境里,那么哪怕它没提供「读密钥」这个工具,已有的工具组合起来也能读到。敏感变量在服务器这一层就丢掉,不要指望 Agent 层面去挡。
第三层,观察与检测
- 审计日志必须离机。每一次工具调用都要记:哪个客户端调的、工具名、参数、结果、时间戳。而且日志要追加写、送到服务器之外的机器上。原因很直接,Agent 如果有文件工具,它对日志文件就有写权限。
- 盯住这几类事件。工具被发现和列举、工具被调用、权限失败、冒出来的新工具、异常的调用量、反复失败的请求、工具或参数结构的重大变更。其中「冒出来的新工具」和「参数结构变更」这两条最值得重点告警,它们正是 Rug Pull 类攻击的必经动作。
第四层,团队层面
- 把 MCP 配置当代码审。任何一个往 .mcp.json 里加一行的合并请求,都要走代码评审,要有白名单和项目范围内的准入策略。现实是没人这么干,所以第一个人在这个目录下启动 Agent 的时候,就用自己的 SSH 密钥和云凭据执行了陌生人的包。
- 接受一个事实:提示注入消不掉。上面这些控制,作用是限制一次注入能碰到什么,而不是把它降到零。研究者的漏洞验证是受控环境里的结果,不等于已经在野大规模利用,但反过来,没有公开案例也不等于没有发生。
最后一句
MCP 对 Agent 做的事,有点像 npm 对 JavaScript 做的事:靠让大家都依赖陌生人的代码,换来了爆发式的强大。当年 npm 生态是用一次次事故,才把「审计、锁版本、沙箱、最小权限」这四件事变成默认识。
MCP 现在就在补这一课。