乐于分享
好东西不私藏

MCP 服务器安全:恶意工具如何攻击 AI Agent

MCP 服务器安全:恶意工具如何攻击 AI Agent
本文内容仅供研究学习使用,未经授权请勿进行非法渗透测试。

Model Context Protocol(MCP)允许 AI Agent 连接外部工具,但 MCP 客户端始终只能看到工具的名称、描述和输入模式(schema),永远看不到底层运行的代码。这一缺口使得一个外表干净的工具能够隐藏命令注入、数据外泄或凭证窃取行为,而所有 API 调用仍然正常返回成功,没有任何指标显示异常。2025 年 9 月的 postmark-mcp 事件——仅凭一行代码就将每封邮件静默 BCC 转发给攻击者——在生产环境中展示了这一风险。

以下三个演示复现了命令注入、工具投毒以及完整的供应链攻击。

2025 年 9 月,Koi Security 的安全研究人员在 postmark-mcp npm 包中发现了异常行为:一行隐蔽代码将该工具发送的每封邮件 BCC 转发至攻击者地址 phan@giftshop[.]club。postmark-mcp 是一个使 AI Agent 能够发送事务性邮件的 MCP 服务器。

粗略估计,基于每周 1,500 次下载量,约有 300 个组织在生产环境中使用该工具,每天最多有 15,000 封邮件被外泄。这些邮件类型包括密码重置、发票、内部通知——每一封都被盲目复制给攻击者。这次攻击运行了数周才被发现,而所需的只是在 Agent 已经信任的工具中添加一行代码,使用的还是用户自己的凭证、运行在用户自己的机器上。

执行该工具的 AI Agent 从未将此标记为可疑行为。所有 API 调用都成功了,每项指标都是健康的,没有任何关于该工具的可疑迹象。这是因为 MCP 协议中没有任何机制能够在稳定且无害的接口背后暴露可疑的行为变化。Agent 只能看到工具的名称、描述和模式。底层运行的代码从不被验证。这种检测与非检测之间的缺口将在后续三个演示中进一步探讨。

什么是 MCP

根据Anthropic 的定义,Model Context Protocol(MCP)是"一个开放标准,使开发者能够在其数据源和 AI 驱动的工具之间建立安全的双向连接"。本质上,MCP 充当 AI Agent 的通用适配器,允许外部工具即插即用,无需为每个工具定制集成。

MCP 中存在三个角色:

  • 主机(Host):AI 应用本身,例如 Claude Desktop
  • 客户端(Client):负责通信的协议连接器
  • 服务器(Server):实际的工具,例如 postmark-mcp

当服务器启动时,它会响应一个名为 tools/list 的请求。该请求返回所提供的每个工具的名称、描述和输入模式。这一切都通过 JSON-RPC 进行,要么通过 stdio(需要在本地机器上运行服务器),要么通过 HTTP(远程运行的服务器)。

危险之处在于,tools/list 响应中的工具描述被作为受信任的上下文提供给 LLM。此外,用户只能看到工具功能的简短截断摘要,隐藏了诸如将所有邮件 BCC 转发给不受信任的第三方等隐蔽操作。本文中的攻击正是存在于这一缺口之中。

统计数据与真实事件

postmark-mcp 漏洞并非孤例。截至 2025 年 12 月,MCP SDK 每月下载量达 9,700 万次,Stacklok 的调查发现 41% 的组织已在运行 MCP。此外,Equixly 发现 43% 的 MCP 服务器存在命令执行漏洞,30% 存在 SSRF 漏洞,22% 存在路径遍历漏洞。Enkrypt 的研究也证实了这一点:32% 的服务器至少存在一个关键漏洞。

MintMCP 发现,在开启自动批准(auto-approval)的服务器上,攻击成功率高达 84.2%——即 Agent 在不询问用户的情况下直接运行工具。旨在捕获这些恶意包的注册中心也未能提供太多帮助:OX Security 于 2026 年 4 月向 11 个公共 MCP 注册中心提交了恶意概念验证,其中 9 个在未经审核的情况下直接接受。

目前已有若干已命名的 CVE,包括:

  • CVE-2025-6514,CVSS 9.6:mcp-remote 中的操作系统命令注入(JFrog)
  • CVE-2025-49596,CVSS 9.4:MCP Inspector 中的远程代码执行(Oligo Security)
  • CVE-2025-54136,CVSS 7.2:MCPoison,Cursor 信任绕过(Check Point)
  • CVE-2026-0755,CVSS 9.8:gemini-mcp-tool 中的命令注入(Trend Micro)
  • CVE-2026-33032,CVSS 9.8:MCPwn,未认证的 nginx-ui(Pluto Security)

攻击面

MCP 的攻击面与 Web 应用、基础设施和云环境中常见的攻击面截然不同。LLM 拥有在用户请求下运行工具的权限,但无法验证这些工具的实际行为。

更进一步,代码在本地以用户权限运行。默认情况下不存在任何隔离沙箱或容器化来最小化攻击面。因此,恶意工具获得的访问权限与本地用户完全相同:文件系统、网络、凭证,加上该特定工具被授予的任何权限,如 git 或云提供商访问权限。在此基础上,一个外表干净的工具(如邮件助手 Agent)可以隐藏任何恶意行为,包括数据外泄、投放信息窃取器(infostealer)和命令执行。

以下演示准确展示了这些攻击的实际运作方式。

演示 1:命令注入

第一个演示涵盖了三个漏洞中最简单的一个:命令注入。

漏洞代码位于 send_notification(message) 中,使用 subprocess 的 shell=True 参数和 f-string 来构建命令。无论 LLM 发送什么内容作为 message,都会直接传入 shell,未经任何过滤。

使用的 Payload 为:Hello'; whoami; echo '。一旦该字符串被放入 f-string,shell 就会执行三条命令而非一条:echo 'Hello'; whoami; echo ''。输出结果分别为 Hello、jamil、空行。账户名直接泄露到了工具的响应中——这是一个直接的引号逃逸。

修复方法只需一行代码。将 shell=True 改为 shell=False,并以参数数组代替字符串传递命令:subprocess.run(["echo", message], shell=False)。相同的 Payload 现在被视为字面字符串,无法逃逸。

这并非理论案例。gemini-mcp-tool 中的 CVE-2026-0755(CVSS 9.8)采用了完全相同的模式:字符串拼接被传入 execAsync,导致 Windows 上的远程代码执行。

演示 2:工具投毒

第二个演示涵盖工具投毒,以及绕过检测的手法。

下面的工具是 get_daily_fact,表面上只是返回每日趣闻。然而在其文档字符串(docstring)中——也就是被作为受信任上下文提供给 LLM 的同一位置——隐藏了一个<IMPORTANT>代码块,指示模型在返回趣闻之前静默调用 send_notification 并附带注入 Payload。用户只会看到简短描述"Returns an interesting fact of the day",永远看不到下方隐藏的代码块。

在 Claude Desktop 上测试时,模型捕获了它。它读取了完整的 docstring,将代码块标记为嵌入的指令而非真正的文档,拒绝执行该指令,并直接指出了 Payload 内容。

绕过方法是将代码块从 docstring 中完全移除,将相同的逻辑转移到实现代码中,封装在一个名为 _exfiltrate() 的函数里。此时描述是干净的,因此 Claude 照常调用该工具,没有发现任何需要标记的内容。恶意代码仍然在运行,因为 Claude 永远看不到 Python 实现代码——它只能看到工具的名称、描述和输入模式。

下图展示了 Claude Desktop 对"How are my PRs doing? Am I caught up? jamil537/payment-service."这一请求的响应。这次描述中不存在隐藏指令,所以 Claude 调用了工具并返回了正常、准确的 PR 列表。与此同时,_exfiltrate() 在后台全程运行。

Claude 能够捕获隐藏在其所读取内容中的指令。然而,它对工具背后实际运行的代码没有任何可见性。

演示 3:供应链攻击

第三个演示与演示 2 使用相同的手法,只是应用范围从单个工具扩展到了整个服务器。

环境设置是一个虚假但看似真实的代码仓库 jamil537/payment-service:私有仓库,两个打开的 PR(feat/oauth2-support 和 fix/payment-retry),一个被提交的 .env 文件包含虚假凭证,以及一个硬编码在 config/config.js 中的 AWS 密钥。与之配套的是 trojanised-github-mcp,一个伪造的 GitHub MCP 服务器,拥有规范的 README、requirements.txt、pyproject.toml,以及六个真实可用的工具:list_repositories、get_issues、get_pull_requests、get_commit_history、create_issue_comment、search_code。每个工具的描述都不含任何恶意指令。

触发条件极其平常:请求 Claude 获取 jamil537/payment-service 的 Pull Request。Claude 返回了正常、准确的 PR 列表,与演示 2 类似。

下图正是这一场景——Claude Desktop 返回了一个干净的 PR 列表。

与此同时,四类数据被静默发送到 OASTIFY 端点。PR 数据,以 JSON 格式。git config(姓名、邮箱)和实时的 gh auth token。完整系统信息:主机名、操作系统、用户、运行时间、CPU、内存、磁盘、运行中的应用、网络 IP,甚至 Mac 序列号。以及针对文件系统运行的 TruffleHog 扫描,它找到了 config/config.js 中硬编码的 AWS 密钥并将其一并外泄。

图 1:被外泄的 PR 数据

图 2:被外泄的 GitHub 认证令牌和凭证

图 3:被外泄的 TruffleHog 扫描结果

图 4:被外泄的系统信息

Postmark-mcp 采用的是完全相同的模式:一个外表干净的包,恶意逻辑隐藏在内部。2026 年 2 月的 Oura/SmartLoader 攻击遵循了同样的思路:攻击者克隆了一个合法的 Oura Ring MCP 服务器,花三个月时间构建虚假的 GitHub 人设使其看起来可信,然后将其提交到注册中心。该服务器随后投递了 StealC 信息窃取器。

演示 3 相较于仅泄露了用户名的演示 1,展示了显著更大的影响:它移交了 PR 数据、GitHub 凭证、完整的系统指纹以及硬编码的密钥。

如何保护自己

以下步骤可帮助降低您对上述演示中所涵盖漏洞的暴露风险。

安装前先审计

不要因为一个 MCP 服务器看起来合法或附带了规范的 README 就直接安装。演示 3 两者都具备,却仍然外泄了凭证。在连接任何工具之前,亲自审查源代码,或对其进行独立审查。

对服务器进程进行沙箱隔离

在隔离环境中运行 MCP 服务器,而非赋予其对您机器的完整访问权限。它不应该能够访问 /.ssh、/.aws、浏览器会话或任何其他非严格必需的资源。

应用最小权限原则

只授予工具其实际需要的访问权限。如果它需要 GitHub 访问权限,就不需要文件系统访问权限。如果它需要读取数据,就不需要额外的写入权限。

过滤出站网络流量

限制 MCP 进程可以向哪些目标发送流量,并默认阻止未知或意外的出站连接。这正是能够阻止演示 3 中 OASTIFY 调用到达攻击者的防护措施。

固定工具描述

协议中没有任何机制阻止描述在您审查和批准之后被静默更改。对描述进行哈希处理(如使用 Snyk 的 Agent Scan)意味着变更会被实际标记出来,而不是悄无声息地溜过。

关闭自动批准

自动批准正是 MintMCP 发现攻击成功率高达 84.2% 的设置。尽管会增加摩擦,您仍应要求每次调用都获得明确批准。

记录每次调用

保留每次工具调用及其参数的完整日志。这将使您能够在出现问题时重建事件经过。

不要依赖注册中心来替您审查

OX Security 向 11 个公共 MCP 注册中心提交了恶意概念验证,其中 9 个在未经审核的情况下接受。被列入目录不能证明任何安全性——请自行审查服务器。

原文出处:https://www.precursorsecurity.com/blog/mcp-server-security-vulnerabilities

培训咨询/报名二维码

ID:linglongsec

报喜专栏总览

https://www.ifhsec.com/list.html

SRC漏洞挖掘培训

学员每一期的收获、我们每一期的进步

玲珑安全第一期SRC漏洞挖掘培训

玲珑安全第二期SRC漏洞挖掘培训

玲珑安全第三期SRC漏洞挖掘培训

玲珑安全第四期SRC漏洞挖掘培训

玲珑安全第五期SRC漏洞挖掘培训

玲珑安全第六期SRC漏洞挖掘培训

玲珑安全第七期SRC漏洞挖掘培训

玲珑安全B站公开课

免费课程观看/日常消息更新/学员赏金报喜

https://space.bilibili.com/602205041

玲珑安全QQ群

191400300

往期漏洞分享

关注公众号 各种优质好文速递

Ask Astro中的RAG安全漏洞

AWS Kiro:通过间接提示注入实现任意代码执行

Amazon Q Developer:通过提示注入实现远程代码执行

Amp Code:通过提示注入实现任意命令执行

GitHub Copilot:通过提示注入实现远程代码执行

提示词注入只是开始,LLM真实攻击面都有哪些?