资讯动态

litellm遭投毒?Python供应链安全自查与止损指南

发布时间:2026/9/13 19:50:08 来源:尧图企业网站定制
这两天 litellm 被投毒的消息在开发群里和热搜上同时炸开了锅。如果你也是跑大模型应用的开发者手头大概率装了 litellm——这个开源的统一 API 网关平时用它接 OpenAI、Anthropic、本地 vLLM省事是真的省事。可一旦这种基础设施级的库被混入恶意代码后果就不是删个包那么简单了。这不是危言耸听而是真实发生在开源组件供应链上的安全事件。攻击者不需要攻破你的服务器只需要让你装到一个“看起来一模一样”的坏包就能把你环境变量里的 API Key、内部配置、甚至是模型服务地址全部打包带走。这篇文章不聊虚的只讲三件事第一litellm 为什么会被盯上所谓的“投毒”通常走哪几条路第二用一套可复现的自查流程教你 20 分钟内判断自己的机器到底中招没有第三如果已经踩雷怎么止损、怎么溯源、怎么避免下次再中招。适合所有用 Python 装过 litellm、或者在自己的项目里间接依赖了 litellm 的开发者。看完之后你不需要成为安全专家也能给自己做一次初步体检。1. 风波源头litellm 为什么会被投毒盯上1.1 litellm 在开发者栈里的位置先简单对齐一下背景。litellm 是一个 Python 编写的开源库/服务核心能力是把各家大模型 API 统一成一套 OpenAI 风格的接口。你只需要改一行 base_url就能从 GPT 切到 Claude再切到本地部署的 Qwen中间的重试、限流、负载均衡、成本统计它都帮你做掉了。很多 AI 应用、企业内部网关、自动化脚本里都跑着它而且为了方便服务进程常驻后台权限通常还不低。这种“承上启下”的位置决定了它的价值对上层应用来说它是流量入口对下层模型服务来说它是唯一出口。攻击者只要污染了这么一层就能在你毫无感知的情况下拿到所有经过它的请求内容包括你最值钱的东西——模型厂商的 API Key。更麻烦的是litellm 本身是一个开源项目依赖链很长同名的仿冒包、被篡改的历史版本、伪装成正确依赖的小工具随便哪一环出问题都可能把恶意代码带进来。1.2 攻击者要的是什么投毒代码一般干什么很多人以为“投毒”就是黑客闲着没事搞破坏实际完全不是。投毒的目的都很直接总结起来无非四类偷凭据扫描进程环境变量、读取.env文件、读取~/.aws/credentials、抓取/proc/self/environ然后通过网络把数据发到远程服务器。这是最典型的因为大模型 API Key 直接挂钩账单拿到就能白嫖甚至转卖。植入后门在包安装时往系统里写一个守护进程或者注册一个定时任务方便后续远程命令执行。常见手法是改crontab、写systemd服务、往 shell 启动文件里塞一键拉取的命令。挖矿悄悄占用 GPU/CPU 跑挖矿程序。这类代码不爱联网外传数据反而隐蔽性更强机器变卡、电费变高才发现。篡改配置改掉 litellm 的路由配置把本该发到 OpenAI 的请求转发到攻击者自己的服务器上实现中间人窃听。这个最阴险因为业务功能完全正常就是结果偶尔有点“怪”。1.3 所谓“投毒”通常走的三条路结合目前网上的讨论范围和供应链投毒的历史案例litellm 相关的投毒事件大概率逃不出下面三种路径名字仿冒与依赖混淆攻击者注册一个和 litellm 极度相似的包名比如lite-llm、litellmm、litellm-client或者利用某些内部源的依赖混淆漏洞诱导开发者安装到恶意包。只要你在 pip install 时打错一个字母或者内部源里恰好存在同名恶意包就会中招。官方版本被污染某个版本的 wheel 或 sdist 在发布后被替换成带毒版本。这种事件比较罕见但一旦发生影响面巨大因为所有从官方源拉取该版本的人都会被波及。验证方法就是做哈希比对后面会讲。依赖链上某个上游包被投毒litellm 本身没问题但它依赖的某个小库被污染导入时被连带加载执行。这种最隐蔽因为你检查 litellm 目录完全干净问题却在更深的地方靠肉眼很难发现。不管事件源头最终指向哪种路径对我们普通开发者来说自查的逻辑是通用的先确认安装来源再查代码异常最后看运行行为。2. 自查第一关确认你装的 litellm 来源和版本到底对不对2.1 先看看本地装了哪个版本、从哪来第一步永远不是去翻源码而是先弄清楚你机器上到底装了什么。打开终端执行pip show litellm重点关注几个字段Version看版本号是不是你预期的那一个如果出现一个你从没见过的版本要警惕。Location看安装目录是不是标准路径如果安装在/tmp、项目目录、或者奇怪的 venv 里要问自己为什么。Installer看是 pip、uv 还是 poetry 装的。Requires看依赖列表里有没有奇怪的新增项。如果你项目用的是 uv对应命令是uv pip show litellm如果你跑在 Docker 里需要进入容器执行同样的检查docker ps | grep 你的容器名 docker exec -it 容器名 pip show litellm另外litellm 也可能是作为间接依赖被装进来的你自己没主动安装过它。这种情况更要查因为越是藏在依赖深处的包越不容易被注意到。可以快速确认pip freeze | grep -i litellm2.2 和 PyPI 官方版本做哈希比对看到版本号之后去 PyPI 页面或者 GitHub Releases 看一下这个版本是否真实存在、是否已经被 yanked撤销。如果本地版本号在官方已经完全搜不到说明来源可疑程度很高。接下来做哈希比对这是判断包是否被篡改最硬核的手段。先用官方源把同版本包下载到本地pip download litellm你本地的版本号 --no-deps -d ./verify然后分别计算下载包和本地安装文件的 SHA256sha256sum ./verify/*.whl sha256sum ./verify/*.tar.gz # 再去 site-packages 里对比 sha256sum pip show 的 Location/litellm/version.py如果你本地文件的哈希和官方源下载的哈希对不上那基本可以确认你装的东西不是官方原包。即使对不上也不必慌张可能是内部源对包做了二次打包但后续需要重点排查。2.3 检查 pip 和 uv 的源配置很多投毒案例不是官方仓库被黑而是你的包管理器源被改了。执行pip config list看有没有index-url、extra-index-url指向非官方地址。同样检查环境变量env | grep -i -E pip|uv|pypi常见的安全隐患是公司内部源本意是为了加速但缺少校验和审计内部源上可能被放置了恶意同名包。还有一类问题是开发者的全局 pip 配置残留了一些公共镜像源或者快餐式代理源这些源本身不坏但没有官方源那么严格的发布审核。对于有requirements.txt或pyproject.toml的项目还要检查依赖声明里有没有出现--extra-index-url、--trusted-host这类参数。一旦发现要确认是项目有意为之还是被改动过。这一步能帮你排除掉大部分“手滑装错源”的情况毕竟官方仓库被真正攻破的事件极少反而是各种自定义源和混淆包名更常见。3. 自查第二关从代码层面找出隐藏恶意逻辑3.1 定位 litellm 的真实目录表面检查做完接下来要动真格翻代码。先拿到 litellm 的绝对路径python -c import litellm; print(litellm.__file__)正常情况下会输出类似/usr/local/lib/python3.11/site-packages/litellm/__init__.py的路径。如果你发现它导入的目录是用户目录、临时目录甚至是一个看起来像临时生成的哈希目录那问题就很严重了。3.2 搜索恶意代码特征码进入 site-packages 下的 litellm 目录搜可疑特征。手工用 grep 可以快速了解情况但更推荐写一个小脚本扫全目录因为子文件多的时候肉眼看不完import pathlib import re SITE pathlib.Path(/usr/local/lib/python3.11/site-packages/litellm) patterns [ rexec\(, reval\(, rbase64\.b64decode, ros\.system, rsubprocess\.(Popen|call|run), rrequests\.(get|post), rurlopen, rgetenv, rsocket\., rcrontab, rnohup, ] for p in SITE.rglob(*.py): try: text p.read_text(encodingutf-8, errorsignore) except Exception: continue for line_no, line in enumerate(text.splitlines(), 1): if re.search(|.join(patterns), line): print(f{p.relative_to(SITE)}:{line_no}: {line.strip()[:120]})跑完之后你大概率会看到一堆结果先别慌。litellm 本身是网络库有requests.post太正常了。关键要区分两类情况正常调用在函数内部、参数来自配置、URL 是官方 API 地址、行为在文档里有说明。异常调用在模块顶部、import 时立即执行、URL 是硬编码 IP 或陌生域名、读取环境变量后马上拼接进去、结果写入/tmp或者~/.cache的隐藏文件。3.3 重点检查 import 时会执行什么投毒代码最喜欢藏在__init__.py里因为只要import litellm就会加载。用 Python 的详细模式看导入过程python -v -c import litellm 21 | tail -100你会看到 Python 逐个加载了哪些模块、从哪个路径加载的。如果发现有人在导入阶段加载了非 litellm 相关的模块比如_requests_forwarder、metrics_exporter、telemetry_sink这类看起来像是内部回调但实际陌生名字的模块就要立刻去 site-packages 里找这个文件看它做了什么。还有一种常见情况litellm 目录下有额外的.so或.pyd文件。正常情况下纯 Python 包不应该有编译产物除非用了 C 扩展这些文件也常见于恶意代码可以检查文件的创建时间和哈希必要时上传到 VirusTotal 看检测结果。3.4 检查最近被创建或修改的文件投毒代码往往是后来被塞进已经安装的包里的所以文件时间戳是个很好的线索。查看 site-packages 里最近一周内被修改的 Python 文件find /usr/local/lib/python3.11/site-packages -name *.py -mtime -7 -newer /tmp find /usr/local/lib/python3.11/site-packages -name *.so -mtime -30重点排查两类位置一是 site-packages 顶层多出来的新目录二是用户的~/.local/lib目录。有些投毒包会专门把恶意模块写到用户级目录让你在系统级目录里找不到任何线索。4. 自查第三关运行时行为和网络外联排查4.1 进程层面有没有异常代码层面查完再查实时行为。先看进程ps aux | grep -i litellm正常的 litellm 进程应该由你的启动命令拉起可能是python -m litellm、litellm --config xxx或者在一个明确的 Python 解释器下面。如果你看到一个用户是 root、父进程是 init、命令行特别乱的 litellm 进程那很可能是被外部写进去的守护进程。同时看一眼 CPU 和内存占用top -o %CPU -n 1如果常规业务没跑但 CPU 一直被打满尤其是某个名字很正常的 Python 子进程占用了 300% 以上的 CPU就要高度怀疑挖矿木马了。4.2 网络外联有没有发往陌生地址的连接投毒代码偷到数据总得发出去所以网络连接是重灾区。用 lsof 或 netstat 查看 Python 进程的连接lsof -i -P -n | grep python # 或者 netstat -anp 2/dev/null | grep python重点看两类持续对外连接一个 Python 进程保持着到陌生 IP 的长连接数据量在持续增加。频繁发往非常规端口比如 80、443、8080 之外的端口尤其是 4444、5555、6667 这类常见于 C2 和后门的端口。如果你发现 litellm 进程连了一个你完全不认识的外部 IP可以把 IP 拿去查一下归属但不能只靠归属判断有的攻击者会把数据发到云厂商对象存储或者消息队列上域名看起来还挺正经。关键在于这个地址是否在你正常使用的服务列表里如果不是就要深究。4.3 定时任务和开机启动项投毒代码为了持久化一定会想办法让它在机器重启后还能跑起来。检查这几种常见持久化位置crontab -l ls -la /etc/cron.d/ /etc/cron.hourly/ /etc/cron.daily/ systemctl list-timers systemctl list-unit-files | grep -E litellm|python同时检查 shell 启动文件grep -n -E litellm|curl|wget|python ~/.bashrc ~/.zshrc ~/.profile ~/.bash_profile 2/dev/nullmacOS 用户还要额外看 LaunchAgentls -la ~/Library/LaunchAgents/ launchctl list | grep -i litellm看到 cron 里有一行python -c import requests; ...、或者 systemd service 的 ExecStart 指向了一个奇怪脚本这些基本就是持久化后门。不要犹豫把那行内容保留截图之后再做清理。4.4 日志与服务商审计记录最后一步可能很多人会忽略那就是去模型服务商的控制台看审计记录。litellm 的 Key 被偷走后攻击者通常会立刻发起试探性调用可能是问你当前模型列表、可能发几条极短的聊天请求然后才正式开始薅羊毛。去 OpenAI、Anthropic、Azure OpenAI 等控制台里拉取最近几天的用量记录重点看是否出现陌生 API Key 的调用是否在凌晨凌晨时段有不明请求请求的 model 是不是你项目里从没用过的调用 IP 来源是否分布在一个奇怪的地区如果发现了那就不仅是“可能中招”而是“已经确认失窃”要立刻进入下一节的止损流程。5. 确认中招后的止损操作隔离、换密钥、清后门5.1 第一时间隔离不是先删包如果前面任何一步确认了异常第一件事不是急着pip uninstall而是先把机器隔离。断网、从内网摘掉、停止对外服务。原因是保留现场才能做更完整的取证比如内存里的进程信息、建立的网络连接、写入磁盘之前的数据这些都是后续判断攻击范围的重要依据。具体操作上如果你跑在云服务器上建议在控制台做安全组变更只保留你的管理 IP 可以 SSH而不是直接关机。如果你跑在本机直接把网线/无线网络断开。隔离的目的是切断攻击者继续访问你机器的通道同时保住你后续查日志的可能。5.2 轮换所有关联凭据这一步的重要性超过删恶意文件本身。攻击者偷到的 Key 在你清理完木马之后依然有效所以必须先让它们全部失效再发新的。需要轮换的凭据清单包括但不限于所有模型服务商的 API KeyOpenAI、Anthropic、Azure、谷歌、阿里、百度、智谱等数据库连接串和密码云平台 AK/SKRedis、消息队列等中间件的密码任何写入过环境变量、config 文件、请求头里的内部 Token不要抱着“我这个 Key 只用在测试环境没关系的”这种侥幸心理。投毒代码一旦偷走 Key会用很短的时间完成批量试探测试环境的 Key 也可能被拿去盗刷。轮换时要特别注意先撤销旧的再创建新的避免新老 Key 在一段时间内同时在线上。同时确认你的应用配置已经更新不要让服务在重启后带着旧 Key 继续跑。5.3 清理恶意文件与持久化后门轮换完密钥再回头清后门。根据前面的检查结果把可疑文件逐一处理但建议先记录下文件路径和 SHA256 再删除方便后面喂给安全工具分析。清理顺序建议删除 site-packages 下确认的恶意文件和包。删除 crontab、systemd、LaunchAgent 里的恶意条目。删除 shell 启动文件里的恶意行。检查/tmp、/var/tmp、~/Downloads下有没有新出现的可执行脚本。检查 Python 包管理器配置里有没有被篡改的源地址恢复成官方源。如果你用的是 Docker最干净的做法是直接重建容器镜像而不是在容器里删文件因为容器文件系统里可能还有隐藏的修改点。重建之后用新的镜像重新部署服务并确认没有多余的持久化挂载。5.4 溯源和复盘止损做完还要问一句我是怎么中的招是装了一个仿冒包还是内部源被污染了还是某个同事分享的脚本里带了恶意包这一步决定了你的防护措施有没有针对性。翻一下 shell 历史history | grep -i -E pip|uv|install查一下最近的 pip 安装时间点ls -l --time-stylefull-iso /usr/local/lib/python3.11/site-packages/litellm对比恶意文件的创建时间和你的操作记录通常能还原出“哪天、我执行了什么命令、中招了”。如果实在找不到直接原因就把这次的恶意文件 hash 保存好提交到安全社区做情报共享说不定能帮到其他人。6. 给以后的自己加几道锁供应链安全实践6.1 锁定版本和哈希从根上减少随机性经此一役最值得养成的习惯就是不再使用不锁版本的依赖安装方式。在requirements.txt里用精确版本号不要用litellm1.40.0配合 pip 的哈希校验模式pip install --require-hashes -r requirements.txt--require-hashes会让 pip 只安装 hash 与记录一致的包任何一个包被篡改都会导致安装失败。虽然维护成本高一点但换来的确定性非常值。如果项目用 uv直接生成uv.lock锁定整个依赖树用 poetry 则生成poetry.lock。锁文件的意义在于每个人在任何时间安装拿到的都是同一份依赖组合不会因为某天某个小版本被悄悄替换就中招。6.2 更新依赖前先看 release notes 和 diff很多投毒事件发生在你“无脑升级”的那一刻。不要一行pip install --upgrade litellm就跑至少要先在官方 GitHub 看 release notes确认版本变更内容正常如果只升级了一个小版本用git diff看代码差异排除可疑提交升级后做一次最小冒烟测试确认核心调用正常这不是工作量大到不可接受的事相反对于基础设施级依赖多花十分钟看 diff 能帮你躲掉 90% 的坑。6.3 用代理仓库和扫描工具做缓冲有条件的话搭一个内网 PyPI 代理所有开发者默认只能从内部源拉包外部包必须经过审核才能同步进来。这样即使上游某个包被投毒也不会瞬间扩散到所有人机器上给了你一个缓冲窗口。工具方面在 CI 里加依赖审计扫描pip install pip-audit pip-audit或者用osv-scanner、safety在每次构建时扫描依赖树里的已知漏洞。容器环境里做镜像扫描同时生成 SBOM软件物料清单这样万一出现风险你能精确知道哪些镜像、哪些容器受影响。6.4 权限最小化和行为监控最后是运行层面。litellm 这种纯转发服务完全不需要 root 权限创建一个专用的低权限用户跑去useradd --system --no-create-home litellm-user敏感环境变量只注入到需要它的容器不要一股脑写进/etc/environment给所有进程共享。Linux 上还可以用 bpf 工具或者云安全组做基本的行为审计重点监控“进程读取环境变量后立即发起外联请求”这类组合动作。不用一次上很重的方案先从最小改动开始逐步补。写在最后的个人体会我把我手上几台机器按上面的顺序过了一遍最终确认没有中招但过程中确实看到了不少平时根本不会注意到的系统细节比如一堆早该清理的旧包、几个隐藏得很深的启动脚本、还有一些权限宽到没边的环境变量。说实话如果不是这次“瓜”上了热搜我大概率还会继续带着这些隐患跑很久。所以我的建议是别等事情爆出来才想起来做检查现在照着上面的五步走一遍二十分钟就能让心里有底。以后再装任何 Python 包都当成“可能会出事的包”来看待装之前看一眼源、锁一下版本、装完扫一遍这个习惯能帮你避免非常多麻烦。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价