LiteLLM 1.107.0 发布流水线被劫持
攻击者用 3 个失窃的 GitHub CLI 令牌改写 LiteLLM 的私有发布流程,从仿冒仓库 Berri-AI/litellm 构建并发布了 1.107.0。恶意代码在任何 Python 启动时运行,窃取凭据,并安装每小时轮询一次的远程代码加载器。
段子原句“CI 昨晚刚从 PyPI 拉了新包并推送到了生产环境,现在全公司电脑都装上了这套 Harness”
- 编号
- 无 CVE 或 GHSA 编号
- 严重度
- 无 CVE;已确认的恶意版本
- 利用状态
- 恶意版本已发布恶意版本已发布:恶意代码已确认。PyPI 上架约 1 小时 44 分钟,Docker 标签被劫持约 2 小时 33 分钟。
- 对段子公司
- CI 如果在 10-21 19:39 至 21:23(北京时间)拉取未固定版本,拿到的就是恶意包。生产环境和全员电脑都要按失陷处理。
- 影响范围
- LiteLLM 每周下载约 2,300 万次。受影响的是 10-21 11:39 至 13:23 UTC 的 pip 安装,以及 11:46 至 14:20 UTC 启动的
latest等镜像。实际下载量和受害组织数未公布。这是 2026 年第二次投毒,3 月的 1.82.7 和 1.82.8 被下载超过 119,000 次。 - 修复版本
- →固定到
litellm==1.106.2 - →恶意版本已从 PyPI、Docker Hub、GHCR 移除
- →固定到
结论
2026-10-21,攻击者劫持 LiteLLM 的私有发布流水线,向 PyPI 和 Docker 镜像仓库发布了带恶意代码的 litellm==1.107.0。[1] [2]PyPI 上的恶意版本在 11:39 至 13:23 UTC 可下载,约 1 小时 44 分钟。[1]Docker Hub 的 v1.107.0、latest、main-latest、main-stable 标签在 11:46 至 14:20 UTC 指向恶意镜像。[1]恶意代码会在任何 Python 进程启动时运行,窃取云、开发、AI 工具、浏览器和 LiteLLM 代理数据库中的凭据。[2] [3]这是 LiteLLM 在 2026 年第二次被投毒。第一次是 3 月 24 日的 1.82.7 和 1.82.8。[2] [5]
事件经过
LiteLLM 在 3 月事故后加固了发布流程。它把发布移到私有仓库,使用 Trusted Publishers,并对 Docker 镜像签名。[1]10 月 20 日(周一),LiteLLM 临时扩大了发布权限,让两名内部工程师每天发布补丁回移版本。[1]攻击者持有 3 名开发者的 GitHub CLI OAuth 令牌。其中一个令牌属于 GitHub 组织所有者。[1]攻击者用所有者令牌关闭分支保护,强推了一个修改后的发布工作流。[1]
修改后的工作流从仿冒仓库 Berri-AI/litellm 构建制品。正版仓库是 BerriAI/litellm,两者只差一个连字符。[1] [2]第二个开发者令牌通过了生产发布审批。第三个令牌随后删除了工作流运行记录,减少了可用于调查的日志。[1]因为制品走的是正常发布通道,构建来源证明(provenance)看起来是合法的。它如实记录了仿冒仓库作为源码来源。[2]
LiteLLM 认为,令牌来自其公网暴露的内部 LiteLLM 部署的日志。官方称,Claude Code 在某些情况下会把 GitHub CLI 令牌写入响应体,而这些响应体被记录到了日志里。[1]攻击者如何进入这个内部部署尚未确认。官方怀疑是利用了一个最近修复的漏洞。[1]
技术细节
恶意版本在 site-packages 中加入 3 个文件:cook.pth、cook.py、sitecustomize.py。[2]Python 每次启动都会自动执行 .pth 文件和 sitecustomize.py。所以只要环境里装了这个版本,运行任何 Python 程序都会触发恶意代码,即使程序从未导入 LiteLLM。[2]运行 pip 本身也会再次触发。删除 litellm 包目录不能清除这些文件。[1] [2]
cook.py 是凭据窃取器,有 16 个收集模块。[3] 收集范围如下:
- 云凭据:AWS、GCP、Azure 凭据文件和实例元数据令牌,Kubernetes、Docker、Vault、Terraform 密钥。[2] [3]
- 开发凭据:SSH、GPG、TLS 私钥,Git、npm、PyPI 令牌,
.env文件,Shell 历史。[2] [3] - AI 工具凭据:Claude Code、Cursor、Copilot、Gemini CLI、Codex 等 AI 编程工具的会话和配置。[2] [3]
- 浏览器:Chrome、Edge、Brave 等浏览器保存的密码和 Cookie。[3]
- 进程环境变量:在 Linux 上读取所有可读进程的
/proc/<pid>/environ。[3] - LiteLLM 代理数据库:如果设置了
DATABASE_URL,它会查询代理保存的上游模型服务商凭据。[2] [3]
窃取的数据先用 AES-256-CBC 加密,再用内置的 RSA-4096 公钥包裹密钥。[3]数据通过明文 HTTP POST 发往仿冒域名 www.litellm[.]dev/healthcheck。[2] [3]因为数据已加密,抓包无法判断具体被偷了什么。[3]
sitecustomize.py 是远程代码加载器。它在 Linux 和 macOS 上派生一个常驻守护进程,并持有 /tmp/remote_code_loader.lock。[3]守护进程每小时读取 GitHub 仓库 letters9813/pythonclient-test 中的 domain.txt。如果文件里有域名,它就下载并执行该域名下的 /codepy。[2] [3]发布时 domain.txt 为空,所以未观察到第二阶段载荷。[2] [3]该 GitHub 账号已被删除。但如果有人重新注册同名账号并创建同名文件,仍在运行的守护进程会执行他的代码。[3]
| 类型 | 值 |
|---|---|
| 投放文件 | cook.pth、cook.py、sitecustomize.py |
| 外传地址 | http://www.litellm.dev/healthcheck |
| 外传请求头 | X-Token: XWiA8gZNa7NcX2XRoAEb |
| 窃取成功标记 | /tmp/.pc_once_72129118c59f5636c0bb |
| 加载器锁文件 | /tmp/remote_code_loader.lock |
| 环境变量 | PRIVCOLLECT_ACTIVE=1 |
| 恶意镜像索引 | sha256:1bbcb1443bc962e45f5aad53fe3d2c30600e0379715fb6c664cdd3ac126c339e |
| 仿冒源码仓库 | github.com/Berri-AI/litellm,提交 20535d8fa11f786a6c24df7488a0e449ed5f09ad |
影响范围
LiteLLM 每周下载量约 2,300 万次。[2]受影响的是 11:39 至 13:23 UTC 之间未固定版本、通过 pip 安装 LiteLLM 的环境。[1]也包括在 11:46 至 14:20 UTC 之间启动 v1.107.0、latest、main-latest、main-stable 镜像的容器。[1]GHCR 上的 litellm、litellm-database、litellm-non_root 三个镜像都受影响。[1]使用私有企业版镜像仓库的企业客户不受影响。[1]官方和第三方都没有公布实际下载次数、受害组织数或被盗凭据数。这些数字未验证。
时间线
- 2026-10-13 00:45 UTC仿冒域名 litellm.dev 注册。[3]
- 2026-10-18 04:17 UTC攻击者创建用于远程加载的 GitHub 仓库。[3]
- 2026-10-21 10:08:29 UTCGitHub 分支保护拒绝了对私有发布工作流的未授权修改。[1]
- 2026-10-21 10:08:34 UTC攻击者用特权开发者令牌绕过保护,强推工作流修改。[1]
- 2026-10-21 10:46:39 UTC修改后的工作流从 Berri-AI/litellm 开始预发布构建。[1]
- 2026-10-21 11:11:18 UTC第二个开发者令牌通过生产发布审批。[1]
- 2026-10-21 11:39:51 UTC7 个恶意 wheel 开始上传到 PyPI,11:40:13 完成。[1]
- 2026-10-21 11:46:56 UTC恶意 Docker 镜像发布。[1]
- 2026-10-21 11:53:49 UTC第三个开发者令牌删除工作流运行记录。[1]
- 2026-10-21 12:06 UTC用户在 GitHub 报告该问题。[3]
- 2026-10-21 13:23 UTCPyPI 已移除 1.107.0。[1]
- 2026-10-21 14:20 UTC所有受影响的 Docker Hub 标签被删除或改指向干净镜像。[1]
- 2026-10-21 16:42 UTC@Manjusaka_Lee 发布本段子,北京时间为 10-22 00:42。
- 2026-10-21 20:30 UTCLiteLLM 更新公告,称已识别攻击链。[1]
修复与缓解
- 不要先运行
pip install。因为任何 Python 启动都会再次执行恶意代码。[1] - 在 Shell 中删除每个受影响
site-packages里的cook.pth、cook.py、sitecustomize.py及对应.pyc文件。[1] [2] - 结束持有
/tmp/remote_code_loader.lock的守护进程。[1] - 重建虚拟环境和镜像,并固定到
litellm==1.106.2。[1] [2] - 只要主机曾安装
1.107.0且能访问外网,就假定凭据已外泄。没有标记文件不能证明安全。[1] - 从干净的机器轮换主机可访问的全部凭据。重点是实例 IAM 角色、LiteLLM 代理数据库中的所有服务商密钥、主密钥和虚拟密钥、
DATABASE_URL密码。[1] - 在 CloudTrail 中检查安装时间附近的
GetCallerIdentity调用,以及之后从外部使用该角色凭据的记录。[1]
3 月 24 日的第一次投毒
2026-03-24,攻击者向 PyPI 发布 litellm 1.82.7 和 1.82.8。[5] [6]那次的入口不同。LiteLLM CI 使用的 Trivy 依赖被投毒,泄露了 PyPI 发布令牌。[5] [7]1.82.8 用 litellm_init.pth 在 Python 启动时运行载荷,并尝试在 Kubernetes 中部署特权 Pod。[7]PyPI 统计,这两个版本在暴露期间被下载超过 119,000 次。[6]Datadog 称,截至 10-21,尚未发现两次事故之间存在关联。[2]
对段子中公司的影响
段子说“CI 昨晚刚从 PyPI 拉了新包并推送到了生产环境,现在全公司电脑都装上了这套 Harness”。如果 CI 在 10-21 11:39 至 13:23 UTC(北京时间 19:39 至 21:23)拉取了未固定版本的 LiteLLM,它拿到的就是 1.107.0。[1]这时生产环境和每台员工电脑都要按“已失陷”处理。因为恶意代码在任何 Python 启动时运行,并会读取所有进程的环境变量。[2] [3]员工电脑上的浏览器密码和 AI 编程工具会话也在窃取范围内。[3]如果 Harness 后面接了 LiteLLM 代理数据库,所有上游模型服务商的密钥都要轮换。[2] [3]在段子列出的所有组件中,只有这一项是“恶意代码已确认存在,而且公司已经装上”。所以它的处置优先级最高。
存疑与未验证
- 实际下载次数、受害组织数和被盗凭据数量:未公布,未验证。
- 攻击者如何进入 LiteLLM 内部部署:官方怀疑是一个最近修复的漏洞,未确认。[1]
- Claude Code 把令牌写入响应体的具体条件:来自 LiteLLM 单方说法,未独立验证。[1]
- 两次投毒是否为同一攻击者:未确认。[2]
- LiteLLM 提到一个计划在 10-27 披露的漏洞,企业版已提前修复。该漏洞与本次事故的关系未验证。[1]
- 段子的发帖时间由推文 ID 推算,不是 X 页面直接显示的时区时间。
参考资料
- [1] Contained: Oct 21, 2026 LiteLLM release v1.107.0 Compromised
- [2] Datadog Security Labs: LiteLLM supply chain compromise
- [3] StepSecurity: litellm 1.107.0 Compromised on PyPI
- [5] LiteLLM Security Update: Suspected Supply Chain Incident, March 2026
- [6] PyPI Incident Report: LiteLLM and Telnyx supply-chain attacks
- [7] Snyk: How a Poisoned Security Scanner Became the Key to Backdooring LiteLLM
调研截止 2026-10-22 UTC+8 · 未验证的内容已在正文中标注 · 数字以原始来源为准