LiteLLM供应链攻击分析:多段被注释备用载荷的发现
在litellm-1.82.8、litellm-1.82.7包的proxy_server.py代码中存在多段不同的b64_payload代码载荷,推测其是历史版本载荷。
概述
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归属地
卢森堡相关截图如下:

