
三两杂感。
缘起
CTF实践课是2023年以来HITsz开设的一门创新竞赛实验课程(全称太长我记不住,姑且叫CTF实践课吧)。自Del0n1x成军后,这门课的结课赛就一直由我们负责,从平台运维、赛题设置到赛事筹办、临场答疑,一手操办。当然,所谓的结课赛,其实就是以CTF形式呈现的类似期末测验的东西。前两年确实只是面向课内的"期末测验",但今年的结课赛从单纯的面向课内学生变成了面向全校,名义上也从结课赛一跃而成为HITsz CTF
。不过时间上还是老样子,只持续8h左右,比起正经的48h
CTF还是差了不少。
赛前筹备
基础设施
之前其实提到过一点,赛事平台在中途从摇摇欲坠的学校服务器搬迁到了我的homelab。其实homelab的64G内存就是在决定迁移后才额外买条子扩容的,原本homelab的内存只有32G而已。
因为只有一台机子所以还是用docker compose up
直接起了个GZCTF的demo,而没有选择k8s集群化部署。不知道是不是这个原因,在课程剩余的一个月时间中平台多次出现与issues/417类似的报错,一番排查也找不到原因——而且每当这个时候,切换一下GZCTF的版本就能缓解...所以最终的权宜之计就是每次报错就切一次版本🤣
题目设置
pid师傅有篇文章写得很有参考价值。毫不夸张地说,这一个月来我都在反复咀嚼回味这篇文章。经历了去年Crypto方向惨痛的非预期教训,再加上今年我需要负责Misc+Crypto两个方向的所有命题工作(甚至还有1/3的Reverse题目),所以题目质量被我提到了相当高的优先级。
什么算好的题目呢?我的预想是,首先要与课程讲的内容有一定联系,不能让人家上了一学期之后发现考的东西跟讲的东西八竿子打不着,那样的话本课程又要差评如潮了;同时要保证解法/考察点足够的有趣,就算没有思路也可以在题目的引导下去探索把玩。当然,在此基础上还要保持合理的难度梯度,按pid的说法,50%的题目是大部分人花点时间就能解决的。当然我的选择更苛刻一点——50%的题目是头部选手花点时间能解决的;剩下的50%中的每一道题,即使头部选手花费一整天时间也不一定能解决。
审慎地考虑许久,最终我决定拉低Misc方向整体的难度,目标是所有题目都有解;而Crypto方向则是2道简单题,预期7-8解;1道中等题,预期2-3解;以及3道难题,预期0解。其他方向不是我负责,不过基本也是跟Crypto类似的难度梯度。并且这次为了鼓励选手每道题都看看,所有题目的初始分值都是统一的,再也不会有"看到是高分题目所以很难干脆不做了"的情况出现了😋
当然代价就是落后的队伍也很难通过一道难题翻盘了
明确目标后就是漫长的出题过程。Misc和Crypto前前后后施工了一个月有余,直到开赛前一晚还在缝缝补补;Reverse则是直接扔了两个很久之前出好的难题。最终,Misc上了1道签到题,1道DTMF隐写+TTS的音频套娃题,1道去年结课赛考过的工具查找题,1道zip明文攻击+LSB隐写的图片套娃题,1道PyJail,还有1道考察Visual Studio项目中.suo文件导致的RCE。Crypto则是偷了两道国外比赛的简单题(有注明作者!),改编了课上的一个格密码例题作为中等题,改编了HITCONCTF和ACTF的两道题的非预期解,又自己出了道LFSR考察联结多项式,作为3个难题。
除了CTF传统的几大方向,这次还设置了两道线下赛的实操题目作为彩蛋——一是复现Windows 7 x32下的永恒之蓝漏洞,一是破解实验室AP设备的WiFi密码。因为是实操,所以两道题的分值均是普通题目的两倍。

传统的五大方向每个方向五六道,AI2道,加上OSINT和Pentest之类的,林林总总32道题,
宣传动员
比赛前一周队友写好了公众号推文,我在得知此事后花了半个晚上紧急糊了张海报出来,从而得以构建公众号-年级群-QQ空间
的完整宣传矩阵。遗憾的是,我们的宣传效果微乎其微,最终公开赛道报名的几位还是去年选过课的ldx😔

不过事后看来报名的选手质量相当之高,用质量换数量,倒也可以接受(才不是因为宣传工作不到位呢)
赛中战况
上午
开赛10min后,签到题的解题数量为0😢
本来我预想中签到题应该是15min左右就被打穿的简单题,因为真的只需要BurpSuite抓包-修改-重放三步走,而Burp是Web课上讲的滚瓜烂熟的工具。但是赛中巡场才发现,很多人的设备上根本没有Burp,也没有意识到实验室设备上提供了Burp;少部分人想起来实验室电脑上有Burp,但是其中会用的人又是少数派。我意识到不对劲,迅速放出了签到题的提示,并且开始解答选手关于Burp使用的疑问。
饶是不至于爆零
Web、Crypto和Reverse的做题进度则差了更多,Crypto的两道简单题在开赛2h后依然0解(我在开赛1h后直接放出了其中一题的解题脚本,但是甚至没人愿意运行一下QAQ),Web的签到题在开赛1h后才有1解,Reverse的签到题更是在开赛2.5h+3条提示的加持下才有1解。
当然,Web的情况多多少少有所预料,因为那个签到题的js代码大部分是混淆过的,只有与flag相关的两三个函数是没有混淆的。面对一大坨混淆代码还要意识到其中一部分代码没有混淆,这对于选手来说并不是很trivial。Reverse则最为抽象,明明给出的是Verilog源码,但很多选手看到sv文件还是选择IDA打开😥
这一点不是很合理,你深的计组充斥着大量Vivado和Verilog。而大二已经学了计组这门课,不应该意识不到sv文件是Verilog源码
至于Crypto,只能说配置sage环境难倒了太多人。即使课上和赛前都强调了sage环境和相应的Python包如何安装,但是赛场上还是有相当大一部分人缺环境。从做题情况来看,赛前对选手初等数论水平的估计也太过乐观了。并且,赛后我才发现Crypto的另一道简单题其实可以被LLM秒掉...
这意味着很多人甚至压根没看题,或者不太懂得充分利用AI工具
我们赛前不是没有强调过AI工具的重要性,并且也是鼓励使用AI辅助的,不过诚如pid所说:
AI 就像杀人书 (一件 LOL 的装备,属性随击杀数增强),或者有几个版本的阿卡丽 (LOL 中的一个可操控角色,在高手手中很强,新手手中又很弱),需要使用者自身具备足够的基础才能激发出更大的效用。
不过这也不是问题。做题情况差,放提示就好了。并且得益于互动性题目足够多(井字棋,LLM,MineCraft,OSINT...),我觉得对后段选手来说体验也不会太坐牢。从提交记录来看,选手的积极性还是很不错的。大言不惭地说,我们成功保住了做题体验这一下限,所以这场比赛至少成功了一半。
随着提示的陆续放出+top2队伍猛猛发力,到了午间,赛前预计的有解的题目已经全部被打通。
下午
简单题被扫荡完毕后,下午选手们的做题速度明显放缓不少。13:00左右就已经有队伍开始申请实操题,想来是传统题目已然难以推进。这也正合我们心意,毕竟都线下赛了,不试试有意思的实景渗透怎么行呢?
更何况两道实景题都是双倍于普通题目的分数
一点小失误是,这两道实景题对macOS都非常不友好,特别是永恒之蓝,几乎是Linux only的玩具(metasploit yes!),这就苦了一些Mac+Windows做题的队伍😣
这个失误也直接导致一些队伍放弃了做永恒之蓝的念头,最后只有一支队伍打通。另一道实景题则相对更有人气一些,最终有四支队伍都进行了尝试,并且有两支队伍成功打通🥰
rank 1的队伍打通了所有实景题,符合预期。
不如说就是因为打通了两道实景题所以才是rank 1?
还有一个小插曲。我们的永恒之蓝靶机是32位的,题面里也说明了这一点。而上台挑战的队伍无一例外都先尝试了64位的payload,碰了一鼻子灰2333
打通实景题后,排名几乎稳定,直到比赛结束。
与此同时,GZCTF v1.1.3 在8.5h的比赛过程中稳定运行,没有再出现之前的报错。安稳的完赛,让我心里最大的一块石头落了地。
赛后小结
Wp还没有上交完成,所以这里只是以赛中观察汇就的管中窥豹之辞。
- 因为一些原因,Pwn事实上几乎是零解,不过具体原因请允许我讳莫如深,总之很不乐观就对了
- Reverse的签到题难住了所有人,不过在层层提示下还是有4个解;中等题爆零,难题则有一题有1解,可喜可贺
- Crypto的简单题在一题给了题解一题AI可秒的情况下各自3解,中等题1解,难题爆零,远低于预期
- Web的简单题一个6解,一个1解,中等题1解,难题爆零,比预期略差
- Misc最难题在有提示的情况下2解,符合预期
- AI简单题成为了本场比赛事实上的签到题
- OSINT比预想中简单,也比预想中有意思
放一张积分趋势图:

top 2的竞争颇为激烈,且分属两个赛道,这是我喜闻乐见的;除了管理员账号,也没有零解队伍。这次比赛,虽不能妄言成功,至少也不算失败。
办赛是为了交流学习,让选手学到东西才是第一要务。基于这种考虑,Crypto方向的题解,在比赛结束后我就立刻放了出去。其他方向等wp收集完成后应该也会放出,不过那就是后话了。
进一步的分析?或许我会在wp收集完之后再来更新本文吧~