3月24日10:39 UTC,两个恶意版本的LiteLLM出现在PyPI仓库。46分钟后,整个项目被PyPI强制隔离,所有版本下架。但这45分钟的窗口期,足以让任何安装过1.82.7或1.82.8的机器沦为筛子——SSH密钥、云凭证、Kubernetes密钥、加密货币钱包,全部被收割。

更麻烦的是,你可能根本没主动装过LiteLLM。它作为transitive dependency(传递依赖)藏在你的依赖树深处,而你一无所知。

攻击链:从Trivy到LiteLLM,一场5天的连环劫

攻击链:从Trivy到LiteLLM,一场5天的连环劫

这不是孤狼作案。威胁组织TeamPCP在5天内完成了三连击,每一步都精准踩在现代DevOps的盲点上。

3月19日:Aqua Security的Trivy扫描器被force-push恶意标签。被篡改的Trivy action专门窃取CI/CD runner中的机密。

3月23日:同一拨人劫持了Checkmarx KICS的全部35个GitHub Actions标签,顺手还搞了OpenVSX扩展。手法一致:先偷服务账户,再批量投毒。

3月24日:用Trivy事件中窃取的PyPI token,TeamPCP在10:39 UTC上传了恶意LiteLLM包。PyPI在11:25 UTC完成隔离——但token早已到手, damage done(损害已成)。

关键细节在这里:LiteLLM的CI/CD流水线没有固定Trivy版本号。当受损的Trivy action在LiteLLM的GitHub Actions runner里执行时,它顺手牵羊拿走了PYPI_PUBLISH token,直接发给攻击者。这个token随后被用来向PyPI推送恶意包。

LiteLLM本身是API密钥管理网关。攻击者专门挑了这个设计上就能接触组织内所有LLM API密钥的包下手。这不是运气,是算计。

恶意代码的两种面孔:从"导入触发"到"无差别感染"

恶意代码的两种面孔:从"导入触发"到"无差别感染"

1.82.7版本把payload藏在proxy/proxy_server.py里,import即触发。老派,但有效。

1.82.8版本玩得更脏。它塞了一个litellm_init.pth文件。Python的site模块在解释器初始化时会自动处理site-packages下的所有.pth文件——这意味着任何Python进程都会触发,不管你有没有import litellm。系统级Python、虚拟环境、容器里的Python,全中。

payload用了双层base64编码:

import base64; exec(base64.b64decode('...'))

解码后是三阶段攻击:

第一阶段(凭证收割):SSH密钥、AWS/GCP/Azure令牌、环境变量(含API key和token)、.env文件、Kubernetes配置,全部打包。

第二阶段(外渗):数据被发送到外部服务器,部分版本还尝试维持持久化访问。

第三阶段(横向移动):利用窃取的凭证尝试访问云基础设施和其他内部系统。

整个设计像个自动售货机:投币(安装包),出货(你的全部机密)。

为什么这次特别麻烦:transitive dependency的幽灵

为什么这次特别麻烦:transitive dependency的幽灵

大多数供应链攻击的受害者是主动安装恶意包的人。这次不同。

LiteLLM被大量项目作为间接依赖引入。你可能在requirements.txt里写的是langchain或llama-index,它们依赖litellm,于是你的环境自动中招。pip list不会告诉你危险来自何方,直到为时已晚。

incident response(事件响应)的复杂度也因此翻倍。你需要:

1. 扫描整个依赖树,确认litellm是否以任何形态存在

2. 检查3月24日前后的安装日志和CI/CD运行记录

3. 轮换所有可能暴露的凭证——不只是你记得的,还包括被收割的环境变量里的

4. 审计云账户和Kubernetes集群的异常访问

5. 如果用了加密货币钱包,检查资产是否被转移

TeamPCP显然研究过现代Python项目的依赖习惯。他们没选最热门的包,选了一个"足够热门又足够隐蔽"的中间层目标。

防御者的困境:当安全工具本身成为攻击向量

防御者的困境:当安全工具本身成为攻击向量

这次攻击最讽刺的闭环:Trivy是安全扫描工具,用来找漏洞的,结果成了漏洞本身。

LiteLLM用Trivy扫描自己的代码安全,Trivy被偷家,LiteLLM跟着陪葬。整个链条的起点是一个没固定版本的CI依赖——这种配置在GitHub Actions里遍地都是。

攻击者深谙现代DevOps的心理:你会固定生产代码的版本,但很少固定CI工具链的版本。毕竟,"安全扫描工具当然要用最新的"。

PyPI的quarantine机制这次反应算快,45分钟隔离在同类事件中属于优秀水平。但45分钟对自动化攻击来说,是永恒。

目前LiteLLM全项目仍在隔离状态,维护团队正在重建发布流程。TeamPCP的完整战果可能永远不会完全公开——有多少token被轮转、有多少集群被入侵、有多少钱包被清空,取决于每个受害者的审计深度。

你的Python环境里,现在有没有一个你从未直接安装、却默默存在的包?上一次检查依赖树的完整图谱,是什么时候?