基于Swoole的CTFAWD竞赛环境的搭建与实践凭借AWD的相关经验,我在一次头脑风暴中使用PHP的Swoole扩展搭建了一个竞赛环境,将分享会变成了一场友谊赛。题目思路本题目来源于我在一个外包项目中的实践。项目中大致有这样一个需求:客户登录系统后,外部设备触发websocket发送操作(比如“开门”、“滴卡”、“按开关”等)在嵌入式系统中经常遇到),requestreceiver为一个已经登录的用户(通常由用户id或用户名绑定)。我最初开发这种应用时,使用了用户唯一标识的userid作为websocket通道的名称。这样做的后果是,无论用户在哪台电脑登录,无论用户登录多少次(小项目没有重复登录+挤下线判断),只要成功登录并打开相应的网页,你会收到这个websocket请求,这会带来一些意想不到的信息泄露。实际项目中有很多解决方案,比如重复登录的判断,websocket“匿名通道”的建立等。这道题是基于AWD的工作原理。CTF-AWD是线下比赛中常见的比赛类型。它通常因其进攻和防守能力而受到玩家的欢迎。这里我主要实现了AWD的两个关键功能:回合制、生存检测和动态旗帜。至于提交flag等前端内容,则由我校GOCTF平台统一处理。回合制:比赛以10分钟为一局进行。在中央服务器方面,提前使用脚本根据目标机器的数量和已经开启的IP生成flags并保存为文件,在游戏开始时记录开始时间。中央服务器每次收到玩家无人机的请求,根据无人机的IP和游戏开始的时间,计算当前处于第几回合,并在flag数组中返回对应的值。而在pusher(一个第三方的websocket工具,可以为运行在cli上的脚本语言提供发送websockets的能力,这个问题就看pusher了),也是以10分钟为一轮,主动推送包含关键内容的内容游戏给玩家。消息存活检测:这次平台库存监控设置比较简单,主要是对web服务(目标机器的80端口)的监控。每隔一段时间向目标机发送一个http请求,在目标机上下载一个保存pusherkey的js文件。如果文件大小符合预期,则确定目标机器是存活的。玩家在游戏过程中需要窃取对方的key来窃听对方的websocket内容,修改自己的pusherkey来释放泄露的动态flag:这部分也是由centralserver和pusher完成的。当请求时,中央服务器根据不同的时间(不同的回合)返回不同的标志给玩家。推送者根据不同的时间(不同的轮次),主动向玩家页面推送不同的flag。AWD缺陷总结在完成本次比赛后,我发现这个平台如果真正用于选手的日常训练,还有一些问题需要克服。由于学校的GOCTF平台在比赛结束时并没有很好地支持动态旗帜功能,因此无法实现根据每一轮中央服务器主动通知CTFF平台旗帜变化的功能,只能暂时让玩家记录自己的标志233333...TAT时钟很难统一。因为推送器和AWD中心服务器不一定运行在同一台服务器上,中心服务器被动接收数据,而推送器主动推送,而且启动时间不同,所以每一轮很难做到完全统一。以后如果需要再做AWD比赛平台,可以添加定时心跳包,保证时钟统一。访问80端口,注册多个用户,登录系统,发现UserID1的用户已经注册,系统提示必须使用UserID2登录。2、你会发现页面以websocket开头,隧道名是user.2。切换到其他用户后,发现websocket链接的是隧道名用户。(当前用户的登录id)以弹窗的形式主动提醒“游戏新闻”,“游戏新闻”包含flag1,但弹窗出现后2秒内,强制用户跳转到注销页面(暗示XSS)登录服务器后台,修改视图文件(home.blade.php),在底部JS部分user中输出当前用户ID。{{Auth::user()->id}}改为user.1(表示强制接收user.1隧道报文)。下一轮推送,收到user.1消息,得到flag2,flag2以=结尾,类似base64编码,解码后得到(机密)账号密码的rot13值,正确密码为重新旋转rot13后获得。以(机密)管理员身份登录后,发现有上传头像的模块,只做前端验证,修改burpsuit请求中的文件名即可上传到PHP“Chopper”"获取系统shell,用户为root。了解了整个过程后发现pusher.js中包含了pusher账号的key,而这个文件在80端口可以很方便的下载,所以每一个目标机器都存在泄露问题。获取到对方的pusherkey后修改为自己的服务器在对方user.1隧道接收消息,获取对方的(机密)管理员账号密码,利用文件上传漏洞获取shell。除了日常的备份、在线监控、源码审计,Shou主要有以下解决问题的思路。mainline/root目录下有个readme.txt,提示从历史命令记录中寻找入口,发现以下两条命令可疑:curl172.17.0.1和phpartisantinker使用`ipaddr命令查找发现和之前的命令在同一个网段,猜测是向中心服务器发起请求进行curl测试,发现没有这个命令,apt、yum等工具无效。使用cat/etc/issue查看当前系统是alpinelinux,所以使用apkaddcurl命令安装curl工具。正常请求后获取flag3根据游戏规则,flag3是一个动态标志,每轮变化一次,不能提交给你的目标机器。所以,可以按照这个思路配合负责进攻部分的队友完成对对方靶机的穿透。比赛使用phpartisantinker命令Laravel框架封装了psyshell,可以直接进入psyshell通过ORM操作修改管理员账号密码,从而获得自己目标机的管理员权限,这和上面描述的websocket修改用户id窃听的效果类似,但是难度比较大,没有达到预期。websocket测试站点的子线每一轮都会收到一个弹窗,会强制跳转到“注销”页面。推测存在XSS漏洞。源代码审核后,resources/views/home。blade.php文件中将$(“”).html(data.message)改为$(“”).text(data.message),这样推送内容不解析,推送XSS中毒(笑死了上天~)源码审计发现在/upload的url下使用upload()方法处理文件上传操作,并没有判断扩展名、矿机类型等,可以试试强制上传文件到对方的服务器。但是,Laravel框架默认启用了csrf拦截。需要在对方服务器注册账号获取csrftokenGithub项目地址https://github.com/zzfly256/C...运行环境AWD平台部分:PHP7+Swoole4目标机部分:任意版本docker项目文件引入服务器:AWD中央服务器,运行在docker宿主机上,负责提供每一轮的flag:flag3getFlag该文件是一个php可执行文件,根据docker中启动的容器ip为容器生成ps命令flags不同,保存为flags.jsonserver文件是一个php可执行文件,监听80端口,根据不同的rounds返回flags.json文件中对应的值flags.json保存生成的flagpusher:Pusher的server.js,在dockerhen或任何计算机上运行。关于pusher的介绍可以在官网找到:www.pusher.compusher-admin该文件是一个php可执行文件,为比赛中的管理员用户推送消息(供玩家窃听websocket,包括一个flag:flag2)pusher-user文件为php可执行文件,为比赛中的普通注册用户(ID为2)推送消息(包含一个flag:flag1)。monitor文件是一个php可执行文件,用于监测玩家无人机是否存活(监测web服务/pusher.js文件大小)pusher-server文件是一个php可执行文件,根据回合推送消息(time)和生存检测getFlag文件是一个php可执行文件,用于生成管理员消息(flag2)。pusher-key.json中保存了每台目标机的pusherkey,以及flag2的值(flag1的值为固定值,每个玩家都一样)web:竞赛题目的企业网站部分为laravel5.8框架,sqlite数据库业务逻辑主要在/app/Http/Controllers/HomeController.php视图文件在/resources/views目录下docker:目标机dockerimage关于本项目更多介绍,你可以移步到:http://www.zzfly.net/build-a-...
