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):
- Compromised LiteLLM 1.107.0 on PyPI Installs a Credential Stealer and Backdoor — SafeDep,2026-10-21
- BerriAI/litellm Issue #47826:[SECURITY] litellm 1.107.0 compromised(含受影响文件 SHA-256、IOC、处置步骤)
- PyPI 项目页:当前最新版本为 1.106.2,1.107.0 已不可见
- 同项目 2026 年 4 月的前一次投毒事件 Issue #24518(v1.82.7 / v1.82.8)
为什么现在重要
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 帮你写代码」时代新增的、却普遍没纳入密钥轮换流程的资产。
工程师/产品人今天能做什么
- 先别
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 进程。 - 按顺序处置:删掉
cook.pth/cook.py/sitecustomize.py及对应__pycache__→ 杀掉持锁的守护进程 → 重新安装并钉死litellm==1.106.2;容器与 venv 建议直接重建而不是修补。 - 当成已泄露来轮换。 只要 1.107.0 在有外网的机器上装过,就假设外传成功(
/tmp/.pc_once_...标记只证明「成功了」,不存在不能证明「没成功」)。轮换:LiteLLM 代理库里的所有 provider key、master key / virtual key、DATABASE_URL密码、该主机可达的实例 IAM role 与 k8s service account。然后查 CloudTrail 里安装时段前后来自实例角色的GetCallerIdentity,以及这些凭证是否在自有基础设施之外被使用。 - 把镜像从 tag 钉到 digest。
latest/main-stable这类浮动标签在这次事件里被污染了近 6 小时。生产部署改用@sha256:摘要,并在 CI 里禁止浮动标签。 - 加一条安装期闸门。 在 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 就能重新激活。是否已有主机在窗口期执行过二阶段代码,无法从公开数据判断。