mssCTF 2020 手记

前言

自2018年第一届以来已经办了三届中学生比赛了。无论从出题还是运维角度,mss都已经相对稳定。不出意料,比赛过程中存在着许多问题,况且今年由于疫情原因在线上进行了决赛,但总体而言,在多方努力与支持下,比赛还是相当不错的。

这份总结是对各位的总结的一个大杂烩,段落的作者将以以下的形式给出:

作者id

内容

运维

CTFd部署

Frank

与去年的情况类似,平台最后都基本落到了我手里。平台的部署实际上没有费太大功夫,所有的东西都是现成的。最终用的插件有:

  • ACM评测插件(之前写好的,改了一点前端)
  • 附件下发插件(By Reverier)
  • 容器下发插件(改的ctfd-whale)

大部分都是现成的,部署起来很方便。无非就是需要更换一下非本地资源文件的CDN并分离静态文件。
不过由于moeCTF时让rx提前研究了静态文件如何压缩、分离并由nginx单独serve,以后的比赛应该不用我干预了

日志

Reverier

初赛太辛苦BlackW@tch了. 初赛Web题目采用了静态docker部署的方式, 所有选手共用一个环境. 赛前去看他部署的时候发现apache的日志输出直接映射到了stdout, 这样搞的没办法查看log了, 但是想着题目应该不会出啥大问题, 折腾了一会儿没折腾好就放弃了. 第二天就直接上线.

然后web题成功出问题了. 有一名选手拿到flag之后挂了个脚本持续删掉flag, 重启docker之后继续删, 活生生的把CTF变成了出题人和选手之间的AD. bw只想着赶快修好环境, 也忘记先导出日志抓人, 日志又被重定向到了stdout, 重启一下docker啥都没了, 最后想起来的时候人也没抓着, 比赛也快结束了.

Frank

决赛时由于Web题目统一部署到了同一台服务器,进行动态容器下发,便于日志的记录,采用了腾讯云的日志服务,能看到所有人启动了什么容器且能看到包括请求题在内的完整的请求记录,相对方便对做题进度与做题人进行监控。虽然决赛的web题目并没有很多人做出来,但是从日志还是可以看出很多人通过了一定的关卡的。

在以后的比赛中一定要做好题目日志的记录,时刻把握好做题人的进展。一来日志的收集也不麻烦(当然要提前熟悉好),二来好处多多:

  • 方便反作弊(最直接的原因)
  • 腾讯云日志免费保留14天,期间如果有人举报,可以随时对现场进行还原。
  • 比赛中把握出题人的做题状况可以适时放hint

上面的这些主要面对的是web题目,但对别的方向理应也有一定参考价值。

关于插件与反作弊

Reverier

在讨论平台的反作弊措施时有人提出了能否给不同选手分发不同题目文件的想法, 思索了一下觉得可行, 我便答应下来, 然后投入到静态题目文件自动分发插件的工作当中. 由于时间紧急就没能好好研究CTFd的文件上传与储存机制, 最开始的想法是创建一种新的挑战类型, 然后慢慢改; 后来写好之后发现数据库冲突了, 测试了好久依旧无法实现. 经Frank的提示, 发现其实只要创建一种新的flag类型即可. 时间紧急就采用了最简单的写法, 在api/v1/challenges.py里直接判断flag类型然后返回对应的文件, 简单粗暴. 这样写出来的插件由于更改了平台原有的文件, 所以没法即插即用, 等有时间了好好研究一下如何在不更改原有api的情况下实现自动分发.

初赛过程中自动分发插件倒工作良好, 没出什么幺蛾子.

复赛的账号分发任务交给我来做了, 采用自动注册脚本没费什么力气, 然后发送邮件拜托洛千用工具人做法全部发送到了选手的邮箱. 自动分发插件依旧沿用初赛的插件. Frank收集了所有web和pwn题目之后采用CTFd-Whale插件把题目弄成了动态的, 选手启动自己专属的docker环境做题, flag也各不相同, 防止作弊的同时也有效避免了初赛选手删flag的问题. PPC评测由于Windows下换行符CRLF的问题导致测试题目部分选手写的代码没有通过, 不过没什么大碍. 整个复赛过程中平台运行情况挺稳定的, 整个复赛过程中动态题目总共创建了487次docker环境, 其中用于测试题目创建了51次, 选手解题创建了436次, 其中陈培琛启动了35次题目环境, 杜厚德启动了32次, 陈鸿嘉启动了29次, 在"浪费服务器性能排行榜"上夺得前三.

出题与审题

题目很多都是让19级的学弟出的,完整的题目负责列表如下:

场次 分类 题目名 出题人 | 场次 分类 题目名 出题人
初赛 Misc 签到题 luoqian | 决赛 Pwn gift eqqie
初赛 Misc Avicii luoqian | 决赛 Pwn fishing_master Lunatic
初赛 Misc 到底说了什么 Galaxy | 决赛 Pwn Wal1et Wal1et
初赛 Misc 抽卡游戏 zkonge | 决赛 Web hugme blackwatch
初赛 Web node LT | 决赛 Web xml’s the best Reclu3e
初赛 Web unserialize LT | 决赛 Web calc LT
初赛 Web readme blackwatch | 决赛 Web postme Reclu3e
初赛 Crypto easy_stream shallow | 决赛 Crypto easy_encrypt shallow
初赛 Crypto easy_math shallow | 决赛 Crypto hard_block shallow
初赛 Crypto easy_signin shallow | 决赛 Crypto easy_rsa shallow
初赛 Pwn whisper Lunatic | 决赛 Crypto easy_block shallow
初赛 Pwn blind eqqie | 决赛 Reverse hello Reverier
初赛 Pwn baby_format eqqie | 决赛 Reverse base_pro Reverier
初赛 Reverse doors Reverier | 决赛 Reverse coffee Reverier
初赛 Reverse search Reverier | 决赛 Reverse flower Reverier
初赛 Reverse CheckIn Ruby | 决赛 PPC 灌水 lhz&cdcq
初赛 PPC 栅栏加密 lhz&cdcq | 决赛 PPC 安全评估 lhz&cdcq
初赛 PPC 量子波动速读 lhz&cdcq
初赛 PPC RSA加密 lhz&cdcq
初赛 PPC 肝活动 lhz&cdcq

Web

Frank

Web题是被骂惨了,出题人上头了,审题人也上头了。
出题时应当充分考虑做题对象的水平,才能在比赛的同时达到提高水平的效果。像这次的题目,虽然我们自己已经习以为常,但是冷静思考,很多知识点如果要充分理解,需要对底层原理(python、PHP)有相当深入的了解。对于中学生而言,这是很不现实的。

还有一点可以改进的地方是出题前运维应当与出题人沟通好日志如何收集,并在审题阶段对日志收集功能进行测试。今年由于时间匆忙没有仔细地审题,出了各种问题。

Pwn

Reverse

Reverier

初赛逆向题目放出了三道, 有两道题目是我出的, 考点分别是指令虚拟化和全排列, 难度中等, 没爆0. 复赛题目放出了四道, 上午Java逆向和花指令, Java逆向是一个六元一次方程组求解, 两解; flower直接爆0了. 下午本来准备上happy出的一道vm, 但是上午看了看解题情况, 吓得不敢放了, 于是临时出了一个签到题, F5就能看见flag 的那种. 加上一道换表base64解密, 下午成功没有爆0. 有一说一题目质量出的有点低了… 没把握好选手的整体水平, 最后导致题目难度极不合理.

PPC

cdcq

出现的问题:

  • 检查用的时间不够,没有查常规项(例如数据范围,典型的常规项),不能因为题简单就不查
  • 最后没有总测数据,结果导致std改了,但是样例忘了改
  • 没有强调行末(而不是文末)空格。这个应当在评测插件中新增一个Presentation Error进行提示
  • 使用多组数据。多组数据输入对出题人来说很方便,但是对OI选手来说,不习惯多组数据,会导致很多人忘记初始化,从而卡在非核心考察点上,降低比赛体验