2025 年羊城杯初赛全部打完了,趁热把题目和思路梳理了一遍。这篇文章不是官方 writeup 汇总,更多是我个人对赛题结构的观察,以及对几个有代表性方向的思考。如果你明年准备参赛,或者现在刚开始接触 CTF 想找个适合自己的发力点,这届羊城杯的题目分布和难度梯度都很值得参考。
今年初赛依旧是标准的 CTF 解题赛制,覆盖 Web、Reverse、Pwn、Crypto、Misc 五大赛道。不少队伍的成绩差距其实没有出在难题上,而是出在基础题的完成速度和疑难方向的策略选择上。下面我按赛道拆开讲,每个方向我会先列出今年印象比较深的题型,再展开其中一道题的完整解题思路,最后补一点针对性的备赛建议。结尾我会聊聊备赛路线和比赛时的节奏分配问题,这部分对新手可能比单道题的 writeup 更有价值。
1. 羊城杯初赛的整体印象与赛道风向
先说赛制。羊城杯的初赛一般是 8 到 12 小时的连续比赛,采用 Jeopardy 解题模式,总分由各方向题目分值累加,最终排名靠前的队伍进入决赛。这种赛制最大的特点就是时间压力感很强:题目量大,分值跨度大,一个队伍极少能在所有方向都拿高分,所以从第一分钟开始就要想清楚哪些题是自己团队的“基本盘”。
今年的题目分布比往年更均匀。Web 和 Misc 方向的入口题占了不少基础分,Reverse 和 Pwn 的整体难度略高于去年,Crypto 则是典型的中段分水岭——会的人很快能拿分,不会的人可能卡到比赛结束。
我个人的整体感受是,从内容趋势上看,Web 方向不再只是考单独的注入或文件上传,而是喜欢把多个简单漏洞串成一条完整的利用链。Reverse 方向对代码逆向分析方法的要求更高了,个别题甚至可以直接从识别算法特征入手来解题。Pwn 方向依旧依赖扎实的底层功底,但今年有个明显变化:堆题不再占有绝对主导地位,格式化字符串和栈迁移类的题目比重有所回升。Crypto 方向的高分题几乎都和格密码有关,这在近两年的国内赛里已经成了明显风向。
让我印象很深的一道 Misc 题,表面是一张毫无异常的图片,结果通过流量包里的 USB 协议数据还原出了敲键盘的记录。这类题完全不考“脑洞”,就是考验对 Wireshark 过滤语法和 USB HID 协议理解的熟练度。换句话说,今年 Misc 不再是纯粹的脑洞题,而是更偏“细致取证”。
接下来的篇幅,我会按方向展开,每个方向挑一两道有代表性的题目拆解分析。如果你是刚入门的新手,可以重点看第 2 和第 5 部分;如果你已经有明确的主攻方向,可以直接跳到对应赛道。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. Web 方向:从单一漏洞到完整利用链的转变
Web 题在羊城杯里的分值占比一直不低,今年的初赛同样如此。我粗略统计了一下,Web 方向一共有 6 道题,其中两道属于“送分题”,剩下几道都在考察多个漏洞的组合运用。一个很明显的风向变化是:单点漏洞的直白利用已经很少见了,题目更倾向于让你先通过一个低危漏洞获取信息,再结合其他缺陷形成完整攻击链。
2.1 看似普通的登录接口,其实埋了 XXE
今年 Web 方向的第一道题就很有代表性。题目是一个带登录、注册、个人资料修改功能的完整 Web 应用,界面做得相当精致,扫描目录也扫描不出什么明显的后台路径。很多队伍一上来就在 SQL 注入、弱口令上花了大量时间,结果完全没收获。
我后来复盘时发现,这道题的关键藏在“个人资料修改”这个功能里。当你修改昵称时,服务端会返回一段 JSON 数据,但响应里有一个很不起眼的字段:xmlData。顺着这个线索,往请求里插入一段 XML 载荷试试,服务端居然真的会解析这个字段。
这里就有必要详细说说 XXE(XML 外部实体注入)的原理了。XML 规范里允许通过 <!DOCTYPE> 声明来定义“实体”,外部实体可以从本地文件或者远程 URL 读取内容。如果服务端在没有禁用外部实体的情况下直接解析用户提供的 XML,攻击者就可以构造类似下面这样的载荷:
xml复制<?xml version="1.0"?>
<!DOCTYPE foo [
<!ENTITY xxe SYSTEM "file:///etc/passwd">
]>
<user>
<nickname>&xxe;</nickname>
</user>
这段 XML 的意思是:定义一个叫 xxe 的实体,它的内容是读取本机的 /etc/passwd 文件,然后在 <nickname> 标签里引用这个实体。如果服务端把解析后的内容回显到响应里,你就能直接看到文件内容。
这道题的坑在于,file:///etc/passwd 只能读取 Linux 系统文件,但对于拿到 web 源码来说不够用。所以更常用的做法是用 php://filter 协议来读取 PHP 源码,比如:
xml复制<!ENTITY xxe SYSTEM "php://filter/convert.base64-encode/resource=admin.php">
用 php://filter 是因为直接读取源码时,PHP 文件里的 <?php 标签可能会破坏 XML 解析,所以需要先 base64 编码再读取。我拿到 admin.php 的源码后,发现里面有一个 exec() 函数调用,参数直接拼接了用户可控的命令,下一步就是构造命令注入拿 shell。
所以这道题实际上考了三个点:发现 XML 解析入口 → 利用 XXE 读取源码 → 结合源码中的命令注入拿权限。每一步单独拿出来都不算难,但能把它们串起来,靠的是对 Web 应用整体逻辑的理解,而不是单纯背 payload。
2.2 逻辑漏洞组合:JWT 弱密钥与越权
另一道让我印象深刻的 Web 题是一道 Java 应用。它的登录接口会返回一个 JWT(JSON Web Token),但仔细看这个 JWT 的签名算法,写的是 HS256。如果你在解码后发现头部里的 alg 字段是可以被修改的,那就存在一种常见的攻击手法:算法混淆攻击。
python复制import jwt
# 修改算法为 none,或者保持 HS256 但尝试弱密钥爆破
token = jwt.encode(
payload={"user": "admin", "exp": 9999999999},
key="secret",
algorithm="HS256"
)
print(token)
JWT 弱密钥爆破在 CTF 里已经算老套路了,但这道题的考点不在爆破本身,而在后面的越权利用。你拿到 admin 的 JWT 后,应用里有一个“导出用户数据”的功能,直接把导出的文件名做了一次拼接,而且没有校验用户是否真的拥有该数据,存在目录穿越:
code复制GET /api/export?filename=../../../../etc/passwd
这又是一个典型的信息泄漏漏洞。两道题连起来看,其实就是考察选手对“认证机制绕过”和“授权校验缺失”这两类常见缺陷的敏感度。不管应用封装得多复杂,认证和授权永远是两条最值得优先检查的线。
2.3 赛题之外的提醒:工具与手感的平衡
做 Web 题时,Burp Suite 几乎是标配,这一点应该没有争议。但我想多说一句:工具是辅助,思路才是核心。我见过不少选手用扫描器把所有目录跑了一遍,一无所获之后就不知道接下来该干什么了。这其实是把 Web 安全理解成了“扫漏洞”,而不是“理解业务逻辑”。
一个比较实用的思路是:先像普通用户一样把应用完整走一遍,注册、登录、改资料、上传文件、导出数据,每一个功能都手动操作到,然后刻意地问自己三个问题:
- 这个请求的哪些参数是我可控的?
- 服务端有没有对我的输入做处理?处理了哪些?漏掉了哪些?
- 如果我拿到这个数据,能不能进一步触达其他用户或其他文件?
我个人的习惯是,先手动测完所有功能,再决定要不要上自动化工具。自动化工具的定位是辅助验证,而不是替代思考。
3. Reverse 与 Pwn 方向:底层功底的试金石
这两个方向往往是新手最恐惧的区域,因为它们的门槛肉眼可见的高:你得懂汇编、懂内存布局、懂系统调用,甚至还要懂编译器行为。但从比赛得分角度看,Reverse 和 Pwn 又是高分题最集中的地方。今年羊城杯这两个方向给我的感觉是:题目设计越来越像实际软件分析任务,而不是单纯的“给你一个二进制猜 flag”。
3.1 一题倒下的 UPX 壳,藏着算法识别的关键
Reverse 方向第一题是个 Linux ELF 文件,但它被 UPX 加壳了。比较直接的解法就是用 upx -d 尝试脱壳,有大约七成概率可以直接成功。但今年的题目在这里埋了个坎:脱壳出来之后,主函数里的逻辑并不直观,你会发现程序竟然内置了一个国际象棋引擎的评估函数,flag 的校验结果和“最少步数将死”的计算有关。
听到这里你可能觉得这道题需要写一个国际象棋 AI,那可就掉进坑里了。实际上,程序真正调用的核心函数是一个类似 minimax 的递归搜索,它会在固定深度内返回一个整数。你不需要全程读懂这个递归怎么剪枝,你只需要找到它最后对比 flag 字符串的地方,然后动态调试让寄存器里的值命中正确分支。
这里想强调一个概念:静态分析和动态调试的结合。纯静态分析面对这种轻微混淆的代码可以看懂整体结构,但遇到递归、嵌套调用和复杂数据结构时,效率会急剧下降。而纯动态调试又容易“只见树木不见森林”,找不到关键判断点。正确做法是先静态理清程序的大致流程,找出校验点和输入处理的交界处,再用 GDB 在这个交界处下断点,观察关键寄存器和内存的变化。
3.2 Pwn 方向:格式化字符串的经典回归
Pwn 方向今年有一道题很有意思,题目本身极其简单,就是不断读入你的输入,然后用 printf 打印出来。但它是 32 位程序,而且用了 -fstack-protector-all 编译。
学过 Pwn 的人看到 printf 打印用户输入,第一反应就是格式化字符串漏洞。你可以构造 %p、%x、%n 这类格式占位符,让 printf 越界读取或写入栈上的数据。这道题的利用思路特别典型:
- 利用格式化字符串泄露栈上的返回地址和栈地址;
- 计算偏移量,确定
%n参数的位置; - 用
%n分两次写(高地址和低地址分别写),把返回地址改写成 shellcode 所在地址。
这里有个实操细节值得展开说说。用 %n 写内存时,它会把当前已经输出的字符数写入指定地址。如果这个数字很大,一次性输出几万字节会非常慢,所以通常会采用逐字节写入的方式:把目标地址拆成两个字节,用 %hn 分别写入两次,输出的填充量控制在几百字节内。
python复制from pwn import *
# 假设偏移为 6,返回地址在栈上的位置是 offset
payload = p32(addr_low) + p32(addr_high)
payload += b"%<num1>c%<pos1>$hn"
payload += b"%<num2>c%<pos2>$hn"
这种题的流程其实非常固定,甚至可以说有一点点“套路”,但这不代表它简单。它考察的是你对格式化字符串内存布局的理解,以及利用脚本的调试能力。真正的难度在偏移计算上,稍不留神就会把栈上的数值算错。
3.3 我的建议:入门不要怕汇编
我经常遇到新手上来就问:Pwn 是不是要先读完整本《深入理解计算机系统》才能开始?我的回答是:不用,但一些基础是真绕不开的。你不必熟练掌握所有汇编指令,但至少得能看懂函数调用过程中栈是怎么变化的,ebp/rbp 和 esp/rsp 分别指向哪里,返回地址存在哪里。这些内容其实花两周时间就能达到“能解题”的水平。
另一方面,善用工具能大幅缩短你的入门时间。GDB 的插件如 pwndbg 或 GEF 会在每条指令执行后自动标出栈上的关键地址,省去了手动计算的麻烦。checksec 可以快速查看防护机制。pwntools 则是编写利用脚本的神器。工具不是学习的替代品,但它们是让你把精力集中在核心思路上的加速器。
4. Crypto 与 Misc:看似冷门,实则是决定排名的关键
聊完 Web、Reverse 和 Pwn,我把 Crypto 和 Misc 放一起说,是因为它们在比赛策略上的作用非常相似:都是排名分水岭,也都是新手最容易捡分的区域。很多队伍在比赛前半段疯狂打 Web 和 Pwn,结果在中后段因为 Crypto 和 Misc 的零分而停滞不前。反过来,如果你这两块有稳定的输出,排名通常会很好看。
4.1 Rsa 变种与格密码:Crypto 的两大核心考点
今年 Crypto 方向有一道典型的题目:给了你很多组 RSA 公钥和密文,每组公钥的模数不同,但明文相同。刚开始可能没什么头绪,但如果观察这些模数,会发现它们之间存在很大公约数,也就是所谓的“共享素数”问题。
当若干模数共享同一个素因子时,你可以用欧几里得算法两两计算最大公约数,从而分解出其中一组模数,得到私钥后解密密文。这是最经典的低危 RSA 利用,在 CTF 里屡见不鲜。稍微变形后还会考察低加密指数广播攻击:同一明文用三个不同的模数、同一个加密指数(比如 e=3)加密,这时用中国剩余定理可以直接恢复明文。
python复制from Crypto.Util.number import long_to_bytes
from sympy.ntheory.modular import crt
# 三组 (n, c) 使用相同 e=3
x = crt([n1, n2, n3], [c1, c2, c3])[0]
m = int(round(x ** (1/3)))
print(long_to_bytes(m))
今年还有一个趋势值得关注:难度最高的 Crypto 题不再依赖单纯的数学技巧,而是要求选手掌握格密码(Lattice-based cryptography)的工具库使用。比如用 LLL 算法在短时间内找到最短向量,从而破解一些基于隐问题的加密方案。说句实话,格密码的门槛比传统 RSA 要高不少,因为它涉及线性代数和数论的交叉。但好消息是,很多这类题在比赛层面只需要调用现成的 fpylll 或 sage 库就能解决,关键在于你能不能意识到“这里该用格理论”。
我的建议是:Crypto 方向优先掌握 RSA 常见套路(共模、低指数、共享素数、Wiener 攻击等)、AES 的 CBC 翻转攻击、以及哈希长度扩展攻击。这些在绝大多数国内赛事里已经够用了。等这些熟练掌握后,再往格密码方向深入。
4.2 Misc:从流量分析到内存取证
Misc 是 CTF 里包罗万象的方向,今年的羊城杯尤其体现了它的“细致化”。有一道题给了你一个 .pcap 流量包,里面是某台机器抓取的 USB 通信流量。乍看之下,你可能完全不知道从哪下手。
其实这类题的套路非常固定:USB 协议里,键盘敲击的 HID 数据报文中会包含按键的扫描码,这些扫描码可以被还原成对应的键盘字符。你需要先用 Wireshark 过滤出 USB 中断传输的包,然后提取出每个包的 Leftover Capture Data 字段,再把扫描码映射到可见字符,最后还原出键盘敲击的内容。
bash复制# 先用 tshark 提取 HID 数据
tshark -r capture.pcap -Y "usb.transfer_type == 0x01" -T fields -e usb.capdata > usbdata.txt
提取出来的数据形如 0000080000000000,中间两位就是扫描码。网上有现成的 USB HID 扫描码表,对照查询即可得到按键内容。这类题最大的坑是“方向反了”或“大小写不对”,我自己的经验是先用脚本把所有数据解析出来,观察是否构成有意义的英文句子,再根据语义反推是否需要调整大小写。
另一个方向是内存取证。比赛会给一个 raw 内存镜像,让你找一个进程里藏着的信息,或者恢复某个文件。这类题用 Volatility 就能解决大半问题,关键是掌握常用的插件:
pslist查看进程列表,锁定可疑进程;cmdscan查看终端历史命令;filescan扫描文件对象,找到被删除的文件;dumpfiles把文件从内存中提取出来再分析。
Misc 题的价值在于它考察的是“信息还原”的能力,在真实应急取证场景里非常有用。如果你将来想从事蓝队或安全运营方向,多刷这类题会有很大帮助。
4.3 团队分工:谁负责保底分?
我在前面提到 Crypto 和 Misc 是排名分水岭,这里想再展开说说团队分工的问题。一个典型的 3 人战队,比较合理配置是:
- 一人主攻 Web + Misc;
- 一人主攻 Reverse + Pwn;
- 一人主攻 Crypto + Misc,同时兼顾信息收集、题目排序和全局时间管理。
这样的分工能保证“每个方向至少有两人能碰一碰”,同时避免某个方向完全无人可解。很多全栈型选手喜欢单打独斗,但如果目标是排名靠前,分工协作的效率永远高于单打独斗。比赛开始后的第一个小时,浪费时间在完全陌生的偏门考点上是非常不划算的。
5. 从初赛复盘反推备赛路线:靶场、专项训练与比赛心态
这一部分不是题解,但我觉得它可能是对明年参赛者更有价值的内容。比赛结束后的复盘阶段,我花了不少时间思考:如果让我从现在开始重新准备羊城杯,我应该把时间投入到哪里?
5.1 重视基础题,不要只盯着难题
这次初赛里我注意到一个现象:排名中游的队伍,往往不是栽在最难的题目上,而是栽在基础题上。特别是 Web 方向的入门题、Misc 方向的取证题,这些分值不高但胜在稳定,是队伍积分的基石。
基础题的训练方式很简单,就是大量刷题。推荐几个高质量的练习平台:
- CTFHub:国内选手用得最多的靶场之一,各个方向分得很细,入口题到进阶题都有覆盖;
- 攻防世界:有大量历年真题,适合找手感;
- BUUCTF:题目丰富,很多题型能在里面找到相似的变体。
关键是专项训练:不要今天刷 Web、明天刷 Crypto、后天又跳去 Pwn。选定一个方向后,集中一段时间把该方向的基础题型全部过一遍,形成肌肉记忆。尤其是 Web,注入、上传、XSS、SSRF、XXE、反序列化这些基础类型,应该做到看一眼就知道它们的漏洞点在哪儿。
5.2 用好开源工具,但要知道工具背后的原理
做安全这行,工具永远只是起点。我的体会是:你用 sqlmap 能从数据库里拖出数据,但如果你不理解 SQL 注入的本质,换一个带 WAF 的题目你可能就完全摸不着头脑。同理,你用 Volatility 能从内存镜像里提取文件,但如果你不理解进程和内存管理的基本概念,一旦插件报错你就只能干瞪眼。
所以我的建议是“两条腿走路”:一方面刷题时要大胆使用工具,提高效率;另一方面,每道题搞清楚它背后对应的协议或系统机制。比如你用了 tshark 提取 USB 数据,那你就花一个小时看看 USB HID 协议的基础报文字段。这些知识的边际成本很低,但会在将来的某个题目中成倍回报你。
5.3 比赛节奏:前慢后快 vs 前快后慢?
关于比赛节奏,我的经验是“前 30 分钟不要急”。这段时间适合做几件事:通读所有题目,判断每个方向的难度梯度;快速查看每道题的分值,标记性价比高的题目;和队友确认分工,避免多人同时卡在同一道题上。
正式做题时,我的个人习惯是按分值从高到低但难度从低到高来安排。先快速做完确定的、有把握的题,把基础分稳稳握在手里,再去啃难题。如果你一上来就钻最难的一题,很可能两小时过去毫无进展,心态反而先崩了。
5.4 补充一个容易被忽略的加分项:文档与检索能力
比赛的时候,临时翻文档、查资料是非常正常的事。CTF 不是闭卷考试,没有人要求你背下所有库函数的输入输出。真正拉开差距的,是你能不能快速定位到自己需要的信息。有人能五分钟搜到关键 API 的用法,有人会在一堆无关页面里浪费半小时。
这背后的能力就是“检索能力”。平时多积累自己的知识库:把你刷过的题、常用 payload、踩过的坑,用笔记软件记录下来。比赛时遇到问题,先在知识库里搜一遍,再去互联网上查。这个习惯坚持半年,你的做题速度会明显提升。
6. 写在最后:一场比赛的价值不止是分数
最后说点个人感受。每年羊城杯结束,我都会在赛后总结里写一段“如果让我重打一次会怎么做”。这届初赛给我的启发是:高难度题目确实精彩,但决定一个队伍下限的,往往是最容易被忽视的基础题型。团队里的每一位成员,都应该在赛前明确自己的稳定得分项。
另外,我想强调一句可能有点老生常谈的话:网络安全赛事的最终目的不是培养“会打 CTF 的人”,而是培养“面对真实攻击时知道如何防御、如何取证、如何溯源的人”。我在比赛里用到的很多思路——检查输入输出、寻找信任边界、分析异常流量——本质上和真实安全工作中的思路是一致的。
如果你也想通过实战来检验自己的水平,羊城杯是一个很值得参与的舞台。它的题目难度曲线合理,方向覆盖全面,赛后还会有高质量的官方和选手复盘。希望明年能在赛场上看到你。
