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 上,它在第一次按键时就触发。
八项发现、七款代理,其中四项仍未关闭
一半已经修好。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 时写过的同一个
盲点:一份锚定在“什么抵达了模型”的日志,看不见在模型被咨询之前跑过了什么。
那条命令一定会跑。值得回答的问题是:它会找到什么。
让它跑,看看它找到什么
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.firewall、supply_chain.fetch 与
credential.exfiltration 送进组织的事件流。这场攻击在代理端的记录是空白的,而线路端
的记录正载着那个请求。
客户机只够得到你 Mac 上的一样东西。共享文件夹是一扇通往你真实文件系统、活生生且可读写
的窗,而被攻陷后的抹除按设计不会碰它。分享项目目录而不是你的主目录,那么辅助程序那句找
*.pem 的 find,走过的会是一个塞满 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,给那个陌生的文件夹一台属于它自己的机器,
让那份配置在一个“跑”是它唯一能做的事的房间里,爱跑什么就跑什么。