概述

2026年3月24日,AI 开发圈发生了一起震级极高的供应链安全事件:AI 开发者常用的 Python 库 LiteLLM 库(PyPI 日下载量超过300万,月下载近亿) 在 PyPI 上被植入恶意代码,威胁组织 TeamPCP 利用窃取的 PyPI 发布凭证,将恶意版本1.82.7 和 1.82.8 推送至了PyPI,影响了数百万开发者。

事件曝光后,各大安全厂商均对其进行了报道,事件轮廓逐渐清晰。然后,当笔者下载对应样本进行独立分析时,却有了意想不到的发现:在恶意代码深处,隐藏着多段被注释掉的备用载荷。

由于这一细节目前并未在公开网络分析报告中曝光,为了填补这一情报缺口,笔者决定基于此发现,对LiteLLM供应链事件进行详细分析,重点对比不同载荷代码之间的区别及潜在意图。

事件时间线

根据futuresearch团队的《No Prompt Injection Required》报告(https://futuresearch.ai/blog/no-prompt-injection-required/),对此事件进行简要概述:

  • 2026 年 3 月 24 日 10:52 UTC:携带恶意文件的litellm 1.82.8 版本被发布到 PyPI上(无对应 GitHub Tag,直接绕过正常发布流程)。
  • 2026 年 3 月 24 日(发现当日):一名工程师发现自己的Mac电脑及其卡顿,而自己的Mac电脑配置为48GB内存,逻辑上不应该出现卡顿情况;分析发现Mac电脑上存在大量python命令进程。
  • 2026 年 3 月 24 日(发现当日):工程师进一步排查,发现是自己的Cursor工具在使用MCP服务时,自动下载触发了携带恶意文件的LiteLLM依赖,随后工程师迅速定位到恶意文件 litellm_init.pth,并报告给 PyPI 安全团队。
  • 2026 年 3 月 24 日 12:30 UTC:确认 litellm 1.82.7 版本同样被污染。
  • 2026 年 3 月 24 日 20:15 UTC:恶意版本(1.82.7 和 1.82.8)被从 PyPI 下架。

相关截图如下:

触发原理

通过分析,梳理litellm-1.82.8与litellm-1.82.7的触发原理如下:

  • litellm-1.82.7
  • \litellm\proxy\proxy_server.py文件中植入了base64编码的payload,运行 litellm --proxy 或导入 litellm.proxy.proxy_server 时即会触发恶意代码功能。
  • litellm-1.82.8
  • \litellm\proxy\proxy_server.py文件中植入了base64编码的payload,运行 litellm --proxy 或导入 litellm.proxy.proxy_server 时即会触发恶意代码功能。
  • 新增了恶意./litellm_init.pth文件,litellm_init.pth文件中含有base64编码的payload。
  • ./pyproject.toml文件中植入了安装litellm_init.pth文件的代码。当litellm-1.82.8包安装后,litellm_init.pth文件即会被放在 site-packages 目录,Python 解释器每次启动时(任何 python 命令、任何脚本、Jupyter、uv、pip 等)都会自动读取并执行它,完全不需要 import litellm,安装即触发。

litellm-1.82.8

pyproject.toml代码截图如下:

正常litellm-1.82.6包的pyproject.toml代码截图如下:

litellm_init.pth代码截图如下:

proxy_server.py代码截图如下:

正常litellm-1.82.6包的proxy_server.py代码截图如下:

litellm包关于proxy_server.py代码的使用说明截图如下:

litellm-1.82.7

proxy_server.py代码截图如下:

正常litellm-1.82.6包的proxy_server.py代码截图如下:

litellm-1.82.8库分析

通过分析,笔者发现此恶意代码中存在多层Base64载荷,因此,笔者将按照Base64载荷作为攻击阶段分析:

  • b64_payload:调用B64_SCRIPT模块收集数据,将收集数据打包发送;
  • B64_SCRIPT:收集数据、横向移动安装后门、本地安装后门;
  • PERSIST_B64:后门文件;

在这里,笔者将以litellm_init.pth代码作为分析案例。

恶意litellm_init.pth

通过分析,提取litellm_init.pth文件中代码格式如下:

import os, subprocess, sys; subprocess.Popen([sys.executable, "-c", "import base64; exec(base64.b64decode('......'))"], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)

对上述代码结构进行分析,提取代码功能如下:

  • 隐蔽执行:利用 subprocess 启动一个新的 Python 子进程,用于悄悄运行被 Base64 编码的代码;
  • 静默运行:通过将stdout 和 stderr 重定向到 DEVNULL,实现抑制所有标准输出和错误信息的目的;

相关代码截图如下:

b64_payload-创建临时目录

解码litellm_init.pth文件中的base64数据,即可得到b64_payload代码,由于代码未做混淆处理,因此分析也比较简单。

通过分析,发现此py代码运行后,将创建临时目录用于存放数据,目录信息如下:

  • tmp/xxx/c:存放窃取的数据信息
  • tmp/xxx/p:存放内置的RSA公钥,用于RSA加密
  • tmp/xxx/session.key:存放openssl随机生成的 32 字节随机数,用于 AES-256-CBC 加密
  • tmp/xxx/payload.enc:存放 AES-256-CBC 加密后文件
  • tmp/xxx/session.key.enc:存放RSA加密后文件
  • tmp/xxx/tpcp.tar.gz:打包文件,用于外联发送

相关代码截图如下:

b64_payload-加载B64_SCRIPT

成功构建临时目录后,b64_payload代码将利用 subprocess 加载执行B64_SCRIPT代码。

相关代码截图如下:

尝试对B64_SCRIPT代码进行分析,发现此代码将大量利用 subprocess 执行命令,命令返回结果将存放于tmp/xxx/c文件中。

相关代码截图如下:

B64_SCRIPT-收集数据

通过分析,发现B64_SCRIPT代码运行后,将大量收集系统中存在的密钥及配置信息,收集的数据大致梳理如下:

  • 系统指纹信息
  • SSH密钥及配置信息
  • git凭证
  • 云厂商凭证
  • 容器凭证
  • 身份验证令牌和密钥
  • 数据库密码
  • CI/CD密钥
  • TLS/SSL私钥
  • 加密钱包
  • 系统认证数据
  • 等。。。

备注:收集数据路径均是Linux目录结构

系统指纹信息

相关代码截图如下:

SSH密钥及配置信息

相关代码截图如下:

git凭证

相关代码截图如下:

云厂商凭证

相关代码截图如下:

容器凭证

相关代码截图如下:

身份验证令牌和密钥

相关代码截图如下:

数据库密码

相关代码截图如下:

CI/CD密钥

相关代码截图如下:

TLS/SSL私钥

相关代码截图如下:

加密钱包

相关代码截图如下:

系统认证数据

相关代码截图如下:

B64_SCRIPT-横向移动安装后门

通过分析,发现B64_SCRIPT代码中存在横向移动功能:

  • 若找到 Kubernetes 服务帐户令牌,则会读取所有命名空间中的所有密钥。
  • 利用 service account token 在 kube-system 命名空间下创建特权 pod,挂载宿主文件系统并植入 sysmon 后门。

相关代码截图如下:

B64_SCRIPT-本地安装后门

通过分析,发现B64_SCRIPT代码除在Kubernetes环境中安装sysmon后门外,还将在本地安装sysmon后门。

相关代码截图如下:

PERSIST_B64-sysmon后门

通过分析,梳理sysmon后门功能如下:

  • 每 50 分钟发起一次 hxxps://checkmarx[.]zone/raw 外联请求,用以获取后续URL下载地址;
  • 通常返回的URL下载地址是"youtube.com"链接;
  • 若URL下载地址不是"youtube.com"链接,则下载模块到 /tmp/pglog 路径,并运行;
  • 成功下载模块后,则创建 /tmp/.pg_state 文件,用以防止重复执行载荷;

相关代码截图如下:

b64_payload-打包发送数据

成功收集数据后,则b64_payload代码将打包发送数据:

  • 将内置的RSA公钥存放于tmp/xxx/p文件中;
  • 调用openssl命令随机生成 32 字节随机数,用于后续 AES-256-CBC 运行,数据存放于tmp/xxx/session.key文件中;
  • 调用openssl命令,调用 AES-256-CBC 算法加密tmp/xxx/c文件,生成tmp/xxx/payload.enc文件;
  • 调用openssl命令,调用RSA算法加密tmp/xxx/session.key文件,生成tmp/xxx/session.key.enc文件;
  • 调用tar命令,打包tmp/xxx/payload.enc、tmp/xxx/session.key.enc文件;
  • 调用curl命令,将打包文件POST发送至https://models.litellm.cloud/地址;

相关代码截图如下:

litellm-1.82.7库分析

通过分析,发现litellm-1.82.7包的proxy_server.py代码中,存在多段b64_payload代码:

  • 未注释b64_payload代码载荷与litellm-1.82.8包litellm_init.pth文件中的Base64代码载荷相同;
  • 另两段注释b64_payload代码载荷与litellm-1.82.8包litellm_init.pth文件中的Base64代码载荷不同;

进一步分析,发现litellm-1.82.8包的proxy_server.py代码存在相同情况。

相关代码截图如下:

多段被注释的备用载荷

由于litellm-1.82.8、litellm-1.82.7包的proxy_server.py代码中存在多段不同的b64_payload代码载荷,因此,笔者推测注释代码是其历史版本载荷。

通过分析,笔者将不同载荷进行了详细的对比分析:

  • litellm-1.82.8、litellm-1.82.7包
  • 存在三种不同的b64_payload载荷;
  • 被注释的两种b64_payload载荷存在功能代码注释;
  • 未被注释b64_payload载荷收集的数据将存放于tmp目录下;
  • 被注释的两种b64_payload载荷收集的数据将存放于本地目录下的collected_vars.txt文件中;
  • b64_payload载荷
  • 内置的RSA公钥相同;
  • 存在两种不同的B64_SCRIPT载荷;
  • B64_SCRIPT载荷
  • 内置提取PERSIST_B64载荷的算法不同:一种通过Base64解密提取PERSIST_B64载荷,一种通过RC4+Base64解密提取PERSIST_B64载荷;
  • 最终提取的PERSIST_B64载荷内容相同;

不同litellm版本载荷对比

版本

文件名

变量名

Base64载荷MD5

备注

v1.82.8

litellm_init.pth

b64_payload

2618beac9d448f6d29df798e415350fa



proxy_server.py

b64_payload

2618beac9d448f6d29df798e415350fa



proxy_server.py

b64_payload

5194df3f3afb6f26a1e9f7ef09a1ff24

注释代码


proxy_server.py

b64_payload

0359f10c02e818d032a77d432534845d

注释代码

v1.82.7

proxy_server.py

b64_payload

2618beac9d448f6d29df798e415350fa



proxy_server.py

b64_payload

5194df3f3afb6f26a1e9f7ef09a1ff24

注释代码


proxy_server.py

b64_payload

0359f10c02e818d032a77d432534845d

注释代码

litellm-1.82.8包litellm_init.pth代码截图如下:

litellm-1.82.8包proxy_server.py代码截图如下:

litellm-1.82.7包proxy_server.py代码截图如下:

不同b64_payload载荷对比

b64_payload载荷MD5

变量名

Base64载荷MD5

备注

2618beac9d448f6d29df798e415350fa

PUB_KEY_CONTENT

2753ff0813974cfa89af7583448d2d62


2618beac9d448f6d29df798e415350fa

B64_SCRIPT

b7b9f3ecb285dcac3f45f817053c6ed2


5194df3f3afb6f26a1e9f7ef09a1ff24

PUB_KEY_CONTENT

2753ff0813974cfa89af7583448d2d62


5194df3f3afb6f26a1e9f7ef09a1ff24

B64_SCRIPT

b7b9f3ecb285dcac3f45f817053c6ed2


5194df3f3afb6f26a1e9f7ef09a1ff24

B64_SCRIPT

1252adfd1fcd492be1c582e08e1f669d

注释代码

0359f10c02e818d032a77d432534845d

PUB_KEY_CONTENT

2753ff0813974cfa89af7583448d2d62


0359f10c02e818d032a77d432534845d

B64_SCRIPT

1252adfd1fcd492be1c582e08e1f669d


b64_payload(2618beac9d448f6d29df798e415350fa)代码截图如下:

b64_payload(2618beac9d448f6d29df798e415350fa)代码中无功能注释代码,截图如下:

b64_payload(5194df3f3afb6f26a1e9f7ef09a1ff24)代码截图如下:

b64_payload(5194df3f3afb6f26a1e9f7ef09a1ff24)代码中含有功能注释代码,截图如下:

b64_payload(0359f10c02e818d032a77d432534845d)代码截图如下:

b64_payload(0359f10c02e818d032a77d432534845d)代码中同样含有功能注释代码,截图如下:

不同B64_SCRIPT载荷对比

B64_SCRIPT载荷MD5

变量名

Base64载荷MD5

备注

b7b9f3ecb285dcac3f45f817053c6ed2

PERSIST_B64

f11ca91d43ae0c18b613df8bb08bf31a


1252adfd1fcd492be1c582e08e1f669d

PERSIST_B64

f11ca91d43ae0c18b613df8bb08bf31a

RC4解密

B64_SCRIPT(b7b9f3ecb285dcac3f45f817053c6ed2)代码截图如下:

B64_SCRIPT(1252adfd1fcd492be1c582e08e1f669d)代码截图如下:

不同PERSIST_B64载荷对比

对B64_SCRIPT(1252adfd1fcd492be1c582e08e1f669d)中PERSIST_B64载荷进行RC4运算,发现RC4解密后内容与B64_SCRIPT(b7b9f3ecb285dcac3f45f817053c6ed2)中PERSIST_B64载荷相同。

相关截图如下:

IOCs

#litellm-1.82.7
8a2a05fd8bdc329c8a86d2d08229d167500c01ecad06e40477c49fb0096efdea

#litellm-1.82.8
d39f4e7a218053cce976c91eacf184cf09a6960c731cc9d66d8e1a53406593a5

https://models.litellm.cloud/
#域名注册时间
2026-03-23 15:42:00
#解析IP
46.151.182.203
#IP归属地
乌克兰

https://checkmarx.zone/raw
#域名注册时间
2026-03-22 13:39:39
#解析IP
83.142.209.11
#IP归属地
卢森堡

相关截图如下: