前言
NoSQL (Not Only SQL),非关系型数据库,适用于超大规模数据的存储,目前作为分布式云平台和 Web 应用程序的后端数据库越来越受欢迎。NoSQL 数据存储不像关系数据库那样将数据存储在表中,而是存储使用更适合于特定目的的其他数据模型,如文档、图表、对象等。
NoSQL数据库不使用SQL通用查询语言。NoSQL查询语法是由应用程序的编程语言决定的:PHP、JavaScript、Python、Java 等。因此攻击者不仅可以在数据库中执行命令,还可以在应用程序本身中执行命令。
MongoDB是目前最流行的NoSQL 数据库产品之一,它使用类似于 JSON(JavaScript Object Notation)的语法将数据存储为文档,还允许开发人员仅使用 JavaScript构建全栈应用程序。下面我们使用MongoDB查询来演示NoSQL注入攻击。
MongoDB语法和操作符
MongoDB语法和操作符可参考MongoDB 教程。条件操作符有:
- (>) 大于 - $gt
- (<) 小于 - $lt
- (>=) 大于等于 - $gte
- (<= ) 小于等于 - $lte
- ( != ) 不等于 - $ne
- ( = ) 等于 - $eq
可能存在注入的位置
- 认证表格
- 过滤或搜索表单
- 标题和cookie
注入手法
报错注入
通过输入一些特殊的NoSQL字符,查看服务器是否返回错误,以此查看可能泄露的信息。报错信息可能暴露当前使用的是NoSQL数据库,或者类似500的错误。
1 | ' " \ / $ [ ] . > ; { } ( ) |
测试方法:
- 将特殊字符插入每个参数中观察是否发生错误
- 用这些特殊字符或NoSQL关键字(如 $ne、$eq、$where、$or 等)替换已发布的 JSON 内容中的元素,观察是否有错误。
- 发送附加对象以及有效的 JSON。如
{"user": "nullsweep"}
可以改为{"user": ["nullsweep", "foo"]}
或{"$or": [{"user": "foo"}, {"user": "realuser"}]}
其中一些字符或短语还可能触发其他注入漏洞(JS 注入、SQL 注入、shell 注入等),因此可能需要进一步测试以确保它是 NoSQL 后端。
布尔型盲注
通过布尔表达式(true或false结果、0或1等)观察页面响应情况来进行注入。常见语法:
1 | {"$ne": -1} |
可能需要尝试附加某些字符以正确终止查询:
1 | // |
时间盲注
注入sleep函数让页面产生延时,以此判断注入语句是否执行。
1 | {"$where": "sleep(100)"} |
注入示例
PHP MongoDB注入
PHP语言有一个特性:允许用户通过将URL参数更改为带数组括号的参数来将查询字符串数据类型更改为数组。
1 | http://test.com/page?parameter=value //normal URL |
攻击者可以利用这个特性,尝试在字段值中注入MongoDB运算符,如$eq
(等于)、$ne
(不等于)或$gt
(大于)来达到永真或永假的条件进行查询。下面是一个基础的php数据库查询方法,参数值来自表单post参数:
1 | $query = array("user" => $_POST["username"], "password" => $_POST["password"]); |
如果获取的参数直接用于数据库查询以检查登录凭据,通过注入操作符参数值作为数组处理:
1 | ?username[$ne]=1&password[$ne]=1 |
后端php会将其转换为:
1 | array("username" => array("$ne" => 1), "password" => array("$ne" => 1)); |
这将找到用户名和密码不等于1的所有用户,这很可能是永真查询,因此可能导致攻击者绕过身份验证。
MongoDB JavaScript注入
MongoDB API通常需要BSON(二进制 JSON)数据,但允许使用一些 JSON 和未序列化的 JavaScript 表达式。
阅读Mongodb文档可知,MongoDB允许在服务器上执行JavaScript的$where
和mapReduce
操作符。
如下是检索用户名信息的示例代码:
1 | let username = req.query.username; |
用于检索的用户名字符串直接从请求中获取,没有进行任何过滤。假如我们注入' || 'a'=='a
,则后端查询结构则变为了:
1 | $where: `this.username == '' || 'a'=='a'` |
查询结果总为真,实现了注入攻击。
假设服务器存在下面的查询逻辑,用户data数据未经过滤
1 | db.collection.find( { $where: function() { |
攻击者可能会向$userData
参数注入如'a'; sleep(5000)
的字符串。服务器执行的查询将是:
1 | db.collection.find( { $where: function() { |
这样将导致时间盲注。
处于安全考虑,可以禁用JavaScript在服务器端的执行:
mongod
、mongos(MongoDB 4.4开始)
实例在命令行模式通过增加--noscripting
参数或在配置文件中设置security.javascriptEnabled
为 false可禁止在服务器运行javascript。
测试工具
- https://github.com/Charlie-belmer/nosqli
- https://github.com/codingo/NoSQLMap
- https://github.com/an0nlk/Nosql-MongoDB-injection-username-password-enumeration
- hackthebox练习靶场:Mango
参考
- https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/NoSQL%20Injection
- https://www.netsparker.com/blog/web-security/what-is-nosql-injection/
- https://owasp.org/www-project-web-security-testing-guide/latest/4-Web_Application_Security_Testing/07-Input_Validation_Testing/05.6-Testing_for_NoSQL_Injection
- https://nullsweep.com/a-nosql-injection-primer-with-mongo/
- https://blog.csdn.net/weixin_45527786/article/details/113805654
- https://nullsweep.com/nosql-injection-cheatsheet/
- Post title:NoSQL Injection
- Post author:ssooking
- Create time:2020-07-17 18:02:00
- Post link:https://ssooking.github.io/2020/07/nosql-injection/
- Copyright Notice:All articles in this blog are licensed under BY-NC-SA unless stating additionally.