夜雨聆风学习资料网

ARTICLE · 1059785

审查过的插件被调包,四个编码 Agent 都报告安装成功

审查过的插件被调包,四个编码 Agent 都报告安装成功

2026 年 9 月 17 日,安全公司 AIR 公开了一个漏洞,名字叫 Plugin4Shell。

受影响的是四个最常用的编码 Agent,Anthropic 的 Claude Code、OpenAI 的 Codex、微软的 GitHub Copilot,以及 Google 的 Gemini CLI。

攻击不需要你点任何东西。你机器上那个已经装了很久、你早就信任的插件,会在某次后台更新之后变成别人的代码。

这次披露里更值得看的,是这四个产品的审查机制全都停在同一个位置。

指纹本该锁住什么

插件市场给每个插件记一个 commit hash,四十位十六进制字符串,指向某一次具体的代码快照。

这道锁不是凭空设计出来的。AIR 在 Plugin4Shell 之前已经讲过两次同类的故事。

第一次叫 The Story of Skills。他们自己写了一个恶意 skill,看着它走红,最后拿到 26000 多个 Agent 的控制权。结论是,把代码种进一个人们信任的市场,不算难的那一步。

第二次叫 SkillJacking,连种都不用。接管 925 个已经在用的 skill 背后的仓库,影响 134000 个 Agent。

行业给这类「先取得信任,再事后替换」的答案就是 SHA pinning。审查代码到一个提交上,pin 住它,从此以后跑的就是它。

这份快照是被人审查过的。审查通过之后,市场把这个 hash 写下来,往后无论插件作者的仓库怎么变,Agent 都应该只装这一份。这套做法是从软件供应链那边搬过来的,容器镜像摘要、哈希锁定的依赖、可复现构建,底下压着同一个假设,一个指向具体产物的密码学标识符,不可能被悄悄换成别的东西。

Plugin4Shell 讲的就是这条边界失效。

AIR 把受害者的处境写得很干净。他只需要满足一个条件,装了一个插件,来自他信任的市场,那个插件被审查过、被 pin 过,完全符合安全模型的设计意图。

还有一层后果值得单独说。企业自己搭的那套流程,比如只允许从内部镜像装插件、每个插件都人工过一遍再 pin 到某个提交,底下踩的也是同一块地基。审查通过了,pin 写下了,装进去的是别的代码。任何架在 pin 之上的下游流程,都会继承这次失效。

做对每一件事,也没能挡住这次攻击。

四个 Agent 都少了一样的那行代码

根因是同一个。AIR 在两份披露里重复了这句话,四个 Agent 都会去取市场 pin 住的那个 commit,然后让 git 把它 checkout 出来,但四个都不核实工作树最后落在哪里。

第一类变体出现在 Claude Code、Codex 和 Copilot 上。攻击者控制插件仓库之后,建一个分支,分支名就是那四十位 hash,再把它设成默认分支。git 遇到同名的引用和对象时,会优先选引用,只在终端上打一行 refname is ambiguous 的警告,没人会去读。Agent 于是 checkout 到了攻击者的分支,装完之后报告的是,已按预计的 SHA 安装成功。

这个歧义在 Git 里存在了很久,它是名称解析顺序的问题,算不上 Git 的漏洞。让它可以用的,是 Agent 侧没有做那一步确认。

第二类变体出在 Gemini CLI 上。它的流程是先 git fetch origin,再 git checkout FETCH_HEAD。攻击者只要把仓库的默认分支命名成 FETCH_HEAD,checkout 就会解析到那个分支,刚取下来的那份正经 commit 被整个丢掉。

修复方案只要一行。checkout 之后跑一次 git rev-parse HEAD,把它和市场 pin 的那个值比一遍,不一致就中止。

四个产品,一行断言,全部缺失。

这一行比写错一行更难被发现。写错了至少会有东西跑不通,而这一行不存在的时候,整条链路每一步都返回成功。审查过了,pin 写下了,安装报告成功了,代码是别人的。

零点击是怎么成立的

让这次攻击变成「零点击」的,是插件后台自动更新。Claude Code 和 Codex 默认开着它。

攻击者于是有了两条路。

第一条是先老老实实做一个有用的插件,通过审查,攒到一批用户,然后再改上游仓库。第二条更省事,直接劫持一个现有插件的维护者仓库。

AIR 还记了第三条,它让这件事更难防。攻击者提交一个新版本,这个版本也是良性的,然后提一个 PR,请市场把 pin 从旧 commit 改到新 commit。市场主审查通过、批准、更新 pin。到这一步之后攻击者再动手,所有已经装了插件的人会在下一次自动更新里拿到恶意代码。整个过程中,市场主做的每一个动作都是对的。

插件继承的是运行 Agent 那个人的完整权限。一次成功的替换,拿到的是本机文件、存下来的凭据,以及这个人能登录的所有系统。

四家厂商分成了两半

AIR 估计受影响的 Agent 数以百万计。微软自己的口径是,财富 500 强里大约九成公司在用 Copilot。OWASP 这个月刚把「过度代理」提到了 2026 年十大风险里的第三位。

四家厂商的反应分成了两半。AIR 在 2026 年 5 月发现漏洞,当时对四个 Agent 都有可用的验证代码,6 月按协调披露的规矩通知了四家。

Anthropic 在 6 月 16 日发布的 Claude Code 2.1.179 里修掉了,第二天确认。这个版本的公开更新日志列的是常规修复,没有提这个漏洞。

OpenAI 在 8 月 12 日确认 Codex 0.146.0 已修复。

Google 在 8 月 4 日答复,Gemini CLI 的消费者版本正在停用,不会修,建议用户迁到 Antigravity。

微软至今没有发布修复。

GitHub 对 The Register 的说法是,自家市场不允许分支名或标签名长得像 commit hash,所以这个攻击与它无关。AIR 的反驳是,Copilot 的插件可以放在 Bitbucket 和自建 Git 服务器上,那些平台允许这种命名,而 pin 是在用户自己的机器上解析的,没有任何市场能单方面补上这个洞。

到公开披露那天,这个漏洞还没有 CVE 编号。从通知厂商算起已经过去三个月。

这里还有一层结构性的麻烦。市场自己也修不了这个洞。 pin 是在 Agent 内部解析的,只有 Agent 侧的修复能把这个保证恢复回来。市场能做的最多是把分支重名那条路堵上,办法是只允许拒绝 hash 形状命名的主机挂载插件,实际效果等于只认 GitHub。这么做会禁掉 Agent 官方文档里列为合法后端的那些主机,而且对 Gemini CLI 那条路一点用都没有。

这条也要说清楚

The Hacker News 在 9 月 18 日做了一轮核查,结论对威胁面有帮助。Anthropic 的社区目录、Claude Code 与 Copilot 的默认目录里,每一个插件都指向 GitHub 仓库。而自动更新默认只对 Agent 自带的市场开启,自带市场都在 GitHub 上,外部市场是关闭或者可选的。只用默认目录的开发者,碰不到分支重名那条路。

这条限定盖不住 Gemini CLI。它的变体靠的是一个叫 FETCH_HEAD 的分支,GitHub 的命名规则并不明确拦它,而它又是四家里唯一确定永远不会被修补的那个。

还有一条要一起带上。AIR 自己也卖 Agent 安全产品,它在披露里说明,用自家市场和安全过滤的客户没有受到影响。目前也没有任何野外利用的确认,攻击以验证代码的形式存在。

审查是一次性动作,代码是活的

市场把 pin 当成一件已经做完的事。写下来,存档,结束。Agent 把 pin 当成一个已经成立的状态,读出来,用上。而 pin 描述的是一件需要每次核验的事实,你这次拿到的代码,是不是当初审查过的那一份。

一个动作被当成一个状态,中间那段时间就没人看着。插件的自动更新一直在跑,上游仓库的所有权会转移,维护者的账号会被接管。

今天能做的动作不多。用 Claude Code 和 Codex 的,更新到 2.1.179 和 0.146.0 之后。用 Copilot 和 Gemini CLI 的没有补丁可装,只能收紧插件来源,把插件清单过一遍。企业那边还有一件,盯住插件仓库的分支名和所有权变更。

一个市场可以把插件锁到某一次提交上。锁完之后的那段时间,它管不了。

相关学习资料