作者:默白
预估稿费:200RMB(不服你也来投稿啊!)
投稿方式:发送邮件至linwei#360.cn,或登陆网页版在线投稿
前言
PouchDB < = 6.0.4 当中包含了一个任意代码注入向量,只要维持express-pouchdb的默认配置,就可以进行无身份验证的远程代码执行。如果你已经发现一个Node.js注入,那么你还会从这篇文章中了解到一些利用技巧。
去年的SANS假日黑客挑战赛之后,我发现我开始对Node.js及其生态系统感兴趣起来。特别是最近两年物联网的兴起,Microsoft和Azure这些大公司对其进行了大力的推广。Node已经成为一些安全问题的核心,比如说几个月之前的Chalker凭据泄露事件。撇开Node包管理器长达一年半的包装问题不谈,它的系统遵循着“小模块哲学”,不同的任务由不同的模块来完成,甚至是检查某值是否在某数组中这样的事情也会交给一个模块来执行。写这篇文章时安装包的下载次数已经超过了2400万次。
这种哲学导致了大型Node应用程序或框架对于树结构的极度依赖,如果没有自动化工具,对于大多数的项目开发组来说,维护将会变得十分困难。Node.js是很容易学习的,它有一个活跃的交流社区,并且积极的鼓励利用第三方的模块去实现应用程序的一些功能。尽管这对于MVP迭代非常有利,但这种做法非常的不明智,可能会给已经保证了最小攻击面的成熟应用带来更多的安全问题。Node的哲学是有用的,因为它使得任何拥有JavaScript web 开发能力的人都可以编写服务器端代码,使其成为一个有用的系统编程学习工具,以及新开发人员的进步跳板。
我在此请求所有的安全研究人员都能深入研究Node、libuv等各种类型的node模块,因为第三方模块太多了,往往未经审核就可以进入应用市场,被用户安装使用。其中有很多可能包含了一些非常简单的漏洞,这些应该及时报告给Node.js社区及时解决。Node安全项目的工作人员目前似乎正在处理第三方模块的所有漏洞报告,这将对应用程序安全会有很大帮助。
今年早些时候,我参与了有关 Node.js web 通用应用程序框架和数据存储话题的讨论。我花了一些时间研究PouchDB,这是一种"同步数据库",数据存储、处理方式受到了Apache CouchDB的启发,并且增加了一些自己的特性。在一次研究PouchDB的过程中,我发现了一个远程代码执行漏洞,并且可以在测试服务器上反弹回来一个Shell。于是我编写了PoC,并将其发送给了PouchDB的开发者。
关于PouchDB和express-pouchdb
PouchDB被称为“同步数据库”,但本质上是用JavaScript编写的复刻版CouchDB,将数据存储在本地设备以备联机或脱机复制。主要的应用案例应该是Cordova 或Electron支持的应用程序,其中会存在有限连接问题,所以开发者还需要一个本地数据存储。(编者按:PouchDB维护人员Nolan Lawson写道,因为发展中国家的通信水平参差不齐,所以这种用途还是必要的。)
为了复制CouchDB 功能,PouchDB 利用了 Express.js web 框架,以及一个 express-pouchdb插件作为HTTP 中间设备解决方案,便于顺利到达远程数据库,这很像是CouchDB 的 HTTP API。PouchDB用户可以将数据通过HTTP同步到使用此 API的服务器。
奇怪的是,虽然 PouchDB 支持授权和身份验证,但是在使用PouchDB或express-pouchdb教程及默认设置时,既不需要授权,也无需身份验证;pouchdb-auth似乎是一个单独的插件。运行PouchDB文档的示例程序会使得express-pouchdb服务器门户大开。我目前发现的RCE漏洞应该只是对有权限访问数据库的用户有用,但是考虑到默认设置并不安全,这种注入应该可以在默认 express-pouchdb服务器中顺利实现。
此漏洞主要与 _temp_view 端点,以及视图如何在 PouchDB 中内部工作有关。在Couch/Pouch中,视图和设计文件都是脚本文件,允许管理员进行自定义的数据转换。_Temp_view 端点运行一次性视图。CouchDB 表示从性能角度出发,这些不应该被应用于生产环境中。
用户使用 _temp_view 端点可以添加任意的 JavaScript 函数来处理提供的数据。一般来说,对于一个安全研究人员来说挖掘到服务端任意代码执行漏洞总是会让人激动的,所以我在发现这个端点之后立刻开始“找茬”。
PouchDB和_temp_view
现在让我们看看 PouchDB 是如何实现此视图基础构造的。我们已经在使用 JavaScript了,因此对于解释型编程语言JavaScript来说是很简单的:有一个很好的内置函数叫做eval,十多年来几乎每一位JavaScript开发者都被告知最好不要使用这一函数。碰巧的是,在express-pouchdb中 PouchDB用来运行视图的函数就是eval。假设用户通过npm下载安装pouchdb,该函数会在lib/index.js:6201中被触发。(注:较新版本的Node包管理器其实已经对于PouchDB的另一个依赖tough-cookie易受到Regexp DoS攻击发出警告。)
这个eval函数使用的是另一个node插件scope-eval,该插件通过Function.apply在一定范围内传送数据。本模块代码全部以CoffeeScript形式呈现。
在此范围之外没有找到该函数的沙箱,那么,是否可以全局访问呢?
任意代码执行:从信息泄露到拒绝服务到反弹shell
我最开始试图注入一个Node请求,但是没有成功,因为在注入代码范围内不存在请求。尽管在范围内查看所有数据几乎是不可能的,但是幸好我们也不需要查看所有信息,我只需要一个注入 console.log。我的测试服务器包含了对于 Node.process的引用,这是一个含有内部过程状态的全局引用,还包含了对于许多函数的引用。
在这一阶段,注入漏洞使得从this.process.env窃取信息变得十分容易,其中可能会泄露AWS密钥、CSRF密钥等;注入this.process.exit()还会出现一个可注入的、一次性拒绝服务漏洞,或者对使用同一用户身份运行PID的其他租户进程造成损害。
但是代码执行又是什么情况呢?正如上文所述,我们没有办法访问请求,一个Node.js 开发人员使用的主函数当中会包含自己的Node代码。在这种情况下,这都不是问题,只是限制我们加载Node JavaScript模块。通过Node加载模块,我们在注入点会有一个所有模块的引用树,这些模块都是由this.process.mainModule树中的Express服务器运行的JS主脚本加载的,以及在本地Node绑定的this.process.binding加载。
在渗透测试中,我认为最好首先在 process.env 附近挖掘出密钥,然后检查其他可能含有process.mainModule内存配置数据的加载模块。树上的每片”叶子“都包含了模块导出的函数引用;我最初编写了一个小导航函数,用来查找含有child_process的函数,但是没有找到。
注:关于“你有RCE,现在能做什么?”这一话题,将会在我之后要写的"Node.js/Express.js应用程序通用漏洞利用代码"一文中进行更加深入的探讨。文章大致已经完成了,但我还想在发表之前得到一些更清楚的PoC代码。
在Node本机,我们有bindings,这是Node内部库使用的本机C-to-JS 绑定。获得一个shell最常用的两个绑定就是Process和spawn_sync,两个都由Node的内部 child_process 模块所使用的。为了PoC目标,同时限制有效荷载大小,我选择使用 spawn_sync,这会产生系统中,这非常显而易见,因为你创建了一个新进程会导致监听器受阻,从而终止服务。为了证明此漏洞,我们打算选择隐身,以便更快捷的达成目标,而不是一味重复 child_process模块中一半的内容。
实现远程代码执行的payload
this.process.binding(‘spawn_sync’)代码是一个C绑定;使用这一代码时会存在一些不安全因素,也可能会引发一些底层漏洞。(如果你是一个底层逆向工程师,那我确认你一定可以在这里挖掘出一些我没有发现的问题)我们将执行的方法是 SyncProcessRunner::Spawn ,这会带来一些参数,并生成一个相关进程。
知道了这些,我们就可以在用户使用类似有效荷载运行的Node.js进程的同时运行任意二进制文件。
我正在为此编写一个Metasploit 模块。此exploit的伪代码和存在漏洞的应用程序源码在GitLab都可以找到;等到修补程序获得现有用户群的认可,我就会发布我的结果以及Metasploit 模块。
结论,修补程序以及解决方案
每个新框架和数据库出现时,我们都从来不会信任用户输入。PouchDB在2012年意识到存在不同的eval问题,然后关闭了该问题,尽管在文本中没有将其作为一个问题提出。这个RCE exploit在PouchDB 5.4.4和express-pouchdb 1.0.6中测试成功,PouchDB 5.4.5也存在同样的问题。
在与PouchDB维护人员和Node Security的工作人员取得联系之后,我们针对6.0.5中的注入推出了一个修补程序。用户仍应实施纵深防御,并且努力限制某些来自express-pouchdb的URI。一个常见的解决方法就是在express-pouchdb中设置 minimumForPouchDB标志。在撰写本文时这不会违反任何规定。PouchDB用户首先应该确认身份验证,其次是授权,并且最好是在PouchDB反向代理之前。不要允许 _temp_view之类的路径,这会使得服务器上存在远程代码执行。再次强调,一个开发者从来不会信任任何的用户输入。
披露时间轴
调查开源漏洞时,我遵循了谷歌Zero项目的披露时间线和进程。我在发现漏洞的第一时间与团队取得了联系,同时发送了一个有效的PoC,以及一个用Python编写的可反弹shell的exploit。
撰写本文时,PouchDB还没有回复自己的安全报告进程,所以我联系了信任的核心维护人员,希望能够得到开发者的公共配置文件。
2016.07.29:发现漏洞,用PoC上报给 @nolanlawson。
2016.07.30:通过 Twitter 联系Nolan,他说邮件被自动划分为了垃圾邮件,但是还没有得到证实。
2016.07.31:通过电子邮件将漏洞信息发送给 @calvinmetcalf 。
2016.08.01:Calvin没有回应;发送邮件和Twitter给Dale Harvey,所有的核心维护人员都积极看待这一问题。
2016.08.06:联系Nolan Lawson,他确认收到了漏洞报告。
2016.08.18:确认正在编写修补程序,将建议以及披露时间轴发送给Node安全项目工作人员,CVE仍旧悬而未决。
2016.09.18:发布针对PouchDB 6.0.5的修补程序。
2016.09.19:NSP 证实正在准备发布安全警告。
2016.10.17:NSP 发布安全警告。
2016.10.27:商定90天内披露时间轴。
发表评论
您还未登录,请先登录。
登录