密钥泄露未吊销,数月后引发恶意代码投放
据文中描述,LiteLLM 一枚泄露后未吊销的内部服务密钥,被攻击者用于获取具有组织级权限的 GitHub 令牌。事件暴露出仅清理 Git 历史、未撤销凭据及限制令牌权限的连锁风险。
AI 深度解读
一次误提交的密钥,为什么能在 136 天后变成恶意软件发布?原文复盘了 LiteLLM 从内部服务密钥泄漏,到 GitHub 组织权限失守,再到 PyPI、Docker 发布链被接管的过程。作者的核心判断是:攻击不需要零日漏洞,一串长期有效、权限过大的凭证就够了。
更贴近日常开发的风险在后半段:恶意 Python 文件可以在解释器启动时执行,连运行修复用的 pip 都可能再次触发。理解这条链,既能帮助团队检查发布权限,也能避免中招后用错处理顺序。
删掉提交记录,为什么还会留下入口
LiteLLM 是连接 OpenAI、Anthropic、Bedrock 等服务的开源网关,负责请求路由、费用追踪和密钥管理。这也让它成为凭证集中的位置。
据原文援引的事故复盘,2026 年 5 月 27 日,有人误把内部服务的 master key 提交到公开仓库。他们重写了 Git 历史,却没有撤销密钥。10 月 10 日,攻击者首次使用它,间隔 136 天。
重写 Git 历史只能减少密钥继续被看到的机会,不能让已经泄漏的密钥失效。
这把密钥打开了一个供 AI agent 创建 pull request 的内部工具。工具又返回了 GitHub 组织所有者账号的 token。它没有绑定专用服务账号,也没有缩小权限范围,攻击者因此拿到了整个组织的操作能力。
接下来,攻击者往主仓库插入临时 GitHub Actions 任务,读取并外传了 6 项 secret,其中包括第二名维护者的个人 token;另一个任务取得第三名维护者的 token。这些任务几分钟后被删除。两天后,攻击者又从私有仓库拿走 14 项凭证,包括 Anthropic、OpenAI、Bedrock 的密钥。
这里的关键是权限逐层放大:一个内部工具的入口,最终通向仓库、CI secret 和更多维护者身份。

双人审批,为什么没能挡住发布
又等了 9 天,攻击者在 10 月 21 日把发布流水线指向假仓库 Berri-AI/litellm。真实仓库是 BerriAI/litellm,差别只有一个连字符。
分支保护最初挡住了修改,但组织所有者 token 覆盖了限制。
发布还需要两个人:一人发起,一人批准。原来的批准账号被严格限制,并绑定办公室机器;事故前几天,规则被放宽为任意两名工程师即可完成。攻击者持有三名维护者的 token,于是用一个身份发起,另一个身份批准。LiteLLM 推测,这样做也能让审计记录显得正常。
两个账号参与审批,只有在账号背后的控制条件仍然独立时,才有足够的防护价值。
恶意版本 v1.107.0 随后进入 PyPI 和 Docker。研究人员在 27 分钟后发现,PyPI 在两小时内下架。原文还提醒,latest、main-latest、main-stable 都曾指向恶意构建,因此容器镜像应按 digest 固定,而不能只相信标签。
为什么运行修复命令也可能再次中招
恶意包安装了 3 个文件,原文重点解释的是 cook.pth。
Python 的 site-packages 中,.pth 通常用于补充模块搜索路径,但它还保留了一项兼容行为:以 import 开头的行,会由 site 模块在 Python 启动时执行,不要求业务代码导入对应包。

因此,窃密程序可以在脚本、测试或其他 Python 进程启动时运行。对于使用同一受影响 Python 环境的项目,业务依赖里有没有直接使用 LiteLLM,已经不能作为安全判断依据。
下面这条升级命令原本用于修复依赖,却可能先触发恶意代码:
pip install --upgrade litellm
要注意的是 pip 本身也是 Python 程序:解释器先启动,升级逻辑才有机会执行。
原文因此建议先用不启动 Python 的方式寻找恶意文件:
find / -name 'cook.pth' -o -name 'sitecustomize.py' 2>/dev/null
要注意两个文件名条件;按原文建议,应先删除对应恶意文件及匹配的 .pyc,再运行 pip。不过,这条命令只是在查找指定文件,不能证明机器已经恢复可信。
窃密范围也不限于 AI provider key。原文列出的目标还包括云凭证、SSH 密钥、浏览器数据、数据库 URL,以及 Codex、Claude、Gemini、Cursor 的配置。
我的判断
作者把重点放在凭证失效和权限收缩上,是有说服力的。按这份时间线,攻击者不断复用仍然有效的身份,每一层过大的权限都在帮助它跨过下一道边界。相比只清理提交记录,撤销泄漏密钥才会直接切断入口。
但把事故全部归结为「旧凭证」也不够。专用服务账号、受限 token、分支保护、独立审批,各自解决不同的问题。原来的审批限制如果仍在,按作者的分析,三个维护者 token 还不足以完成发布。为了方便而放宽规则,是攻击链成立的重要前提。
这些结论来自单次事故复盘,没有对照实验,不能据此声称某项措施能降低多少风险。LiteLLM 也承认早期更新存在错误,后续还有调整,因此具体时间线应视为当时的阶段性说明。
对开发者而言,另一个边界是:找到并删掉恶意文件,不等于追回已外传的秘密。升级依赖和撤销受影响凭证,需要分别处理。
能拿来做什么
- 检查 AI agent 创建 PR 时使用的身份,确认它是否拿到了个人账号或组织所有者的 token,并把权限缩到实际需要的仓库和操作。
- 把密钥误提交的处理顺序写清楚:先撤销或轮换,再清理 Git 历史,并检查旧凭证是否被使用。
- 检查 Node、Python 项目共用的 CI 发布流程,确认受保护分支能否被高权限 token 绕过,以及双人审批是否依赖彼此独立的控制条件。
- 如果开发机装过受影响版本,先处理原文指出的 Python 启动执行入口,再运行升级命令;同时排查机器上的云、SSH、数据库和 AI 工具凭证。