1. 赛前装备:比刷题更重要的信息收集与工具链准备
很多选手赛前喜欢疯狂刷题,但我见过太多人忽略了一个关键问题:CTF解题的本质不是“会不会”,而是“在有限时间内能不能想起来”。 真正拉开差距的,往往不是某个冷门技巧,而是你能不能在三分钟内判断出一道题的题型、找到正确的入手点、并调用起对应的工具链。
先说信息收集。赛前拿到题目附件,不要急着打开。第一步永远是确认文件类型,我习惯用 file 命令先扫一遍,很多签到题就是把一个文本文件改了个 .png 后缀,file 一秒钟就能识破。第二步是查看文件元数据,图片的 EXIF 信息、文件的十六进制头部、压缩包的注释区,这些不起眼的地方经常藏着提示字符串。第三步是计算哈希值,有时候题目故意让你在公开的哈希库里比对,直接就能定位到原文件,省去大量逆向时间。
工具链的准备也要讲究。我见过不少选手的虚拟机里装了百余个工具,但真正用上的不到十个。以我的经验,一套精简但够用的工具链就够了:
- 信息搜集:
file、binwalk、strings、exiftool、010 Editor - Web 类:
Burp Suite、sqlmap、dirsearch、ffuf - 逆向与 PWN:
IDA Pro(或Ghidra)、gdb(配合pwndbg插件)、objdump、checksec - 密码学:
Python(配合pycryptodome、sympy、z3库)、Hashcat、John the Ripper - Misc 杂项:
Audacity(音频分析)、Wireshark(流量分析)、Stegsolve(图像隐写)、zsteg、outguess
工具不在多,关键是你知道什么时候该用哪一个。下面我会按题型拆解实战思路,每类题都会给你一套"先做什么、再做什么、卡住了换什么思路"的完整链路。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. Web 题:从端点发现到权限获取的高频套路
Web 题在 CTF 中的占比常年最高,也是新手最容易上手的方向。很多人觉得 Web 题就是"找注入点、打 SQL 注入",但实际比赛里,考察面要宽得多。
2.1 信息泄露类:先翻源码和备份文件
遇到一个 Web 题,我首先做的不是扫目录,而是直接看页面源码。注释里的 HTML 代码、隐藏的表单字段、JS 文件里写的 API 接口,这些是出题人最爱的藏宝地。曾经有场比赛,一道题把所有 flag 都写在了 main.js 里的一个配置对象中,只是因为文件名是 bundle.min.js 看起来"像是混淆过的",就没人认真读。
JS 文件要用浏览器开发者工具格式化后再看,重点找这几个关键词:flag、secret、admin、api、token、key。格式化之后你会发现很多看似冗长的代码,其实只有一小段在真正干活,其余全是混淆噪音。
目录扫描是第二个必做动作。dirsearch 配上常用字典,重点找这几个路径:/.git/、/.svn/、/backup.zip、/www.zip、/index.php.bak、/flag.txt。.git 泄露是高频考点,如果扫描到 /.git/ 存在,直接用 GitHack 一把梭,能把源码完整拉下来。源码到手后,审计其中的关键逻辑,尤其是登录、上传、文件读取这三个功能点,90% 的 Web 题考点都藏在里面。
2.2 注入类:先判断闭合方式,再想绕过策略
注入类题目依然是 CTF 的常客,但现在的考察重点已经从"爆数据库"转向了"绕过过滤"和"利用特殊场景"。
SQL 注入的第一步永远是判断闭合方式。输入一个单引号看报错,输入 1' and '1'='1 对比正常页面,再输入 1' and '1'='2 确认是否存在布尔差异。这套三连招做完,你就能判断出是否存在注入点,以及是字符型还是数字型。
如果 WAF 过滤了空格,用注释符 /**/ 或括号绕过;如果过滤了 select,试试大小写混写、双写 seselectlect、或者用内联注释 /*!select*/。这些都试过仍然不行,考虑时间盲注,用 sleep() 延迟来判断条件。
命令注入的套路也类似,重点看哪里接受了系统命令参数,比如 Ping 功能、文件导出功能、日志清理功能。; ls、| cat /flag、$(whoami)、反引号包裹——这四种注入方式是必试的,如果过滤了分号和管道符,可以试试用换行符 %0a 来分割命令。
2.3 SSRF 与任意文件读取:内网横向的关键跳板
SSRF(服务端请求伪造)在近年比赛中出现频率极高,核心思路是找到一处能向指定 URL 发起请求的功能,然后利用它去访问内网资源。
拿到一个疑似 SSRF 的题目,先做一个最小验证:提交一个自己能控制的 URL,比如 http://your-server/test,如果服务端真的发起了请求,会在你自己的服务器上留下访问日志。确认存在之后,利用方式就多了:
- 读云元数据:请求
http://169.254.169.254/latest/meta-data/,如果目标是云环境,可能直接拿到临时密钥 - 扫内网:利用 SSRF 当作代理,探测
http://127.0.0.1:port或内网网段哪些端口开着 - 组合利用:如果内网里有个未授权访问的服务,通过 SSRF 直接打过去
任意文件读取的重点则是路径穿越与伪协议。路径穿越只要在文件名参数里多试几个 ../ 层级,或者用 URL 编码 %2e%2e%2f 绕过过滤。伪协议是 PHP 题的特色,php://filter/read=convert.base64-encode/resource=index.php 可以无痛读取源码,data://text/plain;base64,xxx 可以执行任意代码。
2.4 反序列化与模板注入:两条从代码执行到 RCE 的路
反序列化题在高级别比赛中非常常见,这需要你看懂代码。拿到源码后,先找 unserialize()、pickle.loads()、ObjectInputStream.readObject() 这类函数作为入口点,再追踪类中定义的魔术方法:PHP 里是 __wakeup() 和 __destruct(),Python 里是 __reduce__() 和 __setstate__(),Java 里是 readObject() 和 readResolve()。
构造 payload 时,很多新手卡在"不知怎么把代码串起来"这一步。我的建议是先从网上找一个成熟的 PoC 改,把自己环境跑通,再逐步调整参数去适配目标函数。比赛时直接手搓一个长篇 POP 链不现实,除非你已经提前积累好了常用框架的 payload。
模板注入(SSTI)的判断更简单:输入 {{7*7}},如果页面回显 49,恭喜,你拿到了模板注入的入场券。接下来要确认模板引擎类型——Jinja2、Twig、Freemarker、Velocity 的 payload 各有不同,但凡是能回显 49 的,几乎都能通过模板的底层语言(Python 或 Java)执行系统命令。Jinja2 的一套经典 payload 是从 __class__ 开始逐层向上寻找可利用的类,再找到 os.popen 执行命令,这套链路在现代 Flask 应用里依然好用。
3. 逆向与 PWN:二进制的快速收敛思路
二进制题看起来门槛高,但比赛中的出题套路其实相当固定。掌握一套快速的"收敛思路",能把几个小时的逆向压缩到半小时。
3.1 逆向题:先 strings 后查壳,先动态后静态
拿到一个 ELF 文件,第一件事不是扔进 IDA,而是先跑 strings。如果直接能看到 flag 相关的字符串,说明这是一道送分逆向题,直接用 grep flag 就能定位到关键比较函数。
看不到关键字符串,第二步是查壳。file 命令加上 upx -d 试一下,如果提示是 UPX 加壳,直接脱壳后再重新分析。不是 UPX 壳的话,用 DIE(Detect It Easy)识别壳的类型,常见的还有 ASP 壳、虚拟机壳,遇到这些就老老实实用动态调试跟着走。
静态逆向时,我的习惯是先定位 main 函数,再看关键分支。在 IDA 里按 F5 看伪代码,找 strcmp、memcmp、atoi 这些比较类函数,在它们附近打断点。动态调试配合输入构造,让程序走完整个验证流程,在比较函数处观察寄存器和栈上的值,密钥往往就直接暴露在眼前。
一个实操小技巧:如果程序要求输入一个数字然后判断序列号,别急着逆向算法,先试试负数和超长数字,很多签到题的校验逻辑就是把输入和某个固定值做了等式比较,溢出、负数、0 这三种边界值往往能直接绕过。
3.2 PWN 题:checksec 一眼定方向,ROP 链三板斧
PWN 题的起手式只有一个:checksec。它告诉你这个程序开没开 NX、PIE、Canary、RELRO。这四样东西直接决定了你的利用方式:
- NX 未开启:直接往栈上布置 shellcode,跳过去执行
- NX 开启:考虑 ROP 链,把 libc 里的 gadget 串联起来
- PIE 开启:需要先泄露程序基址,再计算偏移
- Canary 开启:需要先泄露 canary 值,或者寻找可以覆写 canary 的特定漏洞
找漏洞点是 PWN 的核心。空闲时间多练手,你会发现**格式化字符串、栈溢出、堆溢出、UAF(Use After Free)**这四类漏洞占据了绝大多数题目。格式化字符串 %x 和 %s 的利用可以泄露栈上数据,也可以写任意地址;栈溢出则找危险函数 gets()、strcpy()、read(),判断拷贝长度是否超过了缓冲区大小。
ROP 链的构造有一套标准动作:第一,用 cyclic 生成测试字符串,跑崩溃后拿到溢出偏移量;第二,检查 plt 表里有没有 puts 或 write,用它们来泄露 libc 地址;第三,根据泄露的偏移算出程序使用的 libc 版本,用 LibcSearcher 匹配出 system 和 /bin/sh 的地址;第四,重新构造 ROP 链,调用 system("/bin/sh") 完成利用。
如果你在比赛中堆题卡住了,我的建议是及时止损。堆利用的调试耗时较长,除非你能在十分钟内确定利用结构,否则可以先放一放,去攻其他题。这听起来不像"技术建议",但现实中,时间分配本身就是 PWN 题的重要技能。
3.3 逆向还原算法:找出输入变换的数学规律
有一类逆向题不靠比较函数,而是靠算法校验。程序可能把一个输入字符串做了一串复杂的位运算、置换、异或,然后和一个预期结果比对。这种题的核心在于还原输入变换的逻辑,而不是去爆破。
我的做法是:在关键加密函数入口和出口各打一个断点,观察输入的变换规律;如果是简单的异或,按键值一遍遍试;如果是字节置换,拿 ABCDEFGHIJKLMNOP 这样规律明显的输入去跑一遍,看输出里每个字符挪到了哪个位置;如果涉及复杂的算术变换,用 z3 求解器把约束编码进去,直接求解满足条件的输入。
z3 是这类题的神器,它的思路是"我不管算法怎么实现,我只知道输入和输出之间的约束关系,让求解器去算"。比赛前花半小时熟悉一下 z3 的 Python API,绝对不亏。
4. 密码学:识别算法弱点比硬算重要得多
密码学题在 CTF 里是一门"看着难、实则套路多"的题型。大多数题目并不是让你去破解真正的加密算法,而是利用出题人留下的参数漏洞和实现疏忽。
4.1 RSA 系:看参数找漏洞,n 分解永远是第一位
RSA 相关的题占了密码学题目的半壁江山。拿到一个 RSA 题,先把给的参数列出来:n、e、c、p、q、d。然后按这个顺序排查:
n是否过小。小于 512 位的n可以直接用yafu或sage暴力分解- 是否存在多个
n共享一个素数。如果给了两组(n1, c1)和(n2, c2),先算gcd(n1, n2),如果结果不是 1,恭喜,直接拿到了公因子p e是否过小。e=3时,如果明文很小,c直接开三次方就是明文- 是否给了
dp或dq。有dp就能通过爆破十六位二进制数还原出p - 低加密指数广播攻击:同一个明文、三组不同的
n/e/c,用中国剩余定理直接恢复明文
我见过最多人卡住的不是上面这些,而是出题人故意把 n 写成两个非常接近的素数乘积。这种情况用 Fermat 分解法,从 sqrt(n) 开始往上找平方差,几秒钟就出来了。判断方法很简单:两个素数接近,意味着它们的平方平均值和 sqrt(n) 的距离极小,Fermat 分解的攻击半径很小。
4.2 分组密码:识别模式,利用 IV 复用和填充预言
AES/DES 这类对称加密题,考点往往不在算法本身,而在使用方式。最常见的是 ECB 模式,因为同样的明文会得到同样的密文块,这种模式在 CTF 里基本都是被用来做字节翻转攻击或者拼块攻击的。
CBC 模式有两个经典漏洞点。第一个是 IV 复用,同一个 IV 加密两段数据,异或之后 IV 会被抵消,可能直接泄露部分明文。第二个是 填充预言攻击(Padding Oracle Attack),如果服务器会对"填充错误"给出不同的响应,你可以通过不断修改密文的一个字节、观察服务器响应,逐字节还原出明文。手工实现这个攻击比较繁琐,但利用现成脚本库可以快速跑通。
这里有个容易被忽略的思路:先看题目给的密文长度和格式。如果密文是 hex 字符串且长度为 32 或 48,基本可以确定是 AES-128 或 AES-192;如果提供了 key 的生成方式,比如 key = hashlib.md5(flag).digest(),那么密钥本身就是你需要的 flag——这类题目考的是识别出"key 的来源"而不是破解加密。
4.3 哈希类:长度扩展攻击与碰撞利用
哈希相关的题目集中在两个点:长度扩展攻击和已知碰撞。
长度扩展攻击针对的是 MD5/SHA1/SHA256 这类 Merkle-Damgard 结构的哈希。场景通常是这样的:服务器用 secret + message 来做认证,只要你知道 secret 的长度(通常是爆破出来),即使不知道内容,也能通过 hashpump 工具构造出 secret + message + padding + extra 的合法哈希值。这个攻击让我印象很深,因为很多选手看到"不知道密钥"就直接放弃了,其实密钥长度只要在合理范围内,暴力枚举长度往往一试就中。
已知碰撞的利用场景则出现在弱校验中:题目可能提供了一个文件的哈希值要求你找到另一个文件哈希相同。对于 MD5 和 SHA1,直接找现成的碰撞样本即可,网上开源项目不断更新,碰撞样本库也越来越全。
4.4 古典密码:频率分析和词频统计的快速定位
古典密码题通常是 Misc 和密码学的交叉点,出题人会给你一段密文,可能是凯撒、维吉尼亚、栅栏、培根、摩斯等内容。这类题没什么高深技巧,核心是识别编码类型。
先把密文丢到在线识别工具里,或者用脚本检测字符集。如果全是大小写字母没有空格,可能是凯撒或维吉尼亚;如果包含 /、-、空格,很可能是摩斯码;如果是一串 A/B,可能是培根密码;如果字符集是 0-9A-F 的十六进制形式,先转 ASCII 看看。
这里我推荐一个常规操作:先用 Python 的 collections.Counter 统计字符频率,如果密文长度足够长且频率分布和英文文本接近,直接上词频分析工具。比赛里的古典密码题通常不会真的让你手工分析,考点就是你能不能想起"用频率分析工具去解维吉尼亚"。
5. Misc 与取证:冷门杂项里的高频得分点
Misc 题在 CTF 中一直被看作"杂项",但这恰恰是得分效率最高的板块。因为 Misc 的难度梯度极大,前几道送分题基本人人都能做出来,而后面的难题大家也都做不出来——换句话说,Misc 是拉开时间效率差距的关键。
5.1 图片隐写:从 LSB 到 EXIF,按层级排查
图片题是 Misc 的绝对主力。拿到图片后,按这个顺序排查:
strings直接搜索可见字符串,flag字段可能就在图片描述信息里exiftool查看 EXIF 元数据,出题人经常把部分 flag 贴在Artist或Comment字段- 用
Stegsolve逐层分析颜色通道,如果 R/G/B 通道的 LSB 有规律性变化,说明是 LSB 隐写 zsteg专门检测 PNG 和 BMP 的 LSB 隐写,支持多种位深组合- 如果图片看起来像损坏的,检查文件头,用十六进制编辑器修复文件头,比如 PNG 的
89 50 4E 47、JPEG 的FF D8 FF、GIF 的47 49 46 38
有一个我踩过很多次的坑:有些图片隐写会把信息藏在图片末尾追加的数据里。strings 扫不到是因为追加的数据是二进制格式,看起来像乱码,但用 binwalk 或直接查看十六进制末尾就能发现。每次拿到图片,我都会习惯性看一眼十六进制区域的末尾是什么,这是一个几乎零成本的信息获取方式。
5.2 音频与流量:Audacity 看频谱,Wireshark 筛协议
音频题的核心工具是 Audacity。把音频文件的波形切到频谱图视图(Spectrogram),如果隐写了信息,会出现一段和其他部分明显不同的频率图案,声音波形上看似杂音的白噪声区,频谱图里往往藏着二维码或文字的图案。
流量题用 Wireshark 打开 .pcap 文件。最基本的操作是看协议统计:哪个协议流量最多,重点看 HTTP 和 DNS。HTTP 流量可以直接追踪 TCP 流,看有没有上传/下载的文件;DNS 流量要小心 DNS 隧道攻击,大量 DNS 查询请求的子域名可能就是编码后的数据,把子域名提取出来用 base64 解码即可。
还有一类流量题会给一个 FTP 或 SMB 的对象,导出 -> 筛选 -> 分析文件内容。我习惯在 Wireshark 里用 导出 HTTP 对象 和 导出 SMB 对象 这两个功能直接拿到传输的文件本体,而不是逐个包翻。
5.3 压缩包攻击:伪加密识别与明文攻击
压缩包相关题目主要考察三种攻击方式:伪加密、暴力破解、已知明文攻击。
判断伪加密的方法很简单:用十六进制编辑器打开压缩包,找到加密标志位。ZIP 格式中,文件头里的 general purpose bit flag 位,如果第 0 位是 1 表示加密,第 3 位是 1 表示使用了数据描述符。有时候出题人只是把加密标志位改成了 1,但并没有真正加密数据,这时候用 ZipCenOp.jar 或直接手动把标志位改回来,就能无密码解压。这一招能直接拿下大量"伪加密"签到题。
如果是真加密,接着试弱密码。CTF 里压缩包密码基本都是六位纯数字或常见英文单词,fcrackzip 或 John the Ripper 配上弱密码字典跑一轮,通常几分钟内就能出结果。已知明文攻击的利用条件是你知道压缩包里某个文件的内容,直接压缩一个相同的文件,用 bkcrack 就能还原出加密密钥,从而解出全部文件。这个攻击在很多比赛里都成功率高得出奇——出题人放一个 readme.txt 当作干扰文件,结果这个文件本身就成了破解的突破口。
5.4 编码与隐写综合体:先识别,再解码,最后看结构
Misc 压轴题往往是"套娃"结构:一层编码套一层编码,一层隐写嵌一层隐写。处理这种题,我的经验是永远保存中间结果。
解出一层之后先确认结果的格式,像是 base64 的 = 结尾特征、十六进制的 0x 前缀、URL 编码的 %、摩斯码的点横组合。每一步都用脚本记录原始输入、中间输出、编码类型。套娃题最怕的就是"解错了还回不去"——你以为这层是 base64,解完发现是乱码,但已经不知道原输入长什么样了,后面就全乱了。
我还会在套娃解题时留一个"简化解法"的思路:有些比赛题看似复杂,其实解码到最后一步会得到一个二维码、一个 flag{...} 字符串,或者一个特定的文件名。只要坚持"编码识别、解码、格式检测"三步循环,绝大多数套娃题都能走通。
6. 组合题型与赛场时间管理
说完各题型的具体思路,最后聊一个从未写进任何 Writeup、但在实际比赛中性价比极高的话题:怎么分配时间。
一场 CTF 比赛通常只有 12 到 48 小时,你的目标不是解出所有题,而是在规则内拿到尽可能多的分。我的策略很简单:先花 30 分钟把所有题目的附件全扫一遍,用 strings 和 file 做信息收集,把"送分题"单独列出来;先做这些,拿保底分。送分题做完后,再按分值从高到低排序,在每道题上给自己设置一个时间上限——Web 题 40 分钟、逆向题 1 小时、PWN 题 1.5 小时,超时立刻换题。
组合题的判断也很重要:如果一个题目既给了一个 .pcap 文件,又给了一段加密数据,大概率是"先流量分析提取出密钥,再解密数据"。这种题在拆解时,要先找出数据链路的主干,别一上来就晕在细节里。我的习惯是先看文件格式和文件名,很多题目的提示都藏在文件名里,比如 key.png、encrypted.zip、hint.txt 这种命名,本身就是解题思路。
还有一个小技巧:比赛时建立一个 "flag 收集表"。每一道题解出之后,把 flag 格式、题号、解题思路摘要记下来。这不是为了给别人看,而是为了防止你重复劳动——有时候你在某道题上踩了坑,改了方向,结果回来看表才发现之前已经快解出来了,只是差最后一步。
7. 从比赛中沉淀下来的个人建议
打了几年比赛,我最大的感触是:CTF 解题思路的 "100 个" 其实不是 100 个孤立技巧,而是几十套可迁移的思维模型。 你学会了 LSB 隐写,再遇到音频频谱题时,就会自然想到"隐写不一定在图像里,任何能承载数据的媒体都行";你学会了 ROP 链,再遇到一个看起来完全陌生的漏洞利用题,会先问自己"它是不是在某个层面还是栈溢出"。
所以我的建议是:准备一个私人笔记本,每做完一道题,写下三句话——"这道题考的是什么""我的最初思路是什么""最优解法和我思路的差距在哪"。比赛结束后的复盘,比比赛本身更能提升水平。到下一场比赛前,翻翻这个笔记本,比你临时抱佛脚刷一天题有用得多。
如果这篇文章里有哪句话值得你记住,我希望是这句:CTF 考点的是"识别"而不是"创造"。 出题人不会凭空发明一种你从没见过的东西,他只是把已知的套路换了一层壳。你提前见过这个套路,你就赢了一半;你提前准备好了应对脚本,你就赢了另一半。把上面这些思路吃透,赛场上你大概率会发现:"这题不就是某个已知套路的变体吗?"——到那时候,你已经在心态上领先对手了。
