LiteLLM 这次事故说明,代码仓库看起来正常、制品经过正常签名、发布还有两人审批,仍可能同时成立于一次供应链入侵。真正失效的是这些控制所依赖的身份和权限边界:攻击者沿一枚未撤销的密钥进入 agent 平台,取得组织所有者身份,再借 CI 扩展到多个维护者凭据。技术重点是这些权限如何组合,而不只是恶意版本存在过。初始通报:Contained: Oct 21, 2026 LiteLLM release v1.107.0 Compromised;官方复盘:The attack chain behind the v1.107.0 LiteLLM release。
LiteLLM · 初始通报 2026-10-21;Oliver Jensen、Mateo Wang 的复盘 2026-10-23
制品受影响,不等于源代码标签被改
官方确认受影响的是 PyPI 的 1.107.0 七个 wheel,以及相关 Docker Hub、GHCR 镜像标签。PyPI 暴露窗口约为 11:39–13:23 UTC;Docker Hub 对应镜像约在 11:46–14:20 UTC 被分发。不同注册表的处置时刻不同,不能只拿一个两小时窗口判断所有镜像。
原始 v1.107.0 GitHub 源码标签不含恶意文件,制品来自攻击者控制的 fork。这种差别对调查很关键:查看 tag 的代码、比较当前 main,均不足以证明当时拉取的二进制安全。必须追到构建所用仓库、提交、流水线运行以及实际制品摘要,才能把源与交付物对应起来。
第一处失守:泄露被隐藏,密钥却仍有效
5 月 27 日,一份公开提交包含内部实验性 Agent Platform 的 master key。团队发现后改写了可见历史,也将密钥标记为需要撤销,但这枚 master key 实际没有撤销。10 月 10 日,攻击者用它读取 agent 配置,取得 GitHub 组织所有者的 CLI OAuth token。
这里包含两个不同缺陷:泄露后的凭据失效流程没有闭环,以及实验平台持有了权限过高的个人身份。删除可见提交无法收回已经复制的秘密;把 token 放在内部服务中也不会自动降低它的权限。该 token 是完成双因素认证后的凭据,拥有它的请求不会再次得到网页登录时的保护。
第二处失守:CI 变成权限扩展通道
取得所有者身份后,攻击者在多个仓库的临时分支中加入工作流,将 CI 可访问的 secrets 发往外部,取得另外两位维护者的 OAuth token。随后还删除相关运行记录。单个服务的凭据问题于是扩散成多个看起来独立的 GitHub 身份被控制。
两人审批只有在身份和权限保持独立时才提供额外约束。如果发起者能读取批准者的 token,或者组织所有者可以关闭分支保护,两次确认只是同一攻击者沿不同身份完成的动作。这是对事故信任结构的解释,不意味着多重审批本身没有价值;真正要检查的是批准权限能否被同一条路径取得或绕过。
第三处失守:合法流水线构建了错误来源
10 月 21 日,攻击者改动私有发布仓库,让它从相似命名的恶意 fork 拉取源码。一次使用普通维护者身份的直接推送被保护规则拒绝,随后所有者身份关闭限制并强推改动。攻击者再用一个身份启动发布、另一个身份批准生产环境,流水线完成签名和发布。
官方还披露,原来与办公室物理设备绑定的特殊批准要求在那一周临时放宽,以支持更频繁发布。这扩大了可利用空间;但组织所有者本来就可能修改限制,所以不能把根因缩成一次审批流程放宽。签名证明某条受信发布链处理了制品,并不自动证明它取用了正确源仓库。
删除运行记录没有抹掉所有证据
发布之后,攻击者删除生产与 staging 运行记录,再回滚、改写工作流历史。调查仍利用组织审计日志、token 哈希、API 请求记录和可按提交 SHA 访问的对象还原路径。某个 fork 提交能从上游仓库的提交 URL 打开,也容易让调查者误认其来源。
因此,事故证据应分开保存“当前仓库状态”和“当时运行所采用的状态”。对 LLM 基础设施,agent 平台、代码托管、CI、制品注册表与推理供应商账户常常连在一起;只核查应用部署日志会遗漏这些控制面的授权变化。
处置与长期修复不能混写
团队锁住注册表,撤掉大部分组织成员权限,并按所有内部凭据均可能泄露的假设推进轮换与审计。原文将更完整的流程改进留待后续发布,所以本篇不把建议性的最小权限、独立发布身份和离线审批写成已经全部落地的措施。
对使用方,暴露判断应依据实际安装或运行的版本与镜像,而不是仅看现在仓库是否恢复正常;对维护方,更可迁移的教训是让撤销成为可验证状态,让构建来源成为可检查约束,并保留控制面审计证据。这些原则来自已确认攻击链,而非初始通报曾提出、后来被修正的“日志泄露 token”假说。原通报与复盘没有适合展示核心机制的官方架构图,本篇保留文字解释。
发布信任需要一张可核对的依赖表
从这条攻击链可以提炼三个需要分别证明的事实:谁批准了发布,流水线实际读取哪份源码,以及用户最终取得哪个制品。三者只要有一个被默认相信,签名和审批就可能掩盖来源偏移。对于自动化程度越来越高的模型网关与代理平台,构建身份不宜因为需要方便维护就同时获得配置授权、读取全仓库 secrets 和修改审批规则的能力。
事故还说明,实验项目的等级不能只由使用人数判断。一个尚在内部试用的服务,若持有组织级 token,其失效范围就由该 token 决定。清单里的“低流量”“内部使用”“临时工具”并不会自然形成安全边界。相同原则也适用于让 agent 自动修复代码或提交依赖更新的服务:能力需求与凭据范围需要逐项对应。
这些是从已确认事件推导出的控制设计问题,不是对尚未公布整改的猜测。阅读复盘时,尤其值得把每个发现和修复动作放到时间线上,区别“已经暴露”“已识别需要撤销”“撤销真正生效”三个状态。若只留下一个已处理工单,最关键的中间缺口可能再次被忽略。
最后,供应链影响与业务运行影响应分别追踪。即使恶意版本已下架,曾经运行过它的环境与当时可访问的身份仍有自己的调查范围;而没有取得该版本的系统,也不能只因使用同名项目就被归为相同暴露。