从Trivy被劫持到LiteLLM投毒:一场横跨多项目的有组织供应链攻击

admin 2026-03-31 11:36:05 网络安全文章 来源:ZONE.CI 全球网 0 阅读模式

文章总结: 2026年3月,攻击组织TeamPCP通过劫持Trivy安全扫描器的GitHubAction版本并窃取CI/CD凭证,对日下载超300万的LiteLLM库发起供应链攻击,投放包含SSH密钥、云凭证及K8stoken窃取、加密外传和持久化横向移动的三阶段恶意载荷。攻击者利用pull_request_targetworkflow风险、未锁定依赖版本以及Python.pth启动钩子等弱点实施入侵,并以forkbomb意外暴露问题。建议受影响用户彻底轮换所有凭证、清除后门文件、检查K8s特权pod并在干净环境中重新部署,同时CI/CD流程应锁定依赖版本并审查GitHubActions配置。 综合评分: 85 文章分类: 供应链安全,漏洞分析,应急响应,安全建设,漏洞预警


cover_image

从Trivy被劫持到LiteLLM投毒:一场横跨多项目的有组织供应链攻击

Wtttthi Wtttthi

船山信安

2026年3月27日 18:10 广东

一个安全扫描器是怎么变成投毒入口的:LiteLLM供应链攻击

        事情发生在3月24号,LiteLLM的两个版本(1.82.7和1.82.8)被发现在PyPI上挂了恶意代码。这个包每天下载量三百多万次,两个坏版本在PyPI上待了大概三个小时才被隔离。

        攻击者叫TeamPCP,他们不是直接黑进LiteLLM的账号,而是绕了个弯:先搞了Trivy开源安全扫描器,LiteLLM的CI/CD流程里用到了这个工具。然后通过Trivy偷到了PyPI的发布凭证,顺手把LiteLLM的包给换了。

攻击链扩散时间

        2月底的时候,Trivy的CI被人利用pull_request_target这个workflow搞了一波,偷走了aqua-bot的凭证。3月19号,攻击者把Trivy的GitHub Action的tag指向了一个恶意版本(v0.69.4)。这个版本里藏着偷凭证的代码。

        LiteLLM的CI在跑的时候,会从apt装Trivy,而且没锁版本号。于是恶意版本的Trivy在CI环境里跑起来,偷到了PYPI_PUBLISH这个token。有了这个,攻击者就能以LiteLLM维护者的身份往PyPI上发包了。

        3月24号10:39,1.82.7上传;13分钟后,1.82.8上传。两个版本都有恶意代码,但藏的方式不一样。

两种投毒方式

1.82.7:恶意代码直接嵌在litellm/proxy/proxy_server.py里。只要代码里import litellm.proxy,payload就会触发。

1.82.8:这个版本更阴。它在site-packages/下面放了一个litellm_init.pth文件。Python的.pth文件在解释器启动时就会自动执行,不管你在跑什么代码——哪怕你只是pip install一下,或者IDE的language server启动,它都会触发。

        更离谱的是,.pth文件在wheel的RECORD里是正经声明的,hash对得上,所以pip install --require-hashes也拦不住。这种攻击手法在MITRE ATT&CK里叫T1546.018(Python Startup Hooks)。

三阶段payload

把恶意代码解码出来之后,可以看到它分了三个阶段干活:

第一阶段:偷数据

        它能偷的东西包括:SSH私钥、.env文件、Git凭证、shell历史里的API key、各种CI/CD配置、AWS/GCP/Azure的凭证文件、甚至Kubernetes的service account token。

第二阶段:加密外传

        C2域名models.litellm.cloud是3月23号注册的,比投毒早一天。RSA公钥跟Trivy、KICS攻击里用的是同一个,这是最硬的证据链。

第三阶段:持久化和横向移动

        后门装好之后,攻击者还得保证它不跑丢。所以第三阶段干的其实是两件事:一是把后门钉死在系统里,二是往K8s集群里扩散。

        本地持久化这块,他们在~/.config/sysmon/下面塞了个sysmon.py,又配了个systemd用户服务,名字叫“System Telemetry Service”。每5分钟去checkmarx.zone/raw问一次:下一步干啥?要干啥就从那边拉个文件下来,扔到/tmp/pglog里跑起来。当时那个接口返回的是YouTube链接,但想也知道,只要攻击者愿意,随时能换成真正的恶意载荷。

        K8s这边就是另外一种情况了。脚本会先看标准路径下有没有K8s service account token。有的话,它就遍历所有namespace把secrets全翻一遍。然后直接在kube-system里给每个节点起一个特权pod,用alpine:latest镜像,把宿主机根目录挂进去,再把那个sysmon.py后门装到节点上。这些pod名字都带node-setup-前缀,一眼就能认出来。

发现过程

        发现这事儿的人是FutureSearch的Callum McMahon。他在测试一个Cursor插件的时候,LiteLLM作为依赖被装上了。装完之后机器开始卡、内存爆了。追查发现是.pth文件触发了一个fork bomb——payload起了子进程,子进程又触发.pth,套娃了。这个fork bomb不是故意的,是个bug,但反而让问题暴露得更快。

怎么判断自己中招了

# 检查版本
pip show litellm | grep Version

# 检查后门文件
ls -la ~/.config/sysmon/sysmon.py
ls -la ~/.config/systemd/user/sysmon.service

# 检查临时文件残留
ls /tmp/tpcp.tar.gz /tmp/session.key /tmp/payload.enc

# 检查.pth文件
find $(python3 -c "import site; print(' '.join(site.getsitepackages()))") -name "*.pth" -exec grep -l "base64\|exec" {} \;

# 检查K8s
kubectl get pods -A | grep node-setup-

修复建议

没装过坏版本的直接锁版本到<=1.82.6

装过的别只升级。payload在pip install的时候就已经跑过了。建议:

  1. 删掉后门文件,rm -f ~/.config/sysmon/sysmon.py 和对应的systemd服务
  2. 换所有能换的凭证,SSH key、云服务key、Git token、数据库密码、钱包助记词
  3. 检查Kubernetes集群里有没有node-setup-*的pod
  4. 重新部署在干净环境里,别在原系统上修修补补

#

这个事暴露了几个问题:

  • GitHub Actions的pull_request_target workflow设计上有风险
  • CI/CD里用apt装工具不锁版本是裸奔
  • .pth文件作为Python的启动钩子,目前pip没有任何安全检查
  • 流行的AI工具链成了供应链攻击的新靶子——LiteLLM、Trivy、KICS都是这类

        攻击者用了同样的RSA key、同样的tpcp.tar.gz命名、同样的Telegram频道,横跨三个项目,说明他们是成体系地在做这件事。

参考英文链接:

https://snyk.io/articles/poisoned-security-scanner-backdooring-litellm/#stage-3-persistence-and-lateral-movement


免责声明:

本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。

任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。

本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我

本文转载自:船山信安 Wtttthi Wtttthi《从Trivy被劫持到LiteLLM投毒:一场横跨多项目的有组织供应链攻击》

评论:0   参与:  0