先解析路径,再检查:20 行代码中的 Copilot MCP 配置 bug

Dev.to AI 2026-10-26T03:51:01.872394

最初发布于 webofmike.com,日期为 2026-10-21。演示仓库在发布前已实际运行过。如果你的智能体(agent)能选一个文件路径,而你的代码要判断这次写入是否允许,那就去检查两行代码的先后顺序。「先检查、后解析」是 bug,「先解析、后检查」才是修复。这就是 CVE-2026-103572 的简短版——一个存在于 Visual Studio 和 SQL Server Management Studio 的 Copilot 组件中的远程代码执行漏洞。该漏洞由 embracethered.com 披露,CVSS 评分 8.8,微软已在 2026-10-13 的补丁星期二修复。

出了什么问题

Copilot 维护了一份受保护配置文件的清单,其中包括注册 MCP 服务器的那一个文件。这道防护会把请求的文件名当作字符串来比较。而实际写入时,走的却是文件系统解析后的路径。这两件事并不总是一致。

披露文章展示了三种突破方式:

一旦写入落到 MCP 设置文件,剩下的事情就简单了。加一条服务器条目,把它的启动命令指向你控制的东西,IDE 下次加载 MCP 服务器时就会运行它。概念验证程序弹出的是 calc.exe。

据报道,修复方案加了一次 GetFullPath 调用,让路径在任何其他代码查看它之前先被规范化。

并非 Windows 专属的那部分

结尾的点只在 Windows 上生效。.. 那种情况在任何平台都成立,而且本质上是同一个错误:你验证的是一个字符串,写入的却是另一个路径。

我在 themsquared/agent-config-guard-bypass 里做了一个可移植的复现。智能体可以在 workspace/ 里写入。一个同级目录 agent_home/ 存放着 mcp_settings.json,它决定哪些 MCP 服务器会被启动。智能体本不该碰到它。

有漏洞的防护代码:

def guard_write(requested_path: str, content: str) -> Path:
    candidate = WORKSPACE / requested_path  # BUG: 在 ".." 被解析之前,就按原始字符串检查了
if not str(candidate).startswith(str(WORKSPACE)):
    raise PermissionError(f"refused: {requested_path} escapes workspace")
resolved = candidate.resolve()  # ".." collapses here, after the check
resolved.parent.mkdir(parents=True, exist_ok=True)
resolved.write_text(content)
return resolved

这时让它写 ../agent_home/mcp_settings.json。在做路径解析之前,这个字符串确实以工作区路径开头,所以检查能通过。然后 .resolve() 把 .. 折叠掉,写入就落到了工作区外面,正好写进那个决定启动什么内容的配置文件。

修好的版本只是调换了一个调用的位置:

def guard_write(requested_path: str, content: str) -> Path:
    resolved = (WORKSPACE / requested_path).resolve()  # resolve FIRST
    if resolved != WORKSPACE and WORKSPACE not in resolved.parents:
        raise PermissionError(f"refused: {requested_path} escapes workspace")
    resolved.parent.mkdir(parents=True, exist_ok=True)
    resolved.write_text(content)
    return resolved

现在检查和写入用的是同一个规范化路径。手里没有原始字符串可以拿来做手脚了。

跑起来看看:

git clone https://github.com/themsquared/agent-config-guard-bypass.git
cd agent-config-guard-bypass

python3 exploit_demo.py

有漏洞的版本会把一个新的“更新检查”服务器写入 mcp_settings.json。修复后的版本会在任何写入发生之前拒绝同样的调用。

还可以往哪里找

这个漏洞出现在 IDE 里,但这种模式在任何“LLM 选路径、代码照着执行”的地方都可能重演:agent 框架自己的工具处理器、MCP 服务器的工作目录沙箱、一组 MCP 工具前面的策略检查。这些地方,只要 startswith() 排在 resolve() 前面,就可能踩进同一个坑。修复不需要引入新依赖,也不需要重新设计架构。

仓库没有覆盖的部分:末尾带点号的变体(需要 Windows 和 NTFS 环境),以及提示注入的投递方式——那属于模型层面的问题,不是文件写入的问题。这两块内容都在原始文章里。完整文章(含 CVE 常见问题,以及为什么 MCP 配置文件会成为远程代码执行的攻击目标)见:webofmike.com/trailing-dot-mcp-config-bypass


最初发布于 webofmike.com,2026-10-21。演示仓库在发布前已经跑通。

如果你的 agent 能挑选文件路径,而你的代码要决定这次写入是否放行,那就去看那两行代码的顺序。先检查、后解析,就是漏洞;先解析、后检查,就是修复。这就是 CVE-2026-103572 的简短版本——一个存在于 Visual Studio 和 SQL Server Management Studio 的 Copilot 组件中的远程代码执行漏洞。该漏洞由 embracethered.com 披露,CVSS 评分 8.8,目前已修复。

查看原文