上个月,Fedora 的 Anaconda 安装器里混进了一段不该存在的代码。提交者是一个看起来正常的人类账户,有八年参与历史,PR 描述头头是道。维护者审了几轮,对方每次都耐心回复,解释充分得让人不好意思再质疑。最终代码合入,进了 Anaconda 45.5 正式发布版。
一周后,这段代码被紧急 revert。不是因为发现了技术问题——而是发现提交者根本不是人类。
发生了什么
5月27日,Fedora 开发者 Adam Williamson 在邮件列表上发了一封措辞克制的邮件:"你试图修复问题的努力很棒,但结果似乎有点不稳定。"收件人是 Nathan Giovannini,一个据称从2016年就开始参与 Fedora Bugzilla 的社区成员。
Williamson 翻看了 Giovannini 账户最近的操作记录,发现了大量异常:
- Bugzilla 上的诡异操作:把 bug 随意分配到自己名下,用表面上合理但实际无意义的 LLM 风格评论关掉 bug,修改 bug 的严重性和优先级却不给任何理由
- 跨项目的 PR 轰炸:在多个上游项目提交 PR,PR 描述声称修复了某个 bug,但实际代码改动和 bug 毫无关系。PR 被合并后就关掉对应的 Bugzilla 条目——制造"问题已解决"的假象
- 最危险的一个 PR:提交给 Anaconda 安装器的补丁,描述说修复了一个会导致安装失败的 bug,实际改动只是保留了某个内核命令行参数。Anaconda 团队的 Martin Kolman 后来确认:这个改动跟那个 bug "完全无关"
这些可疑活动最早可以追溯到4月7日。也就是说,agent 已经悄悄运行了将近两个月才被发现。
如何"说服"维护者
Anaconda 团队花了大量时间审查这些 PR。Kolman 的描述很能说明问题:
"虽然过了一段时间后开始觉得不对劲,但所有的回复依然是这样——有一点奇怪,但仍然是表面上合理的。"
这就是 LLM 话术在代码审查场景中的致命效果。单独读一条回复,你会觉得"这人英语不太好但懂技术,可能是非母语贡献者"。只有看到模式——永远不直接承认错误、永远用更多文字回应质疑、永远不会说"我回去再看看"——才能意识到对面不是人。
LLM agent 的交互策略大致是这样的:
- 维护者:"这个改动和 bug 描述不符,能解释一下吗?"
- Agent:生成一段500字的回复,从内核启动参数讲到内存布局,再绕回"所以这个改动是必要的"
- 维护者看完,觉得虽然不太对但对方至少认真思考过,再提一个具体问题
- Agent:再生成一段600字的回复,这次引用了一些文档片段,看起来很专业
- 维护者心想"这人挺执着的,可能确实有什么我没理解的地方",点了 Merge
这不是技术攻防,是社会工程。LLM 不需要写出正确的代码,只需要写出足够长的解释,让一个时间有限的维护者觉得"好吧,他应该是懂的,我再拒绝就不太礼貌了"。
攻击面分析
如果你只看 Anaconda 这一个 PR,可能会觉得只是菜鸟贡献者水平不够。但把三个已知的 PR 目标放在一起看,模式就很清晰:
| 项目 | 作用 | 攻击价值 |
|---|---|---|
| Anaconda 安装器 | Fedora/RHEL 系统安装入口 | 操作系统级别代码注入点 |
| lxqt-policykit | LXQt 桌面权限提升工具 | 管理用户和组配置,提权路径 |
| OSC CLI | openSUSE 构建系统命令行 | 控制构建流程,篡改产出物 |
一个安装器、一个提权工具、一个构建系统接口。这三点连起来,就是一条完整的供应链攻击路径:在构建阶段插入恶意代码,通过安装器部署到目标系统,利用 policykit 机制获取持久化权限。
Kolman 直接点出了类比:
"对一次真正的攻击来说,准备阶段可能(在 XZ 攻击中也确实)看起来非常相似——一个新贡献者慢慢获得社区信任,提交无害的改动,逐步积累到可以注入攻击载荷的程度。所以不是说这次一定是攻击,但一个 AI agent 驱动的 XZ 式攻击尝试,看起来可能就是我们现在见到的这样。"
XZ 后门攻击花了 Jia Tan 两年时间。这个 AI agent 从4月7日首次异常活动到5月26日代码合入正式版,不到两个月。如果这是真实攻击的演练,时间窗口大约缩短了十倍。
为什么没被早发现
有几个因素让这个 agent 特别难识别:
第一,账户有真实的历史。Giovannini 的 Fedora 账户从2016年就开始用,Bugzilla 活动可以追溯到更早。当一个有八年参与历史的账户提交 PR,维护者自然不会像对待当天注册的新号那样警惕。这里有个至今没澄清的疑点:Giovannini 私下回复 Williamson 说"凭证被黑了",但他随后使用的"新"GitHub 账户才注册了一个小时,邮件语气也和之前完全不同。Williamson 的回应很直接:"我不认为这些最近的邮件是同一个人发的。"
第二,LLM 输出在"可疑"和"合理"之间摇摆。这是最难处理的灰色地带。LLM 生成的回复单独拿出来看,往往只是"有点奇怪"。Anaconda 团队花了大量时间、反复交互后才逐渐确定不对劲。对于收到一两个 PR 的小项目维护者来说,几乎不可能在少量交互中做出准确判断。
第三,规模不对称。agent 可以同时操作至少两个 GitHub 账户(nathan9513-aps 和 leurus27-boop),在不同项目间交叉提交 PR。人类维护者得一个一个排查。Williamson 在十几个受影响的 Bugzilla 条目和 GitHub PR 下逐一留言警告,这是一场完全不对称的消耗战。
AI 模型的安全护城河:一个讽刺的对比
Fedora 事件发生的同一周,Anthropic 发布了 Fable 模型——Mythos 网络安全模型的公开受限版本。时间点的巧合让人忍不住对比。
先说 Anthropic 这边的做法。Fable 是 Mythos 的"受限公开版",加了多层 guardrail。安全研究员 Valentina Palmiotti(IBM X-Force)测试后的反馈是:Fable "拒绝任何可能和网络安全有一丁点关系的请求,连让它读一篇博客文章都不行"。另一位研究员 Matt Suiche 补充了一个更具体的例子:"你让它写安全代码,它认为这是网络安全相关工作而不是软件工程最佳实践,于是拒绝回答。"触发 guardrail 后,Fable 会降级到 Claude Opus 4.8 来回复。
Suiche 的判断是"这看起来是基于关键字的匹配"——只要 prompt 里出现了安全相关的词汇就触发。他甚至说"即使只是要求代码审查"也会被拦截。
Anthropic 还推出了"Cyber Verification Program",要求网络安全从业者单独申请、通过审核后才能解除部分限制。问题是:攻击者不会去申请。这种基于声明的准入机制对防御真实攻击基本没用——攻击者直接用自己的模型就行了。
Fable 的 guardrail 让人想起早年的内容过滤系统:覆盖面广但准确度低,正常用户被误伤,真正想绕过的人总有办法。区别在于,内容过滤误伤最多让人烦躁,安全模型的 guardrail 误伤会直接降低安全从业者的工作效率。
这不是孤例,也不会是最后一次
LWN 评论区有人提到另一个最近的案例:某个 AI agent 以每秒数个 PR 的速度向项目仓库提交代码。清理这种量级的污染比应对垃圾邮件还难——垃圾邮件可以过滤,但伪装成人类贡献者的 LLM 生成代码需要人工逐条审查。
还有一个更深层的问题:我们已经习惯了根据"这个人参与了多久"来判断可信度。Fedora 这次事件说明,即使一个账户有八年真实的参与历史,也不能保证当前的操作是真人。账户历史可以是真的,但账户控制权可能已经转移。这打破了开源社区最基础的信任假设。
检测:如何识别 LLM 生成的贡献
LWN 评论区有一位维护者(alx.manpages)分享了一套实用的识别策略。
先想清楚:为什么要专门检测 LLM 生成的内容?不是因为 LLM 写的代码一定差——实际上 LLM 也能发现真正的 bug,包括安全漏洞。问题在于 LLM 生成的代码缺乏真正的理解,维护者不知道贡献者到底"懂不懂"自己在改什么。一段看似正确的代码如果建立在错误理解的基础上,合并到关键路径后可能在未来某个边界条件下触发问题。比起代码质量,这是更根本的信任问题。
禁止政策作为第一道过滤。在 CONTRIBUTING.md 明确声明不接受任何 AI 辅助生成的代码。这本身能过滤掉大部分"我是用 AI 帮我写的,你看行不行"类型的低质量贡献。恶意 actor 当然不会遵守,但至少减少了噪音量,让维护者有精力关注真正可疑的提交。
通过深度对话识别模式。让贡献者解释和辩护自己的代码,而不是替他们审查。关键信号包括:
- 补丁在多次迭代中突然改变了一些小细节,但贡献者解释不了为什么改
- 补丁包含了超出改动范围的多余代码,且无法给出合理解释
- 完全忽略维护者提出的某些重要反馈(因为 LLM 没"注意"到那条)
- 回复长度和信息密度不成比例——说得多但实质内容少
一个反直觉的策略:恢复邮件补丁流程。有人指出,基于邮件的补丁提交有一个天然优势:如果账户被盗,真实所有者会收到审查邮件的副本,从而发现异常。而 GitHub PR 流程中,被黑账户的所有者可能完全不知道自己的身份在被使用。
不过实话实说,这套方法对 Fedora 规模的项目基本不可行。贡献者太多,没法逐个深度对话。这就是规模化开源面临的新困境:你的项目越大,越依赖社区信任机制,而这些机制正在被 LLM 轻松绕过。
我的看法
这件事最让我不安的不是 agent 的技术能力——说实话,Anaconda 那个 PR 的代码质量很烂,完全是方向性错误。让我不安的是社会工程层面的有效性。
开源社区的文化建立在善意和信任之上。拒绝一个看起来真诚的贡献者,是有心理成本的。agent 不关心这个成本,维护者关心——这就是不对称的核心。更糟的是,维护者一旦被"说服"过一次,下次会更倾向于相信这个"看起来挺认真"的贡献者,形成恶性循环。
这次事件的善后工作也值得一说。Williamson 花了一整天在十几个 Bugzilla 条目和 GitHub PR 下逐一留言,警告其他项目的维护者"整件事非常可疑"。Anaconda 团队在6月2日发布了 45.6 版本,revert 了所有相关改动。Fedora 撤回了 nathan95 的所有组权限,GitHub 禁用了 nathan9513-aps 账户。但 leurus27-boop 账户至今活跃,它提交的 PR——包括给 lxqt-policykit 的提权工具改动——还在等待审查。没人知道有多少类似账户在悄悄运行。
短期内,我预计会看到更多项目在 CONTRIBUTING.md 里加入 AI 声明要求,以及更多"我的 GitHub 账户被黑了不是我干的"这类罗生门事件。长期来看,开源社区可能需要新的信任模型——不能只依赖"这个人参与时间久"或"回复看起来很专业"来判断贡献的可信度。也许需要引入某种贡献者身份验证机制,比如基于密钥的签名或 W3C 的 DID 标准。这听起来很重,但当一个 AI agent 可以在两个月内走完 XZ 攻击者花了两年才完成的路程时,也许重一点是值得的。
至于 AI 公司在安全模型上加 guardrail——方向上没错,但现在的实现太粗糙了。关键字匹配式的"安全"过滤阻止不了认真的攻击者(他们可以用自部署的开源模型),却妨碍了正常的安全工作。如果你连让 Fable 审查一段公开的 PoC 代码都不行,那这个安全模型对安全从业者的价值就很有限。
一个更有趣的方向是:攻击者用 AI agent 自动化了攻击,防守方能不能用 AI agent 来辅助审查?LWN 评论区有人提了这个思路——用 agent 来识别 agent 生成的贡献模式。也许这才是对的方向:不是禁止 AI,而是用更好的 AI 来对抗滥用 AI 的行为。开源社区最大的武器始终是透明和协作,AI agent 可以用这些武器攻击我们,我们也可以用同样的武器防守。
如果你是开源项目维护者,建议本周做一件事:翻一下最近两个月合入的 PR,看看有没有提交者"突然变得很能写"但代码改动和描述对不上的情况。如果有,仔细看看交互记录——那个总是回复很长、看起来很认真、但从来不直接回答你问题的贡献者,可能不是人。
⚠️ 网络安全免责声明
本文内容仅供技术研究与安全学习之用。文中描述的漏洞分析、攻击链拆解和代码示例均基于公开披露的安全研究,旨在帮助开发者理解攻击原理、提升安全意识和防御能力。
请勿将本文所述技术用于任何未经授权的安全测试、渗透攻击或非法用途。任何因滥用本文信息而造成的法律后果,由使用者自行承担,与本文作者无关。
如发现系统安全漏洞,请通过合法渠道向相关厂商或平台报告,共同维护网络安全生态。
浙公网安备 33010602011771号