XCTF高校挑战赛与zzzcms 2.1.4最新版前台RCE
这次鸽了快两年的XCTF高校挑战赛和TCTF冲了,于是一边当客服一边打比赛(雾
最后小伙伴们AK夺冠,tql!!
zzzcms 0day
国内出CMS题和zzzcms过不去了是吧,天天出前台RCE,下次我也整一个(手动滑稽)……
这次是最新版,我们先来看看历史漏洞的修复情况:
1.7.5/2.1.0前台RCE
更多绕过过滤的变种就不看了,这两个洞需要控制 /search
的keys参数渲染模板,在最新版中参数需要通过一个 safe_key
函数:
1 | // 安全过滤字符串,仅仅保留 [汉字、数字、字母及=<>,_/],最长255字符 |
正如注释说的,只保留了部分字符且不包含 {
,所以直接注入模板参数的方法都失效了。
但是根据writeup,eval
函数触发点之前的过滤依然通过 danger_key
,并且假设我们能通过某种方式绕过 safe_key
,反引号依然能够执行系统命令(需要绕过黑名单)。
本地测试可以通过在 inc/zzz_template.php
24行 parserIfLabel
之前手动修改 $zcontent
查看效果。
任意文件读取
因为不存在 {
,没办法通过 /search
传入参数了,不过根据历史漏洞这个点出问题的情况非常多,我估摸着这次还是和这个有关23333。于是乎追了下整个的流程,意外发现了一个文件读取。
首先当访问 /search/
的时候 search/index.php
会require inc/zzz_client.php
,并且设置 LOCATION
为search。
接下来在 zzz_client.php
里面有一个switch对location的情况做处理,对于search:
1 | case 'search': |
然后在后面会通过一个 load_file
函数加载 $tplfile
的内容,处理内容中的模板参数然后输出。这里 load_file
函数是没有任何过滤的,而 $template
参数可控,于是乎我们就可以通过目录穿越读取到任意文件了!
一个小限制是 getform
最后会经过一个 txt_html
函数过滤,因此无法直接读取php文件:
1 | function txt_html( $s ) { |
PoC:
1 | curl -d "template=../../../../../../../../etc/passwd" -X POST http://localhost/search/ |
文件上传与LFI
试着包含了下flag,果然不行,应该是出题人弄了个猜不到的文件名。不过上面这个文件读取还兼有一个类似于LFI的功能,如果我们能控制服务器上任意一个文件的部分内容,就可以插入模板代码从而绕过过滤回到历史漏洞上去。
前台没有上传点,不过我们可以利用 SESSION_UPLOAD_PROGRESS
来上传临时文件,这篇文章说的很详细了。
值得注意的是默认配置下这个配置是开启的,并且用户可以指定session文件名。改一下exp:
1 | import io |
竞争一会就能成功执行phpinfo。
服务器上的临时文件目录是 /var/lib/php/sessions/
,可以猜常用位置
getflag
一通操作之后我们可以执行命令了,但是在 parserIfLabel
里参数还需要经过一个 danger_key
函数:
1 | function danger_key($s,$type=0) { |
不知道出题人有没有加过滤,但是直接getshell还是挺难的,不过我们或许并不需要完全的shell。可以通过反引号执行 ls / > /tmp/123
然后结合任意文件读取我们就能获得flag文件名,好在没有 readflag
直接读flag就可以啦~
exp:
1 | curl -d "template=../../../../../../../../tmp/123" -X POST http://target/search/ |
搞了这么多乱七八糟的过滤还是不行啊hhh。
babyjava
这个题比较恶心,通过 https://github.com/artsploit/yaml-payload 反序列化拿到shell,扫了下发现有个内网web,接着扫站有个 www.zip
给了apache配置:
1 | +<VirtualHost *:80> |
版本是2.4.41,应该是一个ssrf或者走私之类的,可以看p牛的CVE-2021-40438文章。
不过我们的环境似乎有问题,打什么都是大概率503或小概率主页,演示环境反而成功率很高基本不会503……
扫了半天被503恶心到了,开摆!赛后问了下是打docker,可是我啥都是503打个锤子……