post cover

AI 热点快报:LiteLLM 1.107.0 被投毒,AI 网关成了供应链攻击的首选入口(2026-10-22)


事件与背景

2026 年 10 月 21 日,PyPI 上的 litellm==1.107.0 被发现携带凭证窃取器与远程代码后门。这不是「某个小工具被人塞了恶意依赖」,而是一个被大量团队放在 LLM 调用链正中央的网关库,在分发端被投毒。

时间线(UTC,均为 10 月 21 日):11:20–11:37 恶意 wheel 被构建;11:39–11:40 上传 PyPI(全部 8 个文件,覆盖 Linux / macOS / Windows、glibc 与 musl);11:50 官方发布机器人 litellm-dispatcher[bot] 照常发布 GitHub Release v1.107.0;13:40 该版本从 PyPI 消失;17:13 Docker 镜像的 latest、main-latest、main-stable 标签才被撤下。

注入点极其克制:源码分发(sdist)的 pyproject.toml 只比 git tag v1.107.0 多一行——

[tool.maturin]
data = "cook"   # 注入行,上游不存在

maturin 的 data 指令会把 cook/ 目录复制进 wheel 的 purelib/,于是 cook.pth、cook.py、sitecustomize.py 三个文件被 pip 装进 site-packages 根目录。三个文件在 wheel 的 RECORD 里有正确的哈希,zip 时间戳与正常文件一致,说明是构建时被改、而非事后重打包。tag 本身是干净的,所以「源码审过了」并不能救你。

载荷分两段:

  • cook.pth 内含 import cook,Python 启动时会执行 .pth 里以 import 开头的行。因此凡是这台机器上启动的 Python 进程都会中招——应用、pip、IDE 语言服务器、CI job,即使那个进程从不 import litellm。
  • cook.py(约 22 KB)是自称 privcollect 的窃取器:SSH 私钥、云凭证(AWS/Azure/gcloud/kube/docker)、CI/CD 密钥、浏览器密码、.env、GPG 与 TLS 私钥、每个可读进程的 /proc/*/environ,以及最要命的一项——若存在 DATABASE_URL,它会起 prisma 把 LiteLLM 代理库里 LiteLLM_CredentialsTable 与 LiteLLM_ProxyModelTable 整个导出,也就是这家公司的所有上游模型厂商 API Key。数据用 AES-256-CBC 加密、密钥再用 RSA-4096-OAEP 封装,POST 到 www.litellm.dev(不是官方的 litellm.ai)。
  • sitecustomize.py 是持久化后门:fork + setsid 脱离父进程,每小时从攻击者的 GitHub 仓库读一个域名,再 GET http://<domain>/codepy 并 exec()。

来源(均已用 curl 验证返回 200):

为什么现在重要

1. AI 网关是新的「凭证王座」。 一个 LiteLLM 代理实例里存着 OpenAI、Anthropic、Gemini、Bedrock 的 key,是整条 AI 产品的付款与配额入口。攻击者不需要攻破任何模型厂商,只要拿到这个库的发布权,就等于一次性拿到成百上千家公司的全部上游令牌。影响判断:AI 基础设施组件的发布链安全,权重已经高于它自身的代码质量。

2. .pth + sitecustomize 绕过了所有「我没 import 它」的自我安慰。 这两个机制属于 Python 解释器启动链,在业务代码之前执行。影响判断:依赖审计不能只看「运行时是否加载」,必须看安装产物里多出了什么文件——这次多出来的只有 3 个文件、约 47 KB。

3. 构建产物 ≠ 源码,SBOM 在这一层是瞎的。 tag 干净、上游无 data = "cook" 提交、sdist 只多一行,最终 wheel 却带毒;PyPI 上该版本没有 provenance attestation,下游无法从密码学上确认「这个 wheel 是从那个 commit 构建的」。影响判断:requirements.txt 锁版本不够,锁哈希 + 要求 attestation 才是可验证的边界。

4. 攻击基础设施是「形近」的:仓库 Berri-AI/litellm vs 官方 BerriAI/litellm,域名 litellm.dev vs 官方 litellm.ai。 前者现已 404,后者注册于 10 月 13 日、距投毒仅 8 天。影响判断:这类抢注是预制件,任何有下载量的 AI 项目都该把自己的近似名与近似域名先占掉。

5. AI 编码工具的凭证被列为一等目标。 清单里点名 Claude Code、Claude Desktop、Cursor、Windsurf、Copilot、Codex auth.json、Gemini CLI、Hugging Face token。影响判断:这些 token 背后的权限往往等同仓库写权限,是「AI 帮你写代码」时代新增的、却普遍没纳入密钥轮换流程的资产。

工程师/产品人今天能做什么

  1. 先别 pip install,先查文件。 在被污染的 venv 里跑 pip 会再次触发 cook.pth。直接用 shell 检查:find / -name "cook.pth" -o -name "sitecustomize.py" 2>/dev/null(重点看各 venv 与镜像的 site-packages),以及 /tmp/remote_code_loader.lock、环境变量 PRIVCOLLECT_ACTIVE=1、cwd 为 / 的孤儿 python 进程。
  2. 按顺序处置:删掉 cook.pth / cook.py / sitecustomize.py 及对应 __pycache__ → 杀掉持锁的守护进程 → 重新安装并钉死 litellm==1.106.2;容器与 venv 建议直接重建而不是修补。
  3. 当成已泄露来轮换。 只要 1.107.0 在有外网的机器上装过,就假设外传成功(/tmp/.pc_once_... 标记只证明「成功了」,不存在不能证明「没成功」)。轮换:LiteLLM 代理库里的所有 provider key、master key / virtual key、DATABASE_URL 密码、该主机可达的实例 IAM role 与 k8s service account。然后查 CloudTrail 里安装时段前后来自实例角色的 GetCallerIdentity,以及这些凭证是否在自有基础设施之外被使用。
  4. 把镜像从 tag 钉到 digest。 latest / main-stable 这类浮动标签在这次事件里被污染了近 6 小时。生产部署改用 @sha256: 摘要,并在 CI 里禁止浮动标签。
  5. 加一条安装期闸门。 在 CI 里对 site-packages 做「安装前后 diff」:新增 .pth、新增/覆盖 sitecustomize.py、以及 base64+zlib+exec 组合直接 fail。同时对内网出站做 allowlist——litellm.dev、任意 raw.githubusercontent.com 拉取即执行、以及非必要的 169.254.169.254 访问都该被拦。

待观察

  • 根因仍未定。 恶意 wheel 的构建特征与 BerriAI 正常发布流水线一致,SafeDep 判断(中高置信度)是私有流水线从「被篡改的 git 源」构建,但是维护者凭据、机器人 token、私有镜像还是构建引用被换,只有 BerriAI 自己的流水线日志能确认。目前公开信息不足以指认任何个人。
  • Docker 镜像未被审计。 Issue #47826 明确写了 “We did not inspect the v1.107.0 Docker images”,而标签直到 17:13Z 才撤下。10 月 21 日 11:40–17:13 之间拉过 latest 的环境,应按受影响处理。
  • 第二阶段后门处于休眠但可随时重启。 攻击者的 domain.txt 自 10 月 20 日 09:15Z 起为空、账号已删,codepy 目前取不到;但运营者只需一次 commit 就能重新激活。是否已有主机在窗口期执行过二阶段代码,无法从公开数据判断。