返回所有文章
发布于 · 作者 Renaud Deraison

git status 执行了他们的代码

2026 年 9 月 2 日,Manifold Security 披露了 GitSpawn:横跨七款命令行编码代理的八个缺陷,仓库自己的 .git/config 就能在开发者的机器上执行一条命令。代理跑 git status 想知道自己在哪个分支,git 顺手刷新索引,于是仓库选定的命令就以登录用户的权限执行,在沙箱之外,早于信任提示,而在其中一款工具上甚至早于登录。没有提示词,没有工具调用,没有模型回合。Bromure Agentic Coding 不去给这条路径打补丁,而是换掉这条路径通向的那台机器。

你什么都没敲,也什么都没批准。代理还没启动完,而在其中一款工具上,你甚至还没 登录。某个人发给你的文件夹以你的身份执行了一条命令,而执行它的正是 git。

一位外包完成了一段工作,把项目目录的 zip 放进共享文件夹发给你。你解压、cd 进去, 启动编码代理来熟悉一下情况。代理开头就问 git:这是哪个分支、哪些文件有改动。整个漏洞 利用就这样结束了。

2026 年 9 月 2 日,The Hacker News 报道了 Manifold Security 的一则披露, 内容涵盖横跨七款命令行编码代理的八个缺陷,以 GitSpawn 之名发布。这些受影响的工具加起来 接近五十万颗 GitHub 星标,光是 Claude Code 就 每月超过 7,700 万次 npm 下载。 八个之中有四个,在 Manifold 于 9 月 1 日重新测试时仍会执行仓库提供的命令。

一个值就是命令的性能设置

core.fsmonitor 是 git 给超大型仓库使用者的选项。与其走遍整棵树来找出哪里变了,git 可以调用一个外部辅助程序去问。这个设置的值本身就是那条命令。git 从仓库自己的 .git/config 里读出它,并在每次刷新索引时执行,也就是在 git status、在 git diff、 在每个工具都依赖的底层管道上执行。

Manifold 检视过的代理几乎全都在启动时跑这些命令来收集项目上下文,而它们没有一个会先看 一眼 .git/config。于是仓库指定命令,git 照跑不误。用研究人员的话说,它是“以登录用户的 全部权限,在沙箱之外”执行。OpenAI 在自家针对 Codex 变体的公告里也说,这个辅助程序 “在 Codex 的命令沙箱之外运行,而且没有用户批准提示”。

git clone 完全不会带上这些。克隆传输的是对象和 ref,源仓库的本地配置留在原地。所以 被投毒的项目必须以文件的形式、连同完好的 .git 目录送到你手上:一份 zip 压缩包、 一个共享盘、一个同步文件夹、一支 U 盘。Manifold 列出的正是顾问、客户与同事交接工作的 方式,也是唯一一条完全没有 pull request 的送达路径。git 自己防范恶意仓库的机制 safe.directory 检查的是这个仓库是不是别人的。是你解压的,所以它是你的,于是这道防线 无话可说。

时序让每款代理自己的保护都晚了一步。在 Claude Code 与 Hermes Agent 上,载荷在工作区 信任提示之前触发,而那个对话框存在的目的正是问你信不信任这个目录。在 Qwen Code 上, 它在用户完成认证之前触发;在 Grok Build 上,它在第一次按键时就触发。

GitSpawn — 从交付到 shell文件夹送到zip、共享盘、同步文件夹、U 盘 —.git/config 完好代理开始启动收集上下文:git status · git diffgit 刷新它的索引读取仓库自己的配置core.fsmonitor = …<他们的命令>你的用户、你的环境、你的主目录还没发生的事信任提示Claude Code,Hermes Agent登录Qwen Code 在认证前就先触发敲下任何一个字Grok Build 在第一次按键时就触发模型没有工具调用、没有回合、没有东西可记上排每一道关卡都是代理启动流程里的一步。命令在流程走到它们之前就已经跑完了。
整条链,以及它跑完时还没发生的四件事。仓库的 .git/config 在 core.fsmonitor 里指定一条命令;代理跑 git status 想知道自己在哪个分支;git 刷新索引并调用那个辅助程序。它以登录用户的身份执行,在 Claude Code 与 Hermes Agent 上早于信任提示,在 Qwen Code 上早于认证,在 Grok Build 上则在第一次按键时,而这整段流程里没有任何工具调用,也没有任何模型回合。

八项发现、七款代理,其中四项仍未关闭

一半已经修好。goose 1.44.0 带着 CVE-2026-72718(CVSS 7.0)的修复。 OpenAI 为 CVE-2026-19592 推出了 Codex CLI 0.131.0 与 26.519.x 的桌面版构建。Cursor 也 修补了自己的变体。Manifold 在 2.1.193 上确认了 Claude Code 的 core.fsmonitor 路径, Anthropic 在 2.1.196 关闭了它,但没有发布公告:Manifold 指出,关于 Claude Code 的这两项 发现都没有出现在 npm 的公告记录里。

另外一半原地不动。Hermes Agent 有一个 CVE(CVE-2026-71963),以及一份在六次联系之后仍 未被分类处理的私有公告。Qwen Code 的维护者在 7 月 7 日接受了报告,而 0.22.3 仍在执行那条 命令。Grok Build 也仍在执行。而 Claude Code 还有第二条路径,走的是它的 ultrareview 命令,滥用另一个 git 配置键,在 2.1.252 上依然活着。Manifold 选择不公开那个键的名字, 以免发出一份可重复使用的攻击模板。

Anthropic 三天内修好了第一条路径。这一类缺陷则从同一个产品、同一个文件里的另一个键绕了 过去。

这是 git 的执行面,由每一个会调用 git 的工具继承下来,而 git 不止一个设置的值是它会替你 执行的命令。分页器、编辑器、SSH 命令、hook 路径、diff 与 filter driver,以及以 shell 转义开头的别名,全都接受命令值。现在每一家厂商都得逐键梳理,其中哪些是仓库从启动路径 就够得着的,而对手是一份会随时间长出新键的配置模式。Manifold 建议的修法,也就是在后台 上下文操作期间清理 git 配置并禁用 core.fsmonitor,本质上是一份要拿来对抗整个命名空间 的拒绝列表。研究人员在比报告点名还多的代理里找到了同样的模式。

本来能拦下这件事的控制,都在它的下游

把一支谨慎的团队今天架在编码代理上的东西排开来看:工具使用时的权限提示、工作区信任、 在命令执行前检查它的 hook、把会话送往合规后端的记录端点,以及用于权限决策与绕过的 OpenTelemetry。

这些全都锚定在代理自己的循环上,也就是锚定在代理决定发出的某次工具调用,或模型走过的 某个回合上。GitSpawn 两者都不产生。执行载荷的进程是 git,是代理为了自己整理环境而 调用的,而命令来自一个代理从来没读过的文件。没有工具调用可以批准,也没有 hook 会触发。 一支看着代理记录落进自家 SIEM 的团队,只会看到一段正常开始、什么都没显示的会话,正是 我们在 Anthropic 推出 Compliance API 时写过的同一个 盲点:一份锚定在“什么抵达了模型”的日志,看不见在模型被咨询之前跑过了什么。

那条命令一定会跑。值得回答的问题是:它会找到什么。

解压到你的主目录git status → core.fsmonitor 辅助程序由文件夹指定,以你的身份执行只隔一个目录的东西~/.ssh/id_ed25519ANTHROPIC_API_KEY, AWS_SECRET_ACCESS_KEY~/.git-credentials, ~/.aws, ~/.kube/config~/.zshrc — 下次登录时的持久化对外:一个普通开发工具的普通连接,走普通的端口代理自己的日志里没有任何一行文件夹不受信任。它落地的那个账户受信任。在 Bromure 工作区里打开git status → core.fsmonitor 辅助程序照样跑 — 以 ubuntu 身份,在客户机里一次性 Linux VM · 早于文件夹就开机没有私钥字节 — 由主机 ssh-agent 签名env 与配置里的凭据全是诱饵 (brm_…):80 与 :443 导入主机代理,无法关闭对外防火墙:默认拒绝 → 没有第二阶段诱饵离开它的范围 → 451,VM 暂停,工作区被标记,要抹除才能再开机每个请求都有一行,不管是哪个进程发的命令照样跑。只是跑在一个为它而建的地方。
同一个文件夹、同一个 git status,两台不同的机器。在你的笔记本上,辅助程序以你的身份执行,离你的 SSH 密钥与云凭据只有一个目录的距离,而它的对外连接看起来就是一个普通开发工具的普通连接。在 Bromure 工作区里,它以 ubuntu 的身份,在一台早于这个文件夹存在就开机的 VM 中执行:没有私钥字节可偷,凭据该在的地方摆的是诱饵,而每一个出去的字节都走在主机拥有并记录的线路上。

让它跑,看看它找到什么

Bromure Agentic Coding 为每个工作区配一台跑在 Apple hypervisor 上的 专属 Ubuntu VM。代理、它的 git、它的包管理器,以及这三者启动的任何东西,全都住在客户机 里。把外包的 zip 丢进一个工作区,让辅助程序触发,并且把一切都给攻击者:信任之前、认证 之前,你想的话连客户机里的 root 都给。

读取 SSH 密钥。 没有密钥可读。工作区的私钥留在你的 Mac 上;VM 拿到的是一个通过 vsock 桥接到主机端代理的 SSH_AUTH_SOCK。那个协议有一种请求叫“替我签这个挑战”,却 没有任何一种请求叫“把密钥给我”。

扫一遍环境找凭据。 凭据全都在,形状也符合验证器的期待,而且全都是诱饵。 ANTHROPIC_API_KEY 里放的是假的 sk-ant-api03-brm-…~/.git-credentials~/.aws/config~/.kube/config~/.docker/config.json 也一样。你真正的值加密存在 你的 Mac 上,而代理服务器是在请求离开 VM 之后、且只有在它要去的正是该凭据所铸给的那台 主机时,才把真值换到线路上。没有任何开关能关掉这件事。这就是凭据在这个应用里运作的 方式。

拉下第二阶段。 那个辅助程序是个加载器,几百个字节,工作就是去把真正的载荷抓回来。 这次抓取会撞上工作区的对外防火墙。把未匹配流量设成 Deny,VM 就只到得了你列出的主机, 其他一概不通,而且在两个地方强制执行:虚拟交换机以目的 IP 与从 DNS 嗅到的主机名在所有 协议上匹配,代理服务器再以 TLS 服务器名匹配一次,对 web 规则还会匹配 HTTP 方法。如果 加载器改去伸手向包注册表,它遇到的是供应链流水线,那里的年龄门槛会拒绝比截止日更新的 版本。昨天才发布的载荷过不了这一关。

把找到的东西送出去。 到这里,总得有东西越过一条你的 Mac 拥有的线路。代理服务器会用 一个 Aho-Corasick 自动机,把每一个对外请求的头部与正文比对这个工作区铸出的诱饵。一个 诱饵被送往它铸造范围之外的主机,正是一台机器正在送出它不该知道的东西的签名。代理服务器 会在任何一个字节抵达目的地之前以 HTTP 451 挡下,暂停 VM,并发出一条点名该凭据与两台主机 的告警。接着 Bromure 会把这个工作区标记为已被攻陷,在你抹掉它的磁盘与主目录之前,它不会 再开机。

留下持久化。 客户机里被改过的 .bashrc 只能活到你让这台 VM 活到什么时候。抹除 主目录会重置 /home/ubuntu重置磁盘会从签名过的基础镜像重新克隆系统盘,而 bromure-cli vm run --rm 给你的工作区会在 VM 停止时自我删除。

这个拦截没有任何环境变量可以取消

一个要客户机自己选择加入的代理服务器,就是客户机可以拒绝的代理服务器。Bromure 的虚拟 交换机直接把 VM 的 80 端口与 443 端口流量导进主机的代理服务器:没有 HTTPS_PROXY 可以取消,客户机里没有任何东西关得掉它,而纯 HTTP 也和 HTTPS 一样被检查。这里发出 请求的那个进程从没读过代理的配置,它也不是代理。它是一个 git 启动的辅助可执行文件, 而它走的是跟其他所有东西一样的那条线。

就算没有任何工具调用,那一行还是存在

主机的代理服务器会为每一个离开 VM 的请求写下一条元数据记录,不论是哪个进程发的: 时间、主机、端口、方法、路径、状态、延迟,以及在换上真凭据之前量到的字节数。防火墙 判定、供应链决策与凭据代管都会落进安全时间线(窗口 → 安全时间线…),在已纳管的 Mac 上,它们还会以 egress.firewallsupply_chain.fetchcredential.exfiltration 送进组织的事件流。这场攻击在代理端的记录是空白的,而线路端 的记录正载着那个请求。

客户机只够得到你 Mac 上的一样东西。共享文件夹是一扇通往你真实文件系统、活生生且可读写 的窗,而被攻陷后的抹除按设计不会碰它。分享项目目录而不是你的主目录,那么辅助程序那句找 *.pemfind,走过的会是一个塞满 checkout 与包缓存的 Linux 主目录,而不是你那些靠 钥匙串撑着的登录信息。

那条不在流程里的边界

把 GitSpawn 的四个时序细节放在一起读——早于信任提示、早于认证、早于第一次按键、早于任何 模型回合——它们说的是同一件事。每一道保护都是启动流程里的一步,而每个新功能都会在它前面 再插一步。Cursor 在 2025 年因为仓库提供的 mcp.json 学过一次,今年 7 月又因为一条 早于自家信任提示执行的 worktree 设置命令再学一次。 现在轮到 git 了,一次七个产品,走的是一个在 git 里已经存在多年的配置键。

hypervisor 不是那个流程里的一步。它不读 .git/config。没有任何重构能把它重新排序,也 没有任何代码路径能豁免于它,因为它根本不跑在和那个提示同一个程序里。zip 还没下载完, VM 就已经在跑了,而它的两项保证——这里面没有真正的秘密,以及没有未经中介的对外路径——在 2.1.193 和在 2.1.196 一样成立。

在打开别人发给你的文件夹之前,先看一眼你的 .git/config。Manifold 这样说是对的。你大概 也会照做两个星期,然后在某个忙碌下午的第三个 zip 上跳过,而那差不多就是大多数以“打开 之前”收尾的指示的保质期。 安装 Bromure Agentic Coding,给那个陌生的文件夹一台属于它自己的机器, 让那份配置在一个“跑”是它唯一能做的事的房间里,爱跑什么就跑什么。