11

关不住的“邪恶”代码,mongo-express远程代码执行漏洞分析

 4 years ago
source link: https://x3fwy.bitcron.com/post/mongo-express-rce-explained?
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.
neoserver,ios ssh client

关不住的“邪恶”代码,mongo-express远程代码执行漏洞分析

2020-01-07

  |  

963

近日,奇安信CERT监测到mongo-express远程代码执行漏洞,漏洞编号为CVE-2019-10758。凭借着对Node.js漏洞天生好奇(也是因为我好久没有写类似文章了),便深入跟进了这个漏洞。漏洞原理虽然简单但还是比较有意思的。那么闲话少说,慢慢跟我来看吧。

先介绍下mongo-express是个什么工具,简单的说,mongo-express是一个MongoDB的客户端。和传统我们经常使用的数据库客户端不同的是,mongo-express是一套基于Web的客户端。它使用Node.js+express(一个Node.js服务器框架)开发,故名mongo-express。很自然的,这个客户端能对建立连接的MongoDB数据库执行任意操作。

此漏洞是因为代码中使用的vm去执行不安全的用户可控的代码导致的远程代码执行。至此我把这个漏洞的概况先抛出来,我们继续看。

阅读官方GitHub的安全公告,我们发现漏洞影响0.54.0以下的所有版本,打开DockerHub搜索mongo-express,在官方镜像页面中的tag中可随意选择0.54.0以下的版本进行环境搭建。我们以0.49为例。由于此漏洞环境还需要MongoDB数据库,我们可以通过执行以下docker命令进行快速搭建:

  • 搭建MongoDB数据库
docker run --name mymongo -d mongo:3.2
  • 搭建包含漏洞的mongo-express并且连接到上面的MongoDB数据库:
docker run -it --rm -p 8081:8081 --link mymongo:mongo mongo-express:0.49

当看到以下输出即说明mongo-express成功运行并成功连接MongoDB数据库。

2020-01-06-16-00-24.png

根据上面的输出我们可以发现,mongo-express使用了Basic认证,并且默认账号密码为admin:pass,所以此漏洞虽然是高权限才可以利用,但因为默认高权限账号密码的存在,如果不修改默认账号密码,极易受到攻击。

环境搭建完毕后我们可以访问http://localhost:8081/去看看mongo-express长什么样子了。

2020-01-06-16-05-16.png

漏洞问题出在lib/bson.js中的toBSON()函数中。熟悉MongoDB的同学应该知道,MongoDB存储和查询数据的格式是BSON,它长得像JSON但并不是JSON,数据类型和JSON是有所不同的。众所周知,JSON的数据类型包括string、number、object、array、boolean和null,而BSON的基础数据类型包括byte、int32、int64、uint64、double、decimal128。其中byte类型又可以根据不同的定义派生出更多的类型。若想详细了解BSON相关知识可查看BSON Spec,或者参考gyyyy的文章:《浅析SQL和NoSQL注入(下)》中的“关于BSON”部分。这篇文章就不再赘述了。我们只需要知道BSON和JSON在数据类型上有很大区别就好。

mongo-express中的所有和BSON相关的操作,如新建一个文档(类似其他数据库的插入操作)都需要通过toBSON()函数。如我使用mongo-express新建如下文档:

2020-01-06-17-04-01.png

通过Chrome调试工具,我们可以发现,在我新建一个文档的时候,前端把数据处理成表单的形式提交给后端,如下图:

2020-01-06-17-05-12.png

lib/bson.js中的toBSON()函数中断点,命中后即可看到如下字符串传入了后端:

2020-01-06-17-16-36.png

我们先来分析一下这段代码,可以发现在toBSON()函数上面的getSandbox()定义了MongoDB相关的数据类型,然后在下面的toBSON()函数中对传入的字符串进行了转换,最终变成BSON类型的文档。

代码作者在注释中也提到了,因为JSON.parse不支持BSON类型,因此写了toBSON()函数去转换传入的字符串为BSON类型。我们可以显而易见的看到eval()函数的调用。eval is evil,eval()函数在JavaScript中本就是一个危险函数的存在,在前端中eval()可以造成XSS漏洞,而随着JavaScript被用到了后端,如果eval()中的内容可控,就可以直接造成代码执行了。那么我们是不是就可以直接构造一个类似其他Node.js代码执行漏洞的代码执行PoC即可了?没那么简单。

细心的同学想必已经发现了,toBSON()部分的代码使用了vm模块执行,它是Node.js默认就提供的一个内建模块,vm模块提供了一系列API用于在V8虚拟机环境中编译和运行代码。也就是说通过vm执行的JavaScript代码会运行在一个沙盒中。代码作者其实并没有忽略安全问题。那么在vm中执行的代码就真的安全了吗?我们来简化源码做一个实验:

function evalDirect(string) {
  eval(string);
}

function evalUseVm(string) {
  const vm = require('vm');
  const sandbox = {};
  vm.runInNewContext('eval((' + string + '));', sandbox);
}

evalDirect('require("child_process").execSync("open /System/Applications/Calculator.app")');
evalUseVm('require("child_process").execSync("open /System/Applications/Calculator.app")');

运行这段代码,在调用evalDirect()函数时弹出了计算器,而在执行evalUseVm()函数时抛出如下错误:

2020-01-07-01-32-39.png

报错中说,require未定义。那么只要想办法找到require就能继续构造PoC了。在JavaScript中,创建一个对象,即通过这个对象的原型对象的构造函数实例化这个对象,因此,原型对象是新对象的“模版”。那么假设我们使用的vm的沙箱就是一个当前进程上下文创建的对象,那么我们能不能通过链式调用vm的沙箱的构造函数来查找到当前上下文并且拿到require呢?vm在GitHub中的一个issue:Sandbox can be broken已经给出了答案,这个问题貌似永远也不会得到修复了。

通过this.constructor.constructor("return process")即可拿到当前上下文,我们可以看到其中包含了Node.js的全部全局变量:

2020-01-07-01-16-19.png

这样我们就可以轻松拿到require构造PoC了。

从GitHub补丁diff上看,修复方案是删掉了全部使用vm的代码,而将BSON的处理转为通过mongodb-query-parsermongodb-extended-json这两个库配合实现:

2020-01-07-01-20-00.png

建议升级到mongo-express 0.54.0及以上版本,并且修改默认密码。mongo-express虽然是一个Web项目,但只建议内网使用,或使用其他客户端代替。

通过近期对Node.js漏洞代码的研究,隐约发现Node.js漏洞的一些规律,如eval()出现在代码中,require("child_process")出现在代码中,只要他们可以被控制 ,甚至如CVE-2019-7609的kibana RCE漏洞仅仅是可以控制环境变量,都可能造成较严重的后果。今后再做Node.js相关项目的代码审计、漏洞分析或漏洞挖掘时,我们应充分利用JavaScript的语言特性,或许能总结出更多规律,让思路更清晰。

  1. http://bsonspec.org/spec.html
  2. https://github.com/gf3/sandbox/issues/50
  3. https://juejin.im/post/5afe3c91518825673954f718
  4. https://github.com/mongo-express/mongo-express/pull/522
  5. https://odino.org/eval-no-more-understanding-vm-vm2-nodejs/
  6. https://github.com/gyyyy/footprint/blob/master/articles/2018/sql-vs-nosql-injection-02.md
  7. https://github.com/mongo-express/mongo-express/security/advisories/GHSA-h47j-hc6x-h3qq

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK