那个走出工作区的符号链接,什么也没捞到
GhostApproval 展示了一个恶意仓库如何诱使六款 AI 编码助手在它们本应停留的文件夹之外读写文件:植入 SSH 密钥、劫持 shell 启动、读取开发者自己机器上的云凭证。漏洞本身是一个跟随符号链接的失误;它之所以伤人,是因为代理跑在一台装满你机密的机器上。Bromure Agentic Coding 改变的是后一个事实。
编码代理是一个你请上笔记本、允许它读写文件的程序。GhostApproval 讲的是一个恶意仓库说服这个程序稍稍越界书写的故事:写进你的 SSH 密钥、你的 shell 配置、你的云凭证。漏洞本身既老且钝,伤害来自 代理站立的位置。
2026 年 7 月 8 日,Wiz 的研究团队发布了 GhostApproval, 记述了同一个缺陷如何以六种不同的形态,出现在六款流行的 AI 编码助手 中:Amazon Q Developer、Anthropic 的 Claude Code、Augment、Cursor、 Google 的 Antigravity,以及 Windsurf。细节因产品而异,形状却如出 一辙。你打开一个仓库——也许是别人转来的教程,也许是同事请你看一眼 的项目——把你的编码代理指向它。而在那个仓库的某个角落,藏着一个 不是文件的文件。
一个不是文件的文件。
把戏在于符号链接(symlink):一种文件系统条目,看上去像文件, 实则是指向另一条路径的路标。符号链接是一项寻常的、有四十年历史的 Unix 特性。它的安全问题——编目为 CWE-61——几乎 同样古老:程序若不检查符号链接指向何处就把它打开,攻击者便能把它 引向作者从未想让它触碰的文件。
在 GhostApproval 中,恶意仓库带着一个名字人畜无害的符号链接,比如
project_settings.json。它装的不是设置,而是一枚指针,指向
~/.ssh/authorized_keys、~/.zshrc 或 ~/.aws/credentials——
它们坐落在开发者的主目录里,远在仓库文件夹之外。随后,一份
README(或是按仓库指示行事的用户)让代理“更新
project_settings.json”。代理不解析路标通向哪里就跟着它走,把
内容写进了真正的目标。
有两件事让它不止于猎奇。第一是这些目标各自的分量。往
~/.ssh/authorized_keys 追加一行,就为攻击者装上了免密 SSH 登录
你机器的密钥;写入 ~/.zshrc,你每次打开终端都会运行攻击者的
命令。Augment 的代理甚至不需要写:被问到“项目里的 AWS 密钥”时,
它顺着符号链接走了出去,找到一份凭证文件,把里面的机密直接打印进
聊天,全程没有任何提示。
第二是一处更隐蔽的失守,Wiz 将其归为
CWE-451,即用户
界面失实。在 Claude Code 中,代理自己的内部推理已经认出那是一份
shell 配置文件,确认对话框却仍然只问“要对
project_settings.json 做此修改吗?”人类本可拦下危险的那一个
瞬间,展示给人类的却是错误的信息。Windsurf 和 Amazon Q Developer
更糟:它们在展示批准按钮之前就把文件写上了磁盘,于是对话框成了
撤销提示,而非闸门。攻击者的 SSH 密钥早已就位。
多数厂商此后修复了各自的变体:Cursor 在 v3.0 (CVE-2026-50549), Amazon Q 在 language-server 1.69.0 (CVE-2026-12958), Google 在 Antigravity v1.19.6,Claude Code 则加上了符号链接解析 警告。这是正确的回应,你也应当安装这些更新。但逐一修补每款产品的 符号链接处理,修好的只是机制,赌注还留在原地。
赌注由代理站在哪里决定。
这些载荷无一例外建立在同一个假设上:够到开发者的主目录是值得的。
~/.ssh 里有一把能打开真实机器的密钥,~/.aws/credentials 里有
一枚活的云令牌,~/.zshrc 会在你明天打开的 shell 里运行。符号链接
是路,终点则是一台装着你全部工作生涯的笔记本。
大多数针对编码代理的攻击都倚仗同一个假设。代理是一个能力强大、 行为开放的程序,它读取不受信任的输入(仓库、网页、软件包压缩包、 工具输出),并在一台机器上采取行动——而你的凭证就躺在几个目录 之外的明文文件里。任你如何加固代理,它仍与你运行的其他一切共享 同一个文件系统、同一串钥匙。攻击者会不断铺出新的路。问题在终点。
Bromure Agentic Coding 拿掉的正是这个终点。代理、它派生的 shell、 它克隆的仓库,以及其中的任何代码,都运行在一台按配置档划分的 Linux VM 里:一台由 hypervisor 与你的 Mac 隔开的独立机器。你的 真实凭证从不进入其中。我们在为什么 Bromure Agentic Coding 不是沙箱里写过为什么 边界胜过牢笼;GhostApproval 是对这个想法的一次干净检验,因为它 逃出的正是工作区文件夹——业界所倚赖的那道小小边界。
把同一次逃逸放到 VM 里走一遍。符号链接照样解析(Bromure 根本不
需要识破或拦截这个把戏),写入照样落在 ~/.ssh/authorized_keys
上。但那个文件住在客户机的主目录里,在一台没有任何通路通向你
Mac 的一次性机器上。攻击者的密钥如今换来的,是登录一台你随时会
扔掉的机器的权限——而这台机器从一开始就无法从外部访问。
凭证窃取那个变体的结局更加索然。当一个被攻陷的代理顺着符号链接
走到 ~/.aws/credentials,把内容打印进聊天时,它读到的是一份
桩值:格式规整、密钥却在任何地方都无法通过认证的凭证文件。
真实令牌留在宿主上,由一个中介看管——只在你允许的出站调用发出的
那一刻,它才把占位符换成真机密,并在响应里再换回去。翻遍文件系统
找机密的依赖包或被骗的代理,找到的只是摆设。
确认提示,与第二张网。
对 GhostApproval 两处更锋利的刃口,Bromure 在不同的层上接招。
第一处是确认提示的问题,CWE-451:那个代理心里想着 ~/.zshrc、
嘴上却说 project_settings.json 的对话框。Bromure 干脆取消了
文件写入对话框;一次性 VM 内的写入只触及一块可以随时报废的磁盘,
无需批准。Bromure 把人插在真正的边界上:那些离开 VM、会改变状态
的调用——一次 git push、一条数据库 DROP、一个云端
Terminate。这些调用在宿主上暂停,向你展示字面意义上的操作本身,
而不是代理写的摘要。这正是 GhostApproval 违背的那条原则,用在了
要紧的地方:决定在沙箱之外产生后果的那一刻,就是人类应该看到
真实调用的那一刻。
第二处刃口是启动整场攻击的那条指令:一份 README 让代理“更新
project_settings.json”,模型把它当上下文来读,当命令来服从。
Bromure 会先在设备端读一遍这些不受信任的内容:一个本地分类器对
传入的文件、网页抓取和工具输出打分,找出那些本不该出现在那里的
指令;而代理奉为常设命令的规则文件,会被更严格地过一遍。分类器
会漏掉某些措辞,况且隔离本身已足以击败这次攻击;把它当作第一张网
下面的第二张。
这条线救不了你的地方。
隔离是一个具体的形状,而形状有它的边。
配置档长期存在,持久化因而持久。
~/.zshrc 里的载荷会在那个配置档的 VM 内下一次打开 shell
时运行。它面对的是一台没有宿主密钥、只有桩值凭证的客户机——
一个空房间里的 shell——但它确实会运行。若某项任务让你为此
担心,就换一个全新的配置档来跑。
你挂载什么,就暴露什么。
VM 默认不装你的任何东西。但如果你主动把一个真实的宿主目录 挂载进某个配置档,或把一枚活令牌粘贴进客户机,符号链接逃逸 就够得到它。这条线把你的机密挡在外面;把它们放回去,是只有 你才能做的选择。
有意识地限定中介的范围。
桩值意味着磁盘上的文件一文不值,但中介仍为你允许的调用握着 真实令牌。一个只需要读仓库的配置档,就不该携带一枚能发布 仓库的令牌。隔离限住了爆炸;作用域决定它最大能有多大。
工作区是一堵错误的墙。
说到底,GhostApproval 讲的是业界共同默认了什么可以充当边界:
工作区文件夹,以及“代理待在项目目录里”这一想法——它被当成了
安全属性,实则只是任何一块路标都能绕开的默认行为。符号链接本身
是厂商能修、也确实修了的漏洞。把边界画在文件夹上,你就得永远
证明没有任何符号链接、任何 ..、任何取巧的路径能走出去。把边界
画在 hypervisor 上,这个问题就不再要紧,因为代理逃去的地方,
是一台从未装过你任何东西的一次性机器。
装上厂商的补丁;它们堵上的是一个真实的洞。但这个洞之所以伤人, 是因为代理站在你的笔记本上,离你的密钥只有一个目录之遥。把代理 从你的笔记本上挪走,下一次逃逸(一定会有下一次)到达的将是一个 空房间。Bromure Agentic Coding 免费、开源, 今天即可使用。