代理从未离开沙箱
2026 年 7 月 20 日,Pillar Security 公布了横跨 Cursor、OpenAI 的 Codex CLI、Google 的 Gemini CLI 与 Antigravity 的七个沙箱逃逸。没有一个真正突破了沙箱。代理待在里面、遵守每一条规则,只是写下一个普通的文件——一份 hook 配置、一个虚拟环境、一项 git 设置——而主机上一个受信任的工具读取了它并自行执行。沙箱守住了;逃逸依然发生。Bromure Agentic Coding 把界线画在这一招无处落脚的地方。
先前每一则逃逸故事都在讲墙被攻破。这一则讲的是墙守住了,逃逸却依然发生。 代理服从了沙箱,写下一个它获准写入的文件,而墙另一侧一个受信任的程序捡起 那个文件并执行了它。
编码代理在沙箱中运行,是为了当它做出鲁莽之事时——而它一定会,因为它整天读取 不可信的文字,并且大多都相信——伤害会止于沙箱的墙。这就是那个承诺。2026 年 7 月 20 日,Pillar Security 公布了这个承诺破裂的七种方式,横跨最常用的四个代理:Cursor、OpenAI 的 Codex CLI、Google 的 Gemini CLI 与 Antigravity。 BleepingComputer 在同一周作了报道。
令人意外的数字不是七。这七者共通之处在于:用研究人员自己的话说,"在几乎所有 案例中,代理都不需要直接突破沙箱。它只需要写下某个东西,让沙箱外一个受信任的 组件稍后去执行、加载、扫描,或当作安全来对待。"
这一招——待在里面、写下文件、让主机执行它
想想编码代理的沙箱实际上围住了什么。它围住了代理的进程——执行模型决策的那个
东西。它并不围住代理的文件,因为整份工作就是写文件:源码、配置、构建脚本。
这些文件落在你其他工具所读取的同一块磁盘上。而你其他的工具并不在沙箱里。你
编辑器的 Python 扩展不在沙箱里。你的 git 不在沙箱里。Docker 守护进程不在
沙箱里。它们信任磁盘上的东西,因为直到不久前,唯一往那块磁盘写入的就是你。
所以代理从不碰那道墙。它就在该写的地方写下一个文件,然后等着另一侧有什么东西 去读它。Pillar 的七个逃逸,就是这同一招的七个版本:
一份会执行的 hook 配置
在 Cursor 中,代理写下一份工作区的 .claude hook 配置。Hook 本就用来在
编辑器事件时执行命令——于是那个文件里的命令便执行了,在沙箱之外。获配
CVE-2026-48124,已在 Cursor 3.0.0 修复。
一个被掉包的 Python 解释器
代理编辑项目虚拟环境内的解释器。编辑器的 Python 扩展——在沙箱之外——稍后 调用它,被掉包的可执行文件便执行了。以 GHSA-p9g2-cr55-cw9c 追踪。
一个因名称而被信任的 git 命令
Codex CLI 的"安全命令"允许清单放行了一次 git 调用,因为名称看起来无害,
却没读取使其危险的参数。已在 Codex CLI 0.95.0 修复。
一个等于第二台机器的守护进程
在数个代理上,Docker socket 从沙箱内部就能触及。一个有特权的本机守护进程, 就是整整第二个可以执行代码的地方,就在边界外侧。以 GHSA-v4xv-rqh3-w9mc 追踪。
Antigravity 又加了两个相同形状的——一份漏掉某个案例的 macOS Seatbelt 拒绝清单, 以及一份代理写下、IDE 之后自行执行的 VS Code 任务配置。两者都被评为较难达成, 但都与其余的押着同一个韵。
为何逐一修补并不能封住这个形状
这些每一个都拿到了修补,而且都是好的修补。Cursor 推出了 3.0.0,Codex CLI 推出 了 0.95.0,Docker socket 被锁了起来。如果你用这些工具,就去更新它们。但看看在 这里一个修补必须做到什么。它必须找出代理能写、而某个别的程序会当作指令看待的 每一个文件——hook 配置、解释器路径、git 元数据、任务定义、lint 规则,以及下一 季会冒出来的任何东西——并把每一个都当成特例来处理。那是一份拒绝清单,而 Pillar 把拒绝清单列为四个反复出现的失效模式之首,因为"一个从'默认允许'出发的沙箱配置 文件,必须记住操作系统所公开的每一项危险操作"。操作系统不断加入新操作。这份 清单永远列不完。
更深的理由,是报告里一句读来像定义的话:"如果一个代理能写下系统未来的输入, 那它从一开始就没被装进沙箱。"在一台代理与其他一切共享磁盘与一组受信任守护进程 的机器上,围着代理进程画出的界线,是围错了东西。他们写道,波及范围"不是代理的 进程;它包含代理能写、而主机之后会信任的一切"。在开发者的笔记本电脑上,那几乎 就是一切。
Bromure 把界线画在哪里
Bromure Agentic Coding 不试着把危险文件一一列举,也不试着把 hook、虚拟环境和
任务运行器教给沙箱。它移动界线。每个配置文件的代理都在 Apple Silicon 上一台一
次性 Linux VM 里运行,与 macOS 隔着一道虚拟机管理程序。在这个模型里,"沙箱外那
个受信任的工具"——你编辑器的 Python 扩展、你的 git、Docker 守护进程、IDE 的
任务运行器——并不在一道权限墙的另一侧。它在一道虚拟机管理程序的另一侧,而且根本
不读取这台 VM 的磁盘。
这就是全部的差别。Pillar 那一招需要两样东西在同一个地方:一块代理能写的磁盘,
以及一个会读那块磁盘的受信任程序。把代理的磁盘放进 VM 里,第二样就没了。代理
可以写下一份 hook 配置、掉包一个虚拟环境解释器、埋下一份任务定义——七个逃逸里
的每一个文件——而不会有任何不在沙箱里的主机工具监视那个文件系统来把它捡起。
任何被执行的东西,都在箱子里、以一次性的 ubuntu 用户身份、在一台什么都不装
的机器里执行。
逃逸还有值得点名的后半段,因为 Bromure 也回应了它。假设代理写下的某个文件真的
被 VM 内的某个工具执行了——执行代码的目的,通常不是偷走什么,就是抵达某处。这
两者在这里都是死路。代码冲着那些让这趟行程划算的凭据而去——Anthropic 的钥匙、
AWS 的钥匙、GitHub 的令牌——找到的却是诱饵:主机上一个
凭据中介注入像 brm_… 这样的占位符、一份
合成的 kubeconfig、一把一次性 SSH 钥匙,并且只在网络边界、只在送往你早已核准之
目的地的请求上,才把真正的值换上去。而对外那一步——把战利品外送,或抵达第二台
主机——会穿过主机代理,在那里真正的目的地在出去的路上受到管制,而针对这个配置
文件所代理的云端与 git API 的破坏性操作——一次 delete、一次 drop、或一次
terminate——会撞上一道无论代理被怎么怂恿都能拒绝它的护栏。
在这一切之前,还坐着一层从根源上扣住这些逃逸的防线。它们每一个都始于一次 注入——代理读到的某个文件或工具结果里的一条恶意指令,一开始就叫它去写下那份被 下毒的配置。Bromure 会在模型据以行动之前,用一个设备端的 注入检测器为那份不可信的内容评分。 它拦下其中大部分,而一个够新颖的载荷仍可能溜过单一个分类器,这正是为什么它坐在 一次性箱子的前面、而不是取而代之。
界线一直围错了东西
Pillar 那一周留下的长久教训,不是一份七个待修补 bug 的清单。而是:把代理进程 装进沙箱——在一台这个进程与你所拥有的一切共享磁盘、共享一组受信任工具的机器 上——是把界线围错了东西。如果代理能写下某个已经在外面的东西的输入,它根本不 需要逃出去。
修补那些已知的文件是对的,你也该做。但那是一场对抗每一种未来格式的竞赛——某个 主机工具将来会决定去信任的格式——而这场竞赛不会结束。Bromure Agentic Coding 退出了这场竞赛。它假设代理终将被怂恿去写下那份下毒的文件,并确保当某个工具 读取它时,那个工具是在一个一次性箱子里——秘密以诱饵相待、线路上设有关卡——而不 是在你那台抱着你钥匙的笔记本电脑上。这就是差别:一道你得不停修补的墙,对比一 条画在低一层、写下却无人读取的界线。安装它,给你的代理一台 不是你的、输了也无妨的机器。