XYCTF2024 Web方向题解
最后更新时间:
文章总字数:
预计阅读时间:
页面浏览: 加载中...
仅作wp记录用,不宣泄任何情绪,仅作知识分享
牢牢记住,逝者为大
想你了牢大
题目源码如下:
1 | |
可以看到这里好多限制:
- 长度限制小于等于13
- 命令执行的几个函数都被过滤了
- 对于val,也就是我们传入的值进行了限制,不能够是上面的
bin|mv|cp|ls|\||f|a|l|\?|\*|\>/ - eval前后都有脏数据
这里其实过滤倒是其次,这边要考虑的更重要的是两个方面:长度限制和脏数据
我这边的思路经历了以下的流程:
- 数组绕过strlen
但是发现不太行,因为我们知道字符串拼接之后数组会直接变成Array:
所以肯定是不行的,只能够老老实实用13个字符限制做这个题
- 执行命令
这里我先把Kobe的限制去掉了,变成:
1 | |
这样先测试命令执行
可以看到eval里面前面是一个#,也就是一个注释符,它能够将后面的内容都注释掉。
所以我们就算写好了cmd也没用,只能够注释掉
但是#和//是一样的,都是单行注释符,所以我们只需要换行即可,加个%0a就可以绕过前面的限制
后半段呢,我打算是用__HALT_COMPILER();直接终止编译成字符串的,乍看之下它可行,但是实际上这个函数本身就已经超过了13个字符的限制,更不要谈getshell
在这里我就卡住了一会,因为这个HALT_COMPILER我的印象比较深刻
然后我想着想着突然茅塞顿开,前面的#注释能不能能用到后面去呢?
然后我就测试了一下:
1 | |
当然这个#要用urlencode成%23
答案是可以的:
本地测试dir有结果
所以前后都确定了:
1 | |
接下来就是在11个字符的限制下打一个shell
想起p神的最短webshell:
你猜怎么着,刚好11个长度
payload那就呼之欲出了
1 | |
回到题目,还对>做出了限制
所以我这里打算打一个反弹shell,刚刚好题目是出网的:
1 | |
warm up
第一层就是简单的md5
1 | |
用下面这个链接全秒了:
反序列化-md5和sha1绕过_md5反序列化-CSDN博客
弱等不想想,直接用数组:
1 | |
下面双md5,在上面的文章随便挑一个就行了
1 | |
最后这个,我们要先看看这个XYCTF_550102591
md5之后是什么东西
其实还是个0e开头的md5,那就简单了
利用extract将XYCTF覆盖成和XY一致的变量:
1 | |
到达第二关:
LLeeevvveeelll222.php
1 | |
preg_match,用数组绕:
1 | |
下面这个preg_replace有点特别,估计是利用\e来执行命令
1 | |
抄一下:
1 | |
出了:
1 | |
Make File
直接命令执行
1 | |
ezmd5
两张图片如下:
ezhttp
登录框,f12找到提示藏在某个地方
其实这个时候可以dirsearch开搜
但是这边可以猜一猜,猜到了robots.txt:
访问这个txt
1 | |
然后拿hackbar操一下:
ezpop
看到有个throw Exception就可以想到利用gc回收机制来绕过
链子终点BBB->__get
通过AAA->__toString访问BBB类的$p来进入BBB
通过CCC->destruct进入toString
链子:
1 | |
但是这里要简单的测一下中间这个:
1 | |
弄出来是个什么东西
这里出了个套娃,简单地看一下
1 | |
其实就是相当于用call_user_func调用函数A,通过A(d)实现rce
那么要怎么挑选呢?
首先可以根据$b是一个数组可以确定$a是一个获取到数组的键或者值的函数,无参rce里的几个函数可以利用得到
我这边就挑选了array_rand,不过他是随机的,但是数组长度只有1,所以array_rand就是确定的
通过array_rand能够获取到键名,而且它的键名是一个函数名,我这边选了hex2bin
利用hex2bin来调用system:
1 | |
利用system获取到flag即可,exp如下:
1 | |
ez?make
反引号命令执行反弹shell:
可以看到源码过滤了这些
我是一个复读机
其实这个题一句话就可以带过:
弱密码爆破+requests.values.xx传参绕ssti
弱密码选了个asdqwe,用fuzzdict-master可以爆的出来(某集团常用弱口令字典)
输入框输入中文以后出的大括号才是ssti用的大括号
🤔,总之就是中文这块比较难想。还是队友帮忙测出来的
找一下os_warp_close:
1 | |
payload很简单:
1 | |
加两个中文就有两个大括号,ssti
用admin asdqwe登录即可,ssti完了简单扒个源码
1 | |
ezSerialize
第一层:
1 | |
来到/fpclosefpclosefpcloseffflllaaaggg.php(第二层)
终点类:
C::__call
链子:
1 | |
不知道为什么打不上去
绕wakeup的,版本估计是7.3了,,:
1 | |
不绕的:
1 | |
你妈的,为什么打不了
我操,原来要把name置成null:
1 | |
第三层,链子自己看吧,主要利用到一个stdClass(),可以看一下这位师傅的博客php反序列化小记(1),没什么好说的,链子都是能看得出来的,具体原理大概是如果题目当中没有能够反序列化获取属性的对象,那么可以用stdClass类,这是一个php的内置类:
1 | |
exp:
1 | |
连连看到底是连连什么看
源码大概如下,记不清了:
1 | |
原理都在_rev1ve神的博客文章上有
因为base64只能接受[A-Za-z0-9+/=],所以其他的不可见字符一旦进入到decode流程就会被忽略
所以很简单,套就是了,一直decode直至只剩下XYCTF
找到一个php_filter_chain_generator.py,挺高级的利用了,它支持任意的payload写入:
1 | |
exp:
1 | |
反正多套几层base64肯定能出,实在不行就从XYCTF一步步套上去
ezrce
原理和CTFShow极限rce一致,探姬师傅有个脚本,一把梭了…
并不(
记得加bash的符号就可以了
payload:
1 | |
去base64解码
pharme
测试可得允许上传的后缀为jpg
class.php是一个简单的无参RCE,利用__HALT_COMPILER();终止编译即可
exp:
1 | |
1 | |
要绕过phar头,用下面的脚本:
1 | |
触发phar:
1 | |
我好像用的是第一个触发的
先传ezxyy2.jpg检测请求头,找到数组的最后一个元素
然后再传ezxyy.jpg打rce
ezClass
水题
1 | |
利用php的Error类带出信息即可:
1 | |
1 | |
εZ?¿м@Kε¿?
打开f12发现了hint.php,发现匹配规则如下:
1 | |
这个正则的意思是匹配除了中括号以内的字符
也就是白名单只有这些符号
而且测试发现payload长度<8
也就是最多只能够用7的长度来执行
其实这里可以爆破()
生成一个11^7+11^6+11^5+11^4+11^3+11^2+11的字典即可((
然后本地测试()
测试发现:makefile里的$<就是/flag,但是没有直接读取的权限
这里结合一下linux的东西 $()也能够执行命令,<能够将东西输入到命令里,直接读取不行就用这样的方式读取:
1 | |
将/flag重新定向到一个bash -i里
外部相当于bash -c
其实就相当于bash -c "bash -i /flag"
替换一下/flag就是$<
payload:
1 | |
login
如果注意到这个题有个register.php的话就能够解决了
但是wsrx这个不给扫= =
/register.php注册一个账号,然后登录,发现啥也没有
下意识打开F12,发现了cookie这里有个Rememberme
下意识以为是shiro,然后高高兴兴地去拿shiro的工具打,发现根本不行
man!
然后才仔细看看它的cookie:
1 | |
一坨AAAA,其实这里是pick的特征
看看base64
能直接解码显然不对劲了,还有一个app
这里很明显是python的服务器,那这个cookie经过base64decode的是啥呢?
搜一下就知道
百度:ctf RememberMe
发现是pickle的特征,这下就合理多了
那简单,打pick反序列化就对了,发现还有waf的,直接打不太行
发现过滤的是关键字,比如import,system等
但是os没有被过滤,这个时候直接用os.popen就好了:
1 | |
反弹shell,源码如下:
1 | |
give me flag
哈希长度拓展攻击。
讲下原理吧。
md5的算法流程
对于一个字符串,其二进制长度=字符串长度*2*4
比如对于64个a,其二进制长度就会是64*2*4=512
对于MD5算法来说,要对原数据进行分块处理,以512个二进制数据为一块,直到最后的数据块,分为以下两种情况:
- 长度<=448时,则会填充padding(无意义数据)使其长度达到448,再添加原始明文数据的二进制长度信息直到512位
- 长度>448且<512时,填充padding到下一块的448位,再添加二进制长度信息至512位
将数据分块后就可以进行md5运算了
初始向量是确定的:
1 | |
经过运算即可获得MD5值
而这个运算,对于一个确定的md5,可以通过一定的规则将md5转成secret+xxx的md5值
具体的过程可以看这篇文章:浅谈HASH长度拓展攻击 - Yunen的博客 - 博客园 (cnblogs.com),膜拜师傅Orz
哈希长度拓展攻击一般都推荐hashpump
但是我觉得hexpand更加好用:
1 | |
1 | |
这个43怎么来的呢?因为web的动态flag格式是:
1 | |
找个其他靶机看看flag格式也行,前缀flagname是XYCTF,uuid.uuid4()的长度是36,加起来就是43了
由于这里题目给的是$flag.$value.$time,其中$time=time(),我们需要对时间戳进行预测提交,又由于提交时靶机有延迟,所以这里要测以下请求的时间差
$value.$time是我们要追加的信息,但是$time已经写好了,所以我们生成的md5时追加的信息要删掉
经过我测试大概提交时间和实际时间有三秒钟左右的延迟(计算两次请求的时间差),写出python脚本如下:
1 | |
ezLFI
源码:
1 | |
filterchain秒杀:
1 | |
Baby_Unserialize(赛后复现)
这里真的测了很久…
黑盒java,出网,urldns链能打
想用cc链,但是根本打不得一点,直接返回HOW DARE YOU
这就难办了,也不知道后台有啥gadget,只能盲测一波。这里想到了[用urldns链探测gadget][https://mp.weixin.qq.com/s/KncxkSIZ7HVXZ0iNAX8xPA]
🤔,改一下测gadget:
1 | |
。。。。。。。。

还有很多,就不放出来了
逆天,啥gadget都没
这里就在想它到底是怎么过滤的了。
测得关键字commons.collections被干掉了,🤔
这里还在想他是不是用serialkiller或者是其他的方式,比如常见的resolveClass给ban掉了,然后用一种很新的day给绕过
百思不得其解,只能去找找绕waf的方式
找是找到了,回忆飘如雪师傅的两个方式:
- 加大量脏数据
- 延时分块传输
都没解,这么为难我一个小萌新干啥捏。。
遂放弃。
但其实是自己想太多了。。
还有一种可能就是只检测了解码后的String是否含有commons.collections,毕竟如果直接重写resolveClass就是无解的命题
只需要绕commons.collections即可:
cc6:
1 | |
工具类:
1 | |
1 | |
payload url编码一下打过去就可以了
1 | |
shell弹过来了,flag is in env