MongoDB操作&&注入漏洞&&未授权访问漏洞
- 2019 年 10 月 8 日
- 笔记
注入不止有传统的SQL数据库,NoSQL型数据库也一样存在注入漏洞,在比赛中跟传统的注入相比也算新题型,不少同学可能还不太了解,本文向大家科普MongoDB数据库的常见操作以及攻击的方法——NoSQL注入和未授权访问
MongoDB简介
在 MongoDB
中,没有 表、列
的概念,取而代之的是 集合(collection)、文档(document)
- 库(DB):
- 数据库,包含多个集合
- 集合(Collection):
- 一组文档
- 文档(Document):
- 键值对的一个有序集,即有序的哈希表
MongoDB
内置 JavaScript
解释器,它的文档是 JS
中的对象( {...}
),就是那种没成员函数的对象
MongoDB
默认运行于 27017
端口
MongoDB
以 BSON
格式保存数据,即 BinaryJson
简单操作
CRUD操作
文档插入后会自动添加一个 _id
属性,为唯一标识符 {"_id":ObjectId("56064886ade2f21f36b03134")}
Create
创建数据库
> use [newdbname] //不存在则会创建
创建collections
db.createCollection()
创建集合
集合的 insert
方法,插入单个文档
db.collection.insert({ key1: value1, ley2: value2 })
Read
集合的 find
方法,第一个参数是用来确定返回的文档,第二个参数确定返回的键值的过滤条件
db.collection.find() // 返回全部文档 db.collection.find({"age": 20}) // 按条件查询 db.collection.find({ "age": 20, "name": "iv4n" }) // condition1 && condition2
查询条件
$lt |
$lte |
$gt |
$gte |
$ne |
---|---|---|---|---|
< |
<= |
> |
>= |
!= |
db.collection.find({ "age": {"$gt": 18, "$lt": 30} }) // 返回年龄18< <30
$in |
$nin |
$or |
$not |
$size |
---|---|---|---|---|
指定列表,成员可为不同类型 |
不在列表中 |
条件逻辑或 |
元操作符,可用于其余任何条件 |
大小 |
db.collection.find({ "number": {"$in": [1, 2, 3]} }) db.collection.find({ "$or": [{key1: value1}, {key2: value2}] }) db.collection.find({ "$not": {"age": 20} }) db.collection.find({ "name": {"$size": 4} })
null值
db.collection.find({key1: null}) // 返回所有无key1键的文档
RegExp
db.collection.find({"name": {"$regex": /^[a-z]{0,4}$/i}}) // 可加入正则flag位,如i忽略大小写
$slice,返回键中数组切片
db.collection.find({}, {"comments": {"$slice": 10}}) // 返回前十切片 db.collection.find({}, {"comments": {"$slice": 10}}) // 返回后十切片 db.collection.find({}, {"comments": {"$slice": [23, 10]}}) // 返回24~33切片,神奇的左开右闭
$where,危险语句,可执行任意 JS
函数
db.collection.find({"$where": function(){ for (var i in this){ for (var j in this){ if (i!=j && this[i]==this[j]) return true; } } return false; }})
本质其实就是传入一个返回值为 bool
的匿名函数,对文档进行选择
Update
修改器
$inc,原子操作,并发安全
db.collection.update({key: value}, {"$inc": {a: 1}}) //find操作后键值a会自增1
$set,存在则修改,不存在则创建,就像sql语句的 REPLACE
一样
db.collection.update({"_id": "xxxxxx"}, {"$set": {key: value}})
$unset,set的逆操作
$push,修改文档数组,因为 JavaScript
的数组增删元素就是用 push
& pop
$each,批量修改数组
db.collection.update({}, {"$update": {key: {"$each": [val1, val2, val3]}}})
Delete
db.collection.remove() //删所有文档 db.collection.remove({key: value}) //指定查询条件 db.collection.drop() //删集合 db.dropDatabase() 删库
NoSQL注入
我将 MongoDB
装在我的 Debian9
虚拟机上,创建了以下内容:
> use sqli switched to db sqli > db sqli > db.createCollection("users") { "ok" : 1 } > db.users.insert({"uname": "admin", "passwd": "admin"}) WriteResult({ "nInserted" : 1 }) > db.users.insert({"uname": "iv4n", "passwd": "iv4n"}) WriteResult({ "nInserted" : 1 } > db.users.find() { "_id" : ObjectId("5ba3412314139eac63f891f7"), "uname" : "admin", "passwd" : "admin" } { "_id" : ObjectId("5ba3424114139eac63f891f8"), "uname" : "iv4n", "passwd" : "iv4n" }
以下是PHP代码,这里是PHP 7.x,和5.x的mongoDB库使用有差异:
<?php // init mongoDB engine $server = new MongoDBDriverManager("mongodb://localhost:27017"); $uname = $_POST["uname"]; $passwd = $_POST["passwd"]; $filter = ["uname" => $uname, "passwd" => $passwd, ]; $option = [ "projection" => ["_id" => 0], "sort" => ["_id"=> -1], ]; $query = new MongoDBDriverQuery($filter, $option); $cursor = $server->executeQuery("sqli.users", $query); foreach ($cursor as $doc){ if ($doc->passwd == $passwd){ echo "Login Successfully!<br>"; echo "username is: ".$doc->uname."<br>"; echo "password is: ".$doc->passwd."<br>"; } else { echo "Invalid user and pass"; } } ?>
index页面为一个post方法登录

这里的 filter
为查询条件,语句为 db.users.find({"uname":$uname,"passwd":$passwd})
正常登录,用户名密码错误无回显

$ne
选择器注入,返回所有不等于的 document
,传入数据库的语句实际是 db.users.find({"uname":{"$ne":"a"},"passwd":{"$ne":"a"}})
:

可以看到,返回了数据库的全部信息
$lt/$gt
注入:
前面说过,MongoDB内置的是Javascript的解释器,所以它在字符串的大小判断也遵循JS的逻辑
JS的字符串大小判断逻辑:按字节从左比较ascii码,假如相等则比较下一字节,不等则返回当前位的比较结果

所以我们可以利用大小操作符来注入:

正则注入:
$regex
,传入数据库的语句实际为 db.users.find({"uname":{"$regex":"^a"},"passwd":{"$ne":"a"}})

可以看到,返回了以a开头的用户信息,实际上它和SQL的正则盲注是一样的道理
上述的注入例子还相对更安全,PHP5版本的mongoDB库是允许代入查询字符串的,那样会导致更多的注入漏洞(就像SQL注入的闭合、注入,相信大家都比我懂,就不献丑了)
未授权访问
MongoDB最初安装部署后是不会添加auth选项的,一般的初始化步骤是:
- 不开启auth选项时连接数据库,添加管理员账户
- 开启auth,利用管理员账号登录连接,添加数据库账户
但是很多开发者并不知道这些Tips,没有开启auth选项,且数据库监听了公网,就导致了MongoDB的未授权访问
其实MongoDB的未授权访问和Redis数据库是差不多的,这里我们利用一个工具NoSQLMap来进行数据库信息枚举,有SQLMap那么也就有针对NoSQL数据库的NoSQLMap,它可以注入以及利用未授权访问漏洞

我将数据库不开启auth启动,然后NoSQLMap的1选项设置想关信息
接着点击2选项,提示存在未授权访问漏洞

点击枚举数据库信息

这个工具目前来说还不是很完善,动不动就会直接exception退出,和SQLMap相比差距还是很大。更多的功能等待大家去探索