Node.js 这个反序列化的漏洞到底有多大?

2 年前

外刊君还在考虑在公司内做一些Node.js的尝试,正要撸起袖子写几行代码,就被这个漏洞给吓尿了,【漏洞分析】利用Node.js反序列化的漏洞执行远程代码(含演示视频) - 安全客 - 有思想的安全新媒体关于Node.js存在反序列化远程代码执行漏洞的安全公告Node.js惊爆重大漏洞。妈啊,这种惊天大漏洞,外刊君今年试点Node.js的kpi是不是完不成了?

这个漏洞是什么?

读了读上面这三篇文章,文中都提到了一个 node-serialize 的 Node.js 库。这个库提供的 API 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);

unserialize 内部有这么一段代码(简单起见,我稍微加工了一下):

if (obj[key].indexOf('_$$ND_FUNC$$_') === 0) {
  obj[key] = eval('(' + obj[key].substring('_$$ND_FUNC$$_'.length) + ')');
}

我们简化一下,

eval('(function(){require(\'child_process\').exec(\'ls /\',function(error, stdout, stderr) { console.log(stdout)});}()"})')

简化一下例子,如果用户输入 {"rce":"_$$ND_FUNC$$_process.exit()"},就能把服务器给停掉。

eval('(process.exit())')

这是 Node.js 的重大漏洞吗?

看看国家安全漏洞共享平台是怎么说的:

根据漏洞研究者测试结果,由于涉及IIFE函数表达式,漏洞影响到Node.js现有的所有版本。根据CNVD秘书处的普查结果,目前互联网上直接标定使用node.js运行环境的服务器约有6.8万余台,其中排名前五名的国家和地区是美国(占比58.9%)、中国(23.2%)、英国(4.1%)、荷兰(3.6%)、德国(3.0%)。由于一个应用广泛的名为Express的WEB应用开发框架是基于node.js运行环境的,根据CNVD秘书处初步普查结果,受该漏洞影响的网站服务器有可能超过70万台,后续CNVD将进一步进行漏洞实际威胁影响的精确评估,做好境内用户的应急响应工作。

本段的说法有几点有待商磋。

由于涉及IIFE函数表达式,漏洞影响到Node.js现有的所有版本:这个漏洞处在 eval 函数而不是 IIFE;本文的开头已经给出了对应CVE(通用漏洞列表)的链接 http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2016-3087

Apache Struts 2.3.20.x before 2.3.20.3, 2.3.24.x before 2.3.24.3, and 2.3.28.x before 2.3.28.1, when Dynamic Method Invocation is enabled, allow remote attackers to execute arbitrary code via vectors related to an ! (exclamation mark) operator to the REST Plugin.

例子是 Apache Struts 的,攻击者可以远程通过动态调用来实现攻击。这与 eval的问题是异曲同工的。

由于一个应用广泛的名为Express的WEB应用开发框架是基于node.js运行环境的,根据CNVD秘书处初步普查结果,受该漏洞影响的网站服务器有可能超过70万台:node-serialize 包的有问题,就推导出 Node.js 有问题,然后再说使用 Express 的这70万台服务器有问题,这似乎没有道理。

狭隘地说,这个漏洞只是类库 node-serialize 在使用 eval 这种 JavaScript 社区不推荐使用的函数造成的。这不是 Node.js 的漏洞,因为问题并不是出现在 Node.js 的源码中。

影响有多大呢?外刊君去看了这个项目在github上的star是20,在http://npmjs.org上可以看到有几个类库依赖它,但这些依赖它的类库每个月下载量甚至是个位数。因此影响超过70万台的说法完全是无中生有嘛。

往广里说,这其实是 JavaScript 自身的问题。JavaScript 提供了 eval 函数,对一段字符串形式的 JavaScript 代码进行求值。但是:

Don’t use eval needlessly! eval() is a dangerous function, which executes the code it’s passed with the privileges of the caller. If you run eval() with a string that could be affected by a malicious party, you may end up running malicious code on the user’s machine with the permissions of your webpage / extension. More importantly, third party code can see the scope in which eval() was invoked, which can lead to possible attacks in ways to which the similar Function is not susceptible.——https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/eval

其实在 JavaScript 中已经非常不倡导使用 eval 很久了。

总之,这不是Node.js自身的漏洞,更不是重大漏洞!

怎么修复这个漏洞?

针对 node-serialize,这个漏洞无法从源码上修复。这个库的目标是:

Serialize a object including it’s function into a JSON

将一个对象序列化成 JSON 字符串,包括这个对象上的函数。也就是在反序列化时,必须将字符串函数通过eval 或者 new Function() 转成函数。实现这个库的目标,必须使用这种不安全的方式,因此无论 node-serialize 怎么处理对传入的字符串进行检查,都会留下不安全的隐患。

因此,库的作者给出了两条建议:

  • 在封闭的内部环境中使用这个库,将它们与潜在的注入者完全隔离;比如只从后端服务器传递序列化的对象给前端服务器,并且使用 HTTPs 代替 HTTP;
  • 通过非对称加密(RSA)加密序列化数据,避免被篡改。

在外刊君看来,唯一修复或者避免这个漏洞的方法,就是不再使用 node-serialize 这个库,不再使用 eval 之类可以给注入者带来无限可能的 JavaScript 糟粕,尤其是在后端代码中。

外刊君想说

文前开头的三篇文章的作者,搞安全研究能不能长点心,别老是想搞个大新闻。这漏洞的影响范围真的是放卫星了。可以看看国外最开始提出这个漏洞的文章Exploiting Node.js deserialization bug for Remote Code Execution,国内的示例代码,截图都和这篇文章一样,翻译能不能注明出处?还可以看看原文下面的评论,读者 Lukas 已经指出原文的问题——标题党、过分夸大,搞不清楚问题在哪里。

eval 不能用,eval 不能用,eval 不能用,重要的事情说三遍。当然,new Function 也不能用,这是一个无限的坑。可以使用比如 ESLint 这样的工具来进行检查。

0
推荐阅读