2026 年 3 月 24 日,LiteLLM 官方维护者的 PyPI 账户被入侵。在随后约 46 分钟内,两个被植入恶意代码的版本(1.82.7 和 1.82.8)共计被下载 46,996 次。Snyk 安全团队给出的 CVSS 评分是 9.3(Critical)——这是接近满分的危急评级。直到 PyPI 安全团队收到报告并将包隔离,攻击才被迫中止。
这不是一次因 Prompt Injection 绕过 MCP 防护而产生的"AI 安全"演示。这是一次真实的供应链入侵:维护者账户被盗,包被篡改,凭据被定向发送到攻击者控制的服务器,Kubernetes 集群试图被横向渗透。
Andrej Karpathy 在 X 上写道:"Supply chain attacks like this are basically the scariest thing imaginable in modern software."(这类供应链攻击基本上就是现代软件中最可怕的事。)
事后分析显示,攻击者在同一天发布了两个版本的恶意包,但 payload 和触发条件并不相同。
1.82.7 版本——注入点在 proxy_server.py,触发条件是导入 litellm.proxy。如果你只是用 SDK 的基础功能,这个版本影响有限;但如果你运行了 LiteLLM 代理服务,payload 就会激活。外泄目标:checkmarx[.]zone/raw。
1.82.8 版本——采用 .pth 文件机制,这是 Python site-packages 目录的标准特性:放在 site-packages 中的 .pth 文件会在任何解释器启动时自动执行,不需要 import,不需要运行任何代码,只要装了这个包,每一次 Python 进程启动都会触发。外泄目标:models[.]litellm[.]cloud。
两个版本的收集目标高度一致:SSH 私钥和配置、.env 文件、AWS/GCP/Azure 凭据、Kubernetes 配置、数据库密码、.gitconfig、Shell 历史记录、加密钱包、云元数据端点(IMDS、容器凭据)查询结果。
payload 本身经过精心设计:先做双层 base64 编码,然后通过 openssl 生成随机 32 字节 AES-256 会话密钥,用 AES-256-CBC 加密收集到的数据,再用硬编码的 4096 位 RSA 公钥加密会话密钥,最后打包成 tpcp.tar.gz 通过 curl POST 外发。这套加密架构意味着即使流量被截获,攻击者以外的任何人都无法解密。
46 分钟窗口的精确时间线
| 时间(UTC) | 事件 |
|---|---|
| 3 月 23 日 | 攻击者注册 litellm.cloud 域名(后续作为外泄目标服务器) |
| 3 月 24 日 ~08:30 | 使用被入侵的维护者账户 krrishdholakia 发布恶意版本至 PyPI |
| 10:39 | litellm 1.82.7 上线——含 proxy_server.py payload |
| 10:52 | litellm 1.82.8 上线——含 .pth 文件 payload |
| 10:58 | 受影响用户 Cursor 中 MCP 插件重新连接,uvx 自动拉取 litellm 及 77 个传递依赖(共 14.9 MiB) |
| ~10:59–11:08 | 进程爆炸:约 11,000 个 Python 进程被 fork 出,内存瞬间耗尽 |
| 11:08 | 终端无响应,被迫强制关机 |
| 11:25 | PyPI 安全团队隔离两个恶意版本 |
版本号的选择经过了精心设计:1.82.8 高于任何已发布的合法版本,因此会被所有使用 >= 版本约束的依赖声明自动解析为"最新版本"——大量间接依赖 litellm 的项目,只要在构建时解析到最新版本,都会中招。
恶意代码自己救了我们
发现者是 FutureSearch 的工程师 Callum McMahon。
Cursor 中一个已废弃的 MCP server (futuresearch-mcp-legacy) 依赖中有一个未锁定版本的 litellm。当 Cursor 重启后重新连接 MCP 时,uvx 自动下载了最新版本的 litellm——恰好是刚被植入木马的 1.82.8。
接下来的事情和大多数安全事件不同:发现者不是靠安全工具或代码审计找到问题,而是因为恶意代码自己写崩了。
.pth 文件的运行逻辑是:先 base64 解码 payload,再通过 subprocess.Popen 启动子进程。由于子进程同样是 Python 解释器,它在启动时也会触发 .pth 文件,再次 fork 出新进程,如此递归,形成指数级 fork bomb——约 11,000 个 Python 进程同时运行,RAM 瞬间被吃满,机器死机。
Karpathy 在 X 上直接点出了这件事最刺眼的地方:"如果攻击者没有 vibe code 这次攻击,而是写得干净利落,它可能潜伏数周甚至数月。"
值得注意的是,McMahon 在发现后借助 Claude Code 完成了完整的恶意代码逆向和公开披露:首次识别 payload 后两分钟,Claude 在本地容器中复现了整个攻击链;再过两分钟,一篇详细技术博客已经发布。整个发现与披露流程在同一次 AI 对话中完成。
规模被严重低估:88% 的传递依赖没有版本保护
FutureSearch 团队事后查询了 BigQuery PyPI 数据集,结果比最初报道更触目惊心:
- • 46,996 次下载发生在 46 分钟内
- • 2,337 个 PyPI 包将 litellm 列为直接依赖
- • 其中 2,054 个包(88%)的版本约束允许此次恶意版本,只有 283 个包(12%)通过锁定版本或设置上限避开了攻击
- • 影响范围按三条路径扩散:直接安装(pip/uv)、传递依赖(装了某个库,该库依赖 litellm)、CI/CD 流水线(在窗口期内重建环境,即使最终没有执行应用代码也已被感染)
更值得警惕的是:如果装了 1.82.7 但只用了 SDK 而没有导入 litellm.proxy,理论上不会触发 payload——这意味着一些不知道自己中招的人可能没有任何异常迹象。
攻击者是谁:TeamPCP 与被忽视的前传
Snyk 安全团队的追踪揭开了更完整的背景:这次攻击的幕后组织是 TeamPCP(又名 PCPcat、ShellForce、DeadCatx3),一个在 2025 年底浮现、主要针对云原生基础设施的黑客组织。
TeamPCP 早在数周前就通过入侵 Trivy(aquasecurity/trivy-action)获取了大量云原生项目的 CI 秘密,其中很可能包括 LiteLLM 维护者的 PyPI 凭据。Trivy 入侵事件本身也是一次大规模供应链攻击——攻击者向 GitHub Actions 流水线中植入了恶意代码,窃取 CI 密钥和 secrets。
这次 LiteLLM 攻击只是 TeamPCP 多线行动中的一环:Snyk 报告还指出,该组织同时通过恶意 VSCode 和 Cursor 扩展传播 ZOMBI 远控木马(含隐藏 VNC 服务器和 SOCKS 代理),以及大规模劫持 GitHub 账号。
这意味着:如果你的团队同时使用了 Trivy、LiteLLM、以及某些 AI coding 工具扩展,你可能同时被多条攻击线命中。
横向渗透与持久化:攻击者不只是偷密钥
payload 的详细拆解显示,这次攻击的意图不只是偷凭据,还包括建立持久化控制:
- • 本地持久化:写入
~/.config/sysmon/sysmon.py,并创建 systemd user service 在系统层面建立后门 - • Kubernetes 横向扩散:遍历集群所有 namespace,读取所有 secrets,并在每个节点上以特权模式部署
alpine:latestpod,挂载宿主文件系统,植入后门
这意味着攻击目标不只是"某台开发机",而是"以该机器为跳板拿下整个 Kubernetes 集群"。
.pth 文件:被忽视的 Python 默认攻击面
.pth 文件是 Python site-packages 的标准特性,放在 site-packages 目录中的 .pth 文件会在任何解释器启动时自动执行,在用户代码之前运行。pip 安装本身就会启动 Python 解释器,所以恶意代码在安装过程中就会触发——这意味着即使你只是运行 pip install 构建 CI/CD 环境,网络隔离也无法阻止安装阶段凭据的外泄。
为什么 GitHub 源码没有受污染
值得注意的是:GitHub 上的 LiteLLM 源码从未被入侵。所有恶意代码只存在于直接上传到 PyPI 的 wheel 包中,GitHub release 标签最高只到 v1.82.6.dev1。1.82.7 和 1.82.8 是攻击者直接绕过 GitHub CI/CD 流程、凭 PyPI 账户凭据上传的。
这说明 PyPI 的账户安全(多因素认证、凭据轮换)是独立于代码仓库安全的另一道防线,而且这道防线在此次事件中被率先突破。
如果你装了这两个版本
立即检查:pip show litellm,如果版本是 1.82.7 或 1.82.8,立刻从所有受影响环境中移除。
清除缓存:rm -rf ~/.cache/uv 或 pip cache purge,防止从缓存的 wheel 文件中重新安装。
查找持久化痕迹:检查 ~/.config/sysmon/sysmon.py 和 ~/.config/systemd/user/sysmon.service 是否存在;如果运行在 Kubernetes 中,审计 kube-system 命名空间下是否有以 node-setup- 开头的异常 pod。
轮换所有凭据:假设受影响机器上出现过的所有凭据均已外泄——SSH 密钥、云平台凭据(GCP ADC、AWS Access Key、Azure Token)、Kubernetes 配置、.env 文件中的 API Keys、数据库密码。
检查 Trivy:aquasecurity/trivy-action 和 aquasecurity/setup-trivy 的某些版本也遭到了 TeamPCP 的污染,应同步排查。
为什么这件事值得单独说
LiteLLM 事件最值得记住的结论有三层:
AI 基础设施中最高价值的节点被精准锁定。 LiteLLM 是一个握着所有 LLM API 密钥的代理层,打下它等于拿下整个 AI 基础设施的钥匙。
AI coding 工具的"丝滑体验"就是最真实的风险放大器。 uvx 的自动化依赖拉取,让这次攻击链变成了:MCP server 未锁定版本 → Cursor 自动加载 → uvx 自动下载 → 恶意包进入本地环境。一条完全不需要人工介入的杀伤链。
攻击者的攻击是多线并行的,不是一条线。 TeamPCP 同时打 PyPI、Trivy、VSCode/Cursor 扩展,靠单一防御无法覆盖所有入口。
主要参考来源:
- • LiteLLM PyPI:pypi.org/project/litellm(维护者 ishaan-jaff / BerriAI,97M 月下载)
- • Snyk 安全评级:CVSS 9.3 Critical,advisory SNYK-PYTHON-LITELLM-15762713:snyk.io/vuln/SNYK-PYTHON-LITELLM-15762713
- • Snyk 完整技术分析:snyk.io/articles/poisoned-security-scanner-backdooring-litellm
- • FutureSearch 技术分析:futuresearch.ai/blog/litellm-pypi-supply-chain-attack
- • FutureSearch 完整事后分析:futuresearch.ai/blog/no-prompt-injection-required
- • FutureSearch 量化分析(47,000 用户):futuresearch.ai/blog/litellm-hack-were-you-one-of-the-47000
- • FutureSearch 发现者完整复盘:futuresearch.ai/blog/litellm-attack-transcript
- • GitHub Issue #24512(payload 全量分析):github.com/BerriAI/litellm/issues/24512
- • GitHub Issue #24518(攻击者身份与时间线):github.com/BerriAI/litellm/issues/24518
- • Andrej Karpathy(@karpathy)X 分析帖,2026-03-24
夜雨聆风