找回密码
立即注册
搜索
发回帖 发新帖

6372

积分

0

好友

803

主题
发表于 1 小时前 | 查看: 3| 回复: 0

导语

2026 年 10 月 7 日,知名 Web 安全研究员 BRuteLogic(Rodolfo Assis) 在 X(原 Twitter)上发布了一条措辞简短、影响却极为严重的漏洞披露:

Node.js RCE via EJS (<v6.0.0-alpha)
Unsafe merge - Prototype Pollution leading to RCE via template rendering.

配图是一张实验截图:左侧是一段存在 unsafeMerge 递归合并漏洞的 Express 服务端代码,右侧终端里同一个 /api/status 接口先是正常返回 <h1>Hello Guest</h1>,在攻击者向 /api/config 投递一个含 __proto__ 键的 JSON 后,再次访问即输出 id 命令的结果——整个服务器被“远程命令执行”了。

EJS 原型污染 RCE 实验截图:unsafeMerge 漏洞服务端与攻击结果对比

这次披露并不是全新的漏洞类型,更像是一个值得警惕的版本边界提醒:研究者认为,EJS 官方直到 v6.0.0-alpha 才真正封堵住这条原型污染到 RCE 的利用路径。也就是说,大量仍停留在 3.x/4.x/5.x 的 Node.js 应用可能依然暴露在风险之中。

一、事件拆解:一张截图里的攻击链

推文附图可以拆解为三部分:

① 漏洞服务端(server.js)

  • unsafeMerge(target, source):递归合并函数,未对 __proto__、constructor、prototype 做过滤;
  • POST /api/config:把请求体 req.body 直接合并进内部配置对象;
  • GET /api/status:调用 res.render('index', { user: 'Guest' }) 渲染 EJS 模板。

② 普通模板(views/index.ejs)

<h1>Hello <%= user %></h1>

这个模板本身没有 SSTI 风险,但 EJS 渲染时读取的某些选项会被污染。

③ 攻击结果

  • 攻击者先 POST 一个 JSON 到 /api/config;
  • 再正常 GET /api/status;
  • 返回页面里出现 id 命令输出,证明命令执行成功。

攻击链:从 JSON 配置更新到模板引擎 RCE 的六步流程图

二、核心原理:不安全的合并 + EJS 选项继承

2.1 不安全的递归合并

服务端的核心问题不在 EJS,而在应用自己的合并函数。简化逻辑如下:

function unsafeMerge(target, source) {
  for (let key in source) {
    if (typeof source[key] === 'object') {
      if (!target[key]) target[key] = {};
      unsafeMerge(target[key], source[key]);
    } else {
      target[key] = source[key]; // X
    }
  }
  return target;
}

当攻击者发送:

{
  "__proto__": {
    "client": true,
    "escapeFunction": "function(){ /* 攻击者控制的代码逻辑 */ }"
  }
}

target[key] = source[key] 中的 key 为 __proto__ 时,所有对象的默认原型 Object.prototype 被改写。这不是普通的对象属性赋值,而是直接污染了原型链。

递归合并中 __proto__ 被当作普通键导致原型污染示意图

2.2 EJS 如何利用被污染的选项

EJS 在渲染模板时会读取多个选项,其中与本链相关的有:

  • opts.client:决定模板是否按客户端模式编译;
  • opts.escapeFunction:被拼接到生成的模板函数源码中,作为 HTML 转义函数。

当这些选项未在 opts 自身上定义时,JavaScript 会沿着原型链向上查找,于是落到被污染的 Object.prototype.client 和 Object.prototype.escapeFunction。EJS 把攻击者提供的字符串直接嵌入生成的模板函数,一旦该函数被执行,就等同于在服务端运行任意 Node.js 代码。

研究者为了验证 RCE,在 escapeFunction 里放置了一个会调用 child_process.spawnSync('id') 的函数;实际利用中,该位置可以替换为任何 Node.js 可执行代码。

EJS 渲染阶段被污染选项进入模板代码的攻击路径图

三、影响范围与版本边界

3.1 研究者声称的版本范围

BRuteLogic 在推文中明确标注:

影响范围:EJS < v6.0.0-alpha

也就是说,6.0.0-alpha 之前的所有版本(包括大量生产环境仍在使用的 3.x 系列)理论上都可能受影响。这个结论的前提条件是:应用层存在把用户输入合并进配置对象,并最终触发 EJS 渲染的代码路径。

3.2 已核实的版本事实

  • EJS 6.0.1 已于 2026 年 5 月 26 日发布(npmx 数据);
  • EJS v6.0 引入了安全硬化:官方文档说明,v6 在模板执行前会把 locals 浅拷贝到一个 null-prototype 对象,切断原型链回退,这正是针对原型污染利用的结构性修复;
  • EJS 3.x 系列的安全补丁历史
    • CVE-2022-29078(修复于 3.1.7)
    • CVE-2022-29869(修复于 3.1.8)
    • CVE-2023-29827(修复于 3.1.9)
    • CVE-2024-33883(修复于 3.1.10)

这些 CVE 说明 EJS 维护者一直在逐步封堵原型污染 gadget。但 BRuteLogic 本次披露指出,client / escapeFunction 这条路径直到 6.0.0-alpha 才被完全修复。

四、总结与合规边界

BRuteLogic 的这条推文把一个老生常谈的问题重新拉回到版本边界上:即使模板引擎本身被认为是“安全使用”的,只要应用层把用户输入不加以区分地合并进配置或选项,原型污染仍然是进入 RCE 的高速通道。

EJS 6.0 用 null-prototype 对象切断这条路径,是一个结构性的改进。对于仍在 6.0 以下的项目,安全团队的优先级应该是:

  1. 清点资产中 EJS 的具体版本;
  2. 检查是否有把 req.body / req.query 直接 spread 进模板选项的代码;
  3. 无法立刻升级时,先冻结原型、过滤危险键、加监控。



上一篇:恢复数据让折衣成功率升至95%:复旦美的CMU长时程具身基准
下一篇:SM2密码算法使用规范解读:GM/T 0009-2023密钥格式与预处理流程
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2026-10-9 05:03 , Processed in 0.074411 second(s), 39 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2026 云栈社区.

快速回复 返回顶部 返回列表