先解析路径,再检查:20 行代码中的 Copilot MCP 配置 bug
最初发布于 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 服务器的那一个文件。这道防护会把请求的文件名当作字符串来比较。而实际写入时,走的却是文件系统解析后的路径。这两件事并不总是一致。
披露文章展示了三种突破方式:
- 结尾的点。 在 Windows 上,
settings.json.和settings.json最终指向同一个文件,但它们是不同的字符串。防护检查没匹配上,就放行了。 - 路径穿越。 一个从受信任目录内部开始的路径,仍然可以用
..落到目录外面。 - 模型中的提示注入,也就是攻击者最初把一条恶意路径塞进工具调用的方式。
一旦写入落到 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,目前已修复。