LiteLLM 恶意版本事件与密钥排查
文章称 LiteLLM 1.107.0 曾被植入窃取密钥的恶意代码,即使业务未导入该库,也可能在 Python 启动时执行。文中给出受影响版本、镜像标签及时间窗口,并介绍环境自查、密钥轮换和构建防护。
AI 深度解读
LiteLLM 把多个模型服务的密钥集中到一个网关里,应用接入方便了,攻击者也有了集中下手的位置。原文描述的 1.107.0 投毒事件,危险之处不只在于窃取 AI Key:恶意文件会在 Python 启动时执行,业务代码不导入 LiteLLM,也可能中招。
对前端和全栈团队,这篇值得读的地方是排查顺序:先查锁文件、镜像和构建记录,再判断哪些凭证需要撤销。直接运行 pip 查看版本,反而可能再次触发恶意代码。
没有 import LiteLLM,为什么也会泄露密钥
按原文记录,2026 年 10 月 21 日,被投毒的 LiteLLM 发布产物在公共仓库存留约两小时,获得约 49,000 次下载。这是该项目当年的第二次供应链攻击。
PyPI 的重点风险窗口是当天 UTC 11:39 至 13:23;容器则要检查 UTC 11:46 至 14:20 拉取的镜像,包括 ghcr.io/berriai/litellm 和 Docker Hub 上的 v1.107.0、latest、main-latest、main-stable。不同机构给出的清理时间有差异,Docker Hub 排查应采用较晚的 14:20。LiteLLM 称私有企业仓库未受影响。
攻击利用的是 Python 启动机制。恶意 wheel 在 site-packages 中放入三个文件,位置在 litellm 包目录之外:
cook.pth,内容是import cook。cook.py,负责收集和外传凭证。sitecustomize.py,负责加载后门。
.pth 通常用于补充模块搜索路径,但以 import 开头的行会在 Python 启动时执行;sitecustomize 也是启动阶段会加载的模块。测试、Jupyter、定时任务,都可能成为触发入口。
是否执行恶意代码,不能只看业务有没有导入这个包。
原文称,pip uninstall litellm 不会清掉这些残留文件,运行 python 或 pip 还可能重新触发钩子。它也指出,官方 v1.107.0 源码标签没有恶意载荷。Endor Labs 发现,构建设置中的一行 data = "cook",把公开源码里没有的 cook/ 目录复制进了 wheel。
这解释了为什么只审 Git 仓库还不够:源码、构建输入、最终安装包,需要分别检查。
网关失守,损失为什么会越过 AI Key
LiteLLM 的价值是代管 OpenAI、Anthropic、Google、Bedrock 等服务的密钥。代价是,一台代理服务器可能同时接触多个供应商账户、数据库和云角色。
原文列出的窃取范围远超模型密钥:AWS、Google Cloud、Azure 凭证,云元数据服务提供的实例角色凭证,Kubernetes、Docker、Vault、Terraform 秘密,以及 SSH、GPG、TLS 密钥。开发环境中的 Git、npm、PyPI Token、.env、Shell 历史、进程环境变量,也在收集范围内。
浏览器密码和 Cookie、AI 编程助手会话、IDE 与 MCP 配置同样可能被读取。对跑着 Node 项目的开发机来说,风险来自同机存放的秘密,不需要 Node 项目直接依赖 LiteLLM。
载荷还专门读取 LiteLLM_CredentialsTable 和 LiteLLM_ProxyModelTable,每次查询最多取 500 行。这是查询批次大小,不能理解成总共最多泄露 500 条。
数据加密后被发送到仿冒域名 www.litellm.dev/healthcheck。另一个后门在 Unix 类系统后台运行,每小时读取 GitHub 文件中的服务器地址,再下载执行代码。原文称该文件只出现过测试值,未观察到第二阶段执行;这不代表前面的窃密没有发生。
Wiz 还观察到,UTC 16:20 至 17:01,来自受害者的 AWS 实例角色凭证被两个 IP 地址测试。至少在原文引用的观测中,凭证离开机器后很快进入了利用环节。
排查时,为什么应该先避开 Python
原文引用 Microsoft 的建议:从干净机器检查可疑环境,不要先进入环境运行 Python。即使是 LiteLLM 建议的 pip show litellm,也会重启钩子。
先搜索各仓库的 uv.lock、poetry.lock、Pipfile.lock、requirements*.txt,确认是否出现 litellm==1.107.0。再检查所有 site-packages,寻找上述三个文件、__pycache__/cook*.pyc 和 litellm-1.107.0.dist-info。
下面这条原文命令用于在 Linux 或 macOS 上寻找启动钩子:
find / -name cook.pth 2>/dev/null
注意它只查 cook.pth,且隐藏了权限错误;没找到不能单独作为未受影响的证明。
容器要核对实际镜像,原文给出的查看命令是:
docker image ls --digests
注意输出中的 digest,而非仅看标签。原文列出的恶意摘要为 sha256:1bbcb1443bc962e45f5aad53fe3d2c30600e0379715fb6c664cdd3ac126c339e;litellm-database 和 litellm-non_root 还有各自摘要,所给正文没有展开。
还应查 UTC 11:39 至 14:20 的 CI 日志、pip 与 uv 缓存、内部镜像源。公共仓库撤包后,缓存仍可能让恶意版本在稍后安装。
/tmp/remote_code_loader.lock 被 Python 守护进程持有,是后门运行的线索;/tmp/.pc_once_72129118c59f5636c0bb 是数据已发送的标记,但不存在也不能排除泄露。网络日志可搜索仿冒域名及请求头 X-Token: XWiA8gZNa7NcX2XRoAEb。
发现命中后,原文的处置原则是:只要 1.107.0 在可出网主机执行过,就按凭证已经泄露处理。隔离主机并停止自动重启 Python,终止后门,清理文件,然后从可信环境重建。文中建议固定到 litellm==1.106.2,或采用 10 月 24 日恢复发布后的签名版本,例如 1.106.4。
凭证轮换也必须从干净机器进行。除了供应商 Key,还包括实例或任务 IAM 角色、代理 master key、全部 virtual keys、DATABASE_URL 密码和 gh CLI 登录。浏览器及 AI 工具会话要单独撤销,改 Key 不一定结束现存会话。
最后检查 AWS CloudTrail 中的 GetCallerIdentity、Bedrock 列举调用、网络外的凭证使用,以及模型平台的异常费用。
我的判断
这篇最有价值的提醒,是把 AI 网关当成高敏感凭证库管理。Microsoft 在原文引用的 2026 年 8 月建议中使用了「Tier-0」:也就是需要最高级别保护的秘密存储位置。一个网关持有多个供应商密钥时,这个定位有充分理由。
但「有签名就安全」并不成立。按原文复盘,攻击起点是一枚泄露近五个月却未撤销的密钥,后续涉及三名工程师的 GitHub CLI Token。攻击者使用真实凭证驱动正式流水线,可信发布、不可变标签和签名镜像并未被破解。它们能证明产物来源,却不能保证来源内部没有失守。
固定版本、校验哈希、按 digest 运行镜像,可以阻止依赖悄悄漂移;如果主动固定的产物已经恶意,这些措施也无法替你识别它。
作者建议延迟七天升级,因为当年两次攻击都在当天被发现。这个案例支持设置观察期,但没有证明七天适合所有团队,也没有给出与自动升级的对照数据。安全修复仍需要快速通道。
另外,三月和十月不能合并成同一攻击链。三月涉及 1.82.7、1.82.8 和 litellm_init.pth,项目方认为入口与 CI 中的 Trivy 有关;Datadog 将其关联到 TeamPCP,但未确认与十月存在联系。
这些判断依据的是原文汇集的项目公告和机构分析,作者没有提供自己的复现实验。文中「截至 10 月 26 日,1.106.4 为最新版本」也只能按该时间点理解,不能当成持续有效的升级结论。
能拿来做什么
- 如果团队部署过 LiteLLM,先查锁文件、构建记录和镜像 digest,再从干净环境检查残留文件,避免把
pip show当成排查第一步。 - 给 Python 依赖升级设置观察期,并保留安全修复快速通道;同时检查 pip、uv 缓存和内部镜像源,防止撤包后继续安装旧缓存。
- 为每个接入网关的应用分配独立 virtual key 和费用上限,让网关以 non-root 容器运行,并减少宿主机上无关的云凭证、npm Token 和个人会话。
- 把凭证泄露处置写进 CI 维护流程:删除泄密提交后仍要撤销密钥;使用 AWS OIDC 等短期凭证时,也要检查角色权限和异常调用,短期有效不代表不会被立即利用。