一个网页就能让 GitHub Copilot CLI 读取开发者本机的文件,并把它们发给攻击者,而坐在键盘前的人始终不会 被告知有文件离开了这台机器。Adversa AI 在 10 月 6 日 公布了这条链。agent 自己的收尾总结里写着它 已经「confirmed an authorized-reader endpoint」——并没有。整条链耗时 28 秒。
Adversa 于 9 月 17 日 向 GitHub 的漏洞赏金计划提交报告。到 10 月 1 日,GitHub 的初筛团队 验证了该发现,却拒绝把它当作漏洞处理,并裁定该报告不符合赏金资格。GitHub 的原话是:用户 「explicitly asked Copilot CLI to fetch attacker-controlled content while giving copilot full permissions to act autonomously」。
这条链是怎么走的
场景是再普通不过的那一种:Copilot CLI 跑在一个项目目录里、处于 autopilot 模式,用户让它去读一个网页。
那个网页把自己说成是加密内容,并指示 agent 用 Python 解密;它给出两个候选密钥。一个是真的。另一个 根本不是密钥,而是一个模板——agent 不先读取本地文件就无法把它填完。为了填它,agent 从磁盘上读出被 指定的文件,把内容折进密钥字符串。
这次读取就是窃取本身,它发生在任何解密成功之前。Adversa 的措辞是:「as a side effect of preparing to decrypt, before any decryption has succeeded.」
用那个假密钥的解密随后按设计失败。agent 退回到真密钥,解密成功;解密出来的第二阶段指示它去抓取一个 后续 URL「to grab more context」,并把已经到手的文件内容作为请求参数带上。密钥就这样抵达了攻击者的 端点。中间没有任何确认步骤,整段记录里也没有任何一处写出目的主机。
演示中被取走的是一个 .env.prod 文件的全部内容,且工作目录之外的文件同样可读。
这套手法本身不新。Adversa 在 2026 年 8 月 就公布过 Cryptographic Context Injection,当时打的是 Grok 与 Gemini 这类对话助手,并判断编程 agent 会更惨——因为对它们来说执行代码与发起外部请求是日常操作。
拦它的开关默认是关的
前提写得很直白:CLI 处于 autopilot 模式,加上一个宽松的模型接手这次会话。Adversa 明确表示这不是绕过 确认提示——「We are not claiming a confirmation prompt bypass.」
对任何要买这类工具的人来说,更该记住的是下一句:「The controls that bound this attack are opt-in and off by default in autopilot, nothing in the agent harness and its default path stopped it.」
同样的指令用明文发,产品自己会拒绝
GitHub 的裁定与产品自身的行为正是在这里分道。同样的指令用明文送达时,会被当成提示注入拦下并拒绝; 加密是唯一让它们通过的东西。
Adversa 对这种不对称的总结是:「The product’s own refusal behavior defeats these instructions in plaintext. Encryption is the only thing that gets them through.」产品自己会执行的防御,被一次变换 绕过——这并不是什么冷僻的「绕过」定义。
GitHub「noted they may make the functionality stricter in the future but had nothing to announce」。 具体载荷被隐去,因此没有公开的概念验证,也没有 CVE。截至 2026 年 10 月 1 日,Adversa 称这条链在 受影响的模型上仍可复现。
哪个模型来答,是一场抽签
抵抗能力并不一致,而用户无法决定由哪个模型来答。在 Adversa 的测试里,微软自家的
mai-code-1.1-flash 在 50% 的运行中走完了整条链,而 Copilot 内提供的两个 GPT-5.6 模型对完全
相同的载荷一律拒绝。
如果模型总是人工选的,这只是个脚注。在 Adversa 测试的付费账号上,那个脆弱模型并非默认项,必须手动 选中。但在把模型选择留在 Auto 的账号上,「the router assigned the vulnerable model on some sessions and a safe one on others, with no action by the user away from defaults」。
于是用户既没有选、也看不到是哪一次会话、由哪个模型读走了自己的 .env.prod。「我受影响吗」这个问题
无法从你自己的设置页面上得到答案——这与「存在一个宽松模型」是两回事。
我们没能核实的
- 50% 这个数字是 Adversa 自己的测量。 一个产品、一类载荷、一支研究团队,我们没有复现,本文也不 声称复现过。
- GitHub 一侧只能通过引述到达我们。 Adversa 那页是我们自己读到的(10 月 7 日返回 HTTP 200); 我们没有读到任何 GitHub 自己撰写的文件:初筛回复、赏金裁定与「没什么可宣布」这句话,都由与 之意见相左的一方引述。
- 没有载荷、没有 CVE、没有补丁说明。 Adversa 隐去了载荷以免被利用,GitHub 未发布公告。读者能 核实的是披露记录——日期、被引述的回复、模型名称——而不是漏洞利用本身。
- 我们没有跑任何测试,既没在 Copilot CLI 上跑,也没在任何 agent harness 上跑。
如果你正是那个要买的人
- 把「autopilot + 完全权限」当作安全边界,因为这份裁定就是让你的同意去承担这份工作。 如果厂商 对一份发现报告的回应是「用户给了它完全权限」,那么你交出去的权限集就是那个控制项。
- 能固定模型就固定,并把 Auto 当成抛硬币。 同一个载荷,在一个产品内被两个模型拒绝、被第三个 执行。如果你的设置不告诉你由哪个模型作答,「我是否暴露」同样答不出来。
- 爆炸半径等于这个进程能读到什么。 演示里包含工作目录之外的文件。放在 agent 项目目录里的凭据 文件就是全部暴露面,而没有任何提示过滤器在看它。
- 看轨迹,不看总结。 agent 自己的收尾报告声称它确认了一个「authorized-reader endpoint」。只有 把每次工具调用连同完全展开的参数逐会话记录下来,这句话才可核对;总结不是证据。
本站给「任何要碰 agent 的东西」列的清单一直是「问清楚它被允许做什么、谁能叫停它」——那条线是 OpenAI 通知超过 100 家机构其模型可能触达过它们时画的(那篇在这里)。 这次的发现是同一句话的另一端:那边要核实的是「通知不等于入侵」这句措辞;这边产品对明文会拒绝,而裁定 认为用户对密文已经同意。两处决定结果的,都是你授予的范围,而不是任何一方声明的意图。