1. 内容整体设计与思路拆解
1.1 FLAG 为什么是 CTF 的“终极目标”
CTF(Capture The Flag,夺旗赛)的核心玩法很简单——题目里藏着一个字符串,叫 FLAG,你把它找出来、提交上去,就能得分。比赛排名看的就是谁能更快、更稳地把这个字符串“薅”出来。某种程度上,FLAG 就像一场寻宝游戏里的宝藏坐标,解题过程就是你根据各类线索一步步逼近坐标的过程。
很多刚接触 CTF 的朋友容易走进一个误区:以为 FLAG 一定藏在某个固定的、显眼的地方。实际上,FLAG 的藏法千奇百怪——可能就在网页源码的注释里,可能在流量包某个不起眼的 TCP 流里,可能藏在图片的 EXIF 信息里,也可能需要你修复一个损坏的压缩包才能看到。我打了几年 CTF,最大的体会是:与其漫无目的地刷题,不如先把“FLAG 都藏在哪些地方”这个问题彻底摸清楚。
这篇训练笔记是系列第五篇,打算专门把“获取 FLAG 的常见方法”系统整理一遍。我按 CTF 最常见的几个方向——Web、杂项、隐写、逆向与 PWN——把典型的拿 FLAG 路径拆开讲,每一条方法都会结合真实题目场景说明“为什么这么找”。由于内容量确实大,这篇先整理到一半,后续实战中遇到新思路我会继续补充。
1.2 拿到题目后的第一反应:判断 FLAG 的藏匿维度
我见过不少人在题目上耗了几个小时,最后发现 FLAG 其实就在眼皮底下。问题往往出在“一开始就没判断清楚 FLAG 的藏匿维度”。这里我说的“维度”指的是:FLAG 到底藏在数据文件里、藏在协议交互里、藏在程序运行逻辑里,还是藏在某个加密/编码的变形中?
判断维度的方法其实很朴素——先看题目类型和题目描述。Web 题目的 FLAG 通常在服务端,需要通过漏洞拿到;杂项题目的 FLAG 通常在给出的附件中,需要通过拆解文件、分析隐写、解码等方式提取;逆向和 PWN 题目的 FLAG 往往在你成功利用程序漏洞或逆出算法后,由远程服务直接回显;密码学题目的 FLAG 则大概率藏在密文背后,需要你识别加密方式并还原明文。
这个“第一反应”特别重要。因为在 CTF 里,效率就是分数。你花 30 秒判断出题方向,就能少走好几个小时的弯路。举例来说,如果一道题给了你一个压缩包,而且题目描述里写着“小明的生日”,你的第一反应不应该是去暴力破解压缩包密码,而应该去尝试生日相关数字组合,同时检查压缩包是不是“伪加密”——这两个方向里,伪加密的检测优先级更高,因为它能让你在不知道密码的情况下直接解压。
2. 核心细节解析与实操要点
2.1 Web 方向拿 FLAG 的常见入口
Web 方向的 FLAG 获取,本质上就是“找到服务端程序的薄弱点,让程序帮你把 FLAG 吐出来”。这里我不谈太深奥的利用链,只聊几个出现频率极高的入口,也是我在实战中反复见到的。
2.1.1 命令执行漏洞:FLAG 就在 shell 的回显里
命令执行类题目的典型场景是:代码里某个参数被拼接到系统命令中,没有做过滤,于是你可以在参数里注入额外的命令。常见的函数有system()、exec()、shell_exec()、passthru()等。PHP 里还有一个容易被忽略的passthru(),它和system()很像,但会把原始输出直接送到浏览器,一般用于输出二进制数据(比如图片),很多题目偏偏就爱用它。
拿到命令执行漏洞后,第一步是确认你能执行什么命令。我通常先提交一个无害的命令,比如id或whoami,看回显。接下来就是找 FLAG 的过程。FLAG 文件名没有统一标准,常见的有flag、flag.txt、flag.php、f1ag、flag_is_here等,所以我会直接执行一个组合命令:ls -la; find / -name "*flag*" 2>/dev/null。
这里有个细节:很多题目环境是 Docker 容器,find命令不一定存在。如果find不可用,就老老实实用ls -la /、ls -la /var/www/html、cat /flag这种逐个排查的方式。还有一种情况是命令执行结果被截断了,只显示前面几行,这时可以用cat /flag | base64,把内容编码后输出,避免特殊字符干扰显示。我在一些题目里遇到 FLAG 中含有空格和特殊符号,直接cat会被程序解析出错,用 base64 编码输出再本地解码,几乎不会失手。
命令执行类题目还有一个常见考点:绕过过滤。如果程序过滤了空格,你可以用${IFS}代替;如果过滤了/,可以尝试$(echo ${PWD} | cut -c1)拼出斜杠。这些绕过技巧不是死记硬背就好,关键是理解过滤的“位置”——是只过滤了 GET 参数,还是连 POST 也过滤了?是只过滤了某个字符,还是没有过滤编码后的字符?搞清楚这些,才能判断该用哪套绕过方案。
2.1.2 文件包含与 PHP 伪协议
文件包含漏洞(File Inclusion)也是 Web 方向的常客。核心原理是程序通过参数动态加载文件,但没有对用户输入做严格限制,导致你能读服务器上的任意文件。这里最常用的“武器”是 PHP 伪协议。
场景举例:URL 是index.php?page=about,你改成index.php?page=php://filter/read=convert.base64-encode/resource=flag.php,就能把flag.php的源码以 base64 编码的形式读出来,再解码拿到 FLAG。
为什么用php://filter?因为直接page=flag.php时,如果目标文件是 PHP 文件,服务器会先执行它而不是显示源码,FLAG 可能已经被程序“藏起来”了。用 base64 编码的方式读取源码,可以绕过这种“执行后输出”的逻辑。这个技巧在我接触的题目里出现频率非常高,值得反复练习。
文件包含还有几个变体:data://协议可以让你把一段纯文本或 base64 编码的 PHP 代码作为数据流包含进来,直接执行;php://input则可以把 POST 请求体当作代码执行;expect://协议在部分环境下能直接执行系统命令。每种协议的环境要求不同,遇到题目时最好先确认 PHP 版本和可用的协议列表。
2.1.3 直接看源码:FLAG 可能根本不需要“打”
很多人一上来就想着“攻击”,反而忽略了最简单的方式——打开网页右键查看源码。CTF 里大量入门题的 FLAG 就在 HTML 注释里、JS 代码里、或者藏在robots.txt中。
robots.txt是很多新手容易忽略的文件。它本来是给搜索引擎爬虫看的文件,说明哪些路径可以爬、哪些不可以。但 CTF 出题人经常把 FLAG 藏在被 Disallow 的路径后面,你在浏览器里访问robots.txt,就能看到类似Disallow: /flag_here.html的提示,跟着路径走,FLAG 到手。
另一个容易被忽略的是.git目录泄露。有些开发者在部署网站时把.git文件一起传上去了,导致整个代码仓库可以被下载。题目场景常见的是git clone后的网站目录里存在.git,直接访问http://target/.git/得到目录列表。我通常会用GitHack这类工具把源码拉下来,然后通过git log、git diff查看历史提交记录——很多出题人会故意在历史版本里删掉 FLAG,但你依然可以从 commit 记录里翻出来。
2.2 杂项(Misc)方向拿 FLAG 的典型套路
杂项在我心里是 CTF 里最 “不讲武德” 的方向,因为它什么都能考——文件分析、编码转换、流量分析、内存取证、压缩包破解、隐写术…… 但也正因为它杂,所以掌握基础套路后,拿分效率往往比 Web 更高。
2.2.1 压缩包处理:伪加密与暴力破解
压缩包是杂项题目的“常客”,FLAG 经常被塞进一个 zip 文件里。拿到 zip 后,第一反应不是去解压,而是先看它的加密状态。
这里有一个非常关键的细节:zip 文件可能是“伪加密”。所谓的伪加密,是指 zip 的加密标志位被篡改过,资源管理器或者部分解压工具会误认为它加密了,要求你输入密码。但实际上文件内容并没有真正加密,你只需要用工具纠正标志位,就能直接解压出里面的文件。
判断伪加密的方法不复杂。用 010 Editor 或 Hex Fiend 打开 zip 文件,找到中央目录区(Central Directory)中对应文件条目里的“通用位标志”(General Purpose Bit Flag),如果第 0 位(数值 1)为 1,表示文件是加密的。把这一位改成 0,保存后重新解压,如果成功,就说明是伪加密。工具层面,ZipCenOp.jar可以直接一键修复伪加密,这条命令我用了无数次:java -jar ZipCenOp.jar r flag.zip。
如果 zip 确实是真加密,那就需要考虑暴力破解或字典攻击。这里要注意:不到万不得已不要直接上全字符暴力破解,除非题目描述明确告诉你密码很短。比较高效的思路是结合题目描述猜密码,比如“小明的生日”就围绕生日数字组合做字典;如果附件里有一个看起来像提示的图片或文本,优先在提示里找线索——密码很可能是某个英文单词、某个年份,或者某段歌词的拼音首字母。
2.2.2 流量包分析:FLAG 藏在协议交互里
流量包分析是杂项题目的重头戏。附件通常是一个.pcap或.pcapng文件,里面记录了某段时间的网络通信数据,FLAG 可能是明文出现在某个包的数据部分,也可能需要你重组 TCP 流才能看到。
Wireshark 是我做流量分析的主力工具。拿到 pcap 文件后,我一般按三步走:
第一步,看协议统计。通过Statistics -> Protocol Hierarchy,快速判断这个流量包里的主要协议是什么。如果 HTTP 流量很多,FLAG 大概率跟网页请求/响应有关;如果全是 TCP 且没有应用层协议,那就需要追踪 TCP 流;如果有 DNS 查询流量,也不要放过,因为有的题目会把 FLAG 拆成多段藏在 DNS 查询的域名里。
第二步,过滤关键词。Wireshark 支持直接在过滤器里搜字符串:frame contains "flag"或者tcp contains "FLAG"。这个方法虽然简单粗暴,但往往是最快的。很多入门题目就是把 FLAG 以明文方式放在某个包里,一过滤就出来了。
第三步,追踪 TCP 流。如果 FLAG 不是直接在包里,那就得右键任意一个 TCP 包,选择“追踪 TCP 流”,把整个会话的数据都看一遍。我遇到过 FLAG 被分在两个请求里,一个请求发送了前半段,另一个发送了后半段,需要把流内容拼起来才能看到完整值。
流量包里还有一种常见情况是“上传/下载文件”,比如通过 HTTP 传了一个压缩包或者图片,FLAG 就藏在这个文件里。这时候可以用 Wireshark 的File -> Export Objects -> HTTP,直接把传输的文件导出,再离线分析。
2.2.3 隐写术:图片、PDF、音频里的玄机
隐写术(Steganography)是把我认为“最抓狂也最有趣”的方向,因为它的核心是“藏”,而你要做的就是“找”。找的线索可能隐藏在图片的像素里、声音的频谱里,或者 PDF 文件的元数据里。
图片隐写是最常见的。FLAG 可能直接写在图片的 EXIF 信息里,用exiftool就能查看;可能附加在图片文件末尾,用strings命令就可以发现可疑字符串;也可能用 LSB(最低有效位)隐写,把 FLAG 编码在图片像素颜色的最低位上,人眼看不出区别,需要用zsteg或StegSolve这类工具提取。
PDF 隐写比较冷门,但也出现过。我的习惯是先用 PDF 阅读器打开看一遍有没有明显的文字遮挡;再用pdf-parser.py检查 pdf 对象结构,看是否有隐藏的文本对象或注释;最后用strings直接扫 PDF 文件里的不可见字符串。有些题目会把 FLAG 以全白色文字塞在 PDF 页面里,你肉眼看不见,但全选复制就能发现。
音频隐写的常用思路是看频谱图。用 Audacity 打开音频文件,切换到频谱图视图,有时候 FLAG 会被绘制成一段文字图案藏在频谱中。这种情况用耳朵是听不见的,但眼睛一看就懂。
3. 实操过程与核心环节实现
3.1 从零开始做一道命令执行题:完整解析
这里我以一个典型的命令执行漏洞题为例,演示完整的解题链。题目页面是一个简单的 IP 查询工具,输入 IP 地址,后端执行ping命令,代码大概是这样的:
<?php $ip = $_GET['ip']; system("ping -c 3 " . $ip); ?>首先,测试是否存在命令注入。在输入框提交127.0.0.1,页面正常回显 ping 结果。接着提交127.0.0.1; whoami,如果页面上出现了当前用户信息,比如www-data,说明命令注入成功。
注意这里用的是分号;,因为分号可以让多条命令顺序执行。如果把分号过滤了,还可以尝试换行符%0a、管道符|、逻辑与&&、逻辑或||。每种符号的效果不同:|只显示后一条命令的输出,||在前一条命令失败时才执行后一条,&&是前一条成功才执行后一条。根据实际回显判断过滤规则,再选择最合适的连接符。
拿到命令执行权限后,执行:
ls -la /; cat /flag 2>/dev/null; find / -name "*flag*" 2>/dev/null如果 FLAG 文件就在根目录下且名为flag,直接cat /flag就结束了。但如果页面把命令输出限制在某个区域,或者只显示前几行,那就需要换一种输出方式。我的习惯是把 FLAG 内容写入一个 Web 目录下的临时文件,再通过浏览器访问。比如:
cat /flag > /var/www/html/flag.txt然后直接在浏览器打开http://target/flag.txt。这个方法在命令执行漏洞利用里非常实用,因为它绕开了命令行输出的各种限制。
3.2 流量包分析实战:从 pcap 到 FLAG
某次训练赛中遇到一个流量包附件,大小约 30MB。打开 Wireshark 后,第一眼看到的是大量 HTTP 流量,这让我提高了警惕——通常有大流量,里面多半藏着文件传输或可疑请求。
我按前面说的三步走。先看协议统计,HTTP 请求数量确实很多,于是直接File -> Export Objects -> HTTP,把所有 HTTP 传输对象导出。导出的文件里有一个 ZIP 压缩包,名字很可疑:secret.zip。
用 7-Zip 打开这个 ZIP,提示需要密码。此时我先检查加密标志位——用 010 Editor 打开,发现通用位标志中的第 0 位是 1,看起来确实是加密的。但出于谨慎,我还是先尝试用ZipCenOp.jar修复伪加密:
java -jar ZipCenOp.jar r secret.zip修复后重新解压,ZIP 成功解压出了一个flag.txt,打开就是完整的 FLAG。
这道题的坑点在于:破解密码会浪费大量时间,但实际上它只是个伪加密。所以再次强调:拿到任何加密压缩包,第一步永远是“检测伪加密”,而不是埋头跑字典。
3.3 图片隐写分析:用 strings、exiftool 和 zsteg 三板斧
图片隐写题目我做了很多,积累了一套固定流程。
第一板斧是strings,直接扫描图片中所有可打印字符串。如果 FLAG 以明文方式附加在图片末尾,strings会直接输出它。比如:
strings cat.jpg | grep -i flag第二板斧是exiftool,查看图片的元数据。FLAG 可能藏在作者、版权、描述等字段里:
exiftool cat.jpg第三板斧才是真正的“科学分析”,用zsteg检测 LSB 类隐写。如果图片是 PNG 或 BMP 格式,zsteg可以检测多种隐写方法:
zsteg -a cat.png这套流程覆盖了 90% 以上的图片隐写场景。如果这三板斧都没结果,再考虑更进阶的分析,比如把图片拆成多个色阶通道对比、查看不同位平面的差异图,或者用stegsolve手动滑动通道查看。不过,在动手做这些“重活”之前,一定要先确认前面的基础检查都做完了,因为大部分题目没有你想的那么复杂。
3.4 Git 泄露利用:从 .git 到源码再到 FLAG
题目环境是一个模拟的网站备份目录,访问http://target/.git/可以看到目录列表,说明存在 Git 泄露。用 GitHack 把源码拉下来:
python GitHack.py http://target/.git/拉取完成后,进入源码目录,执行:
git log --oneline git diff HEAD HEAD~1通过git log发现有两个提交记录,第二个提交的 message 写着 “remove flag”。这时候基本可以断定,FLAG 在第一个提交里。执行git show HEAD~1,就能看到之前提交版本中的flag.php文件完整内容,FLAG 就在里面。
这类题目的核心逻辑是:出题人把 FLAG 提交到了 Git 仓库,然后又删掉了,模拟的是开发者在解决完问题后提交了新的代码版本。但 Git 的设计决定了历史记录不会被真正抹去,任何被提交过的内容都可以从版本历史中找回。
4. 常见问题与排查技巧实录
4.1 遇到 403 报错怎么办
在 Web 题里,“做ctf网站老报403”是新手问得最多的问题。403 表示访问被拒绝了,可能的原因有三种:目录权限设置、WAF(Web 应用防火墙)拦截、文件本身不存在。
如果是目录权限问题,更换访问路径、尝试直接访问文件而非目录列表即可;如果是 WAF 拦截,那就要考虑对请求做变形,比如使用编码、大小写混写等方式绕过;如果文件本身不存在,服务器也可能返回 403 而不是 404,这是常见的安全配置策略,用来防止路径探测。
我的建议是:遇到 403,不要慌,先尝试访问一个肯定不存在的路径,看服务器返回什么状态码。如果返回 404,说明 403 只是针对某些特定路径的策略;如果也返回 403,那说明服务器对所有非法路径统一返回 403,此时可以放心大胆地在合法路径范围内继续找线索,不用过于在意那个 403。
4.2 压缩包破解跑不出密码怎么办
很多人一拿到加密压缩包就开始跑字典,结果跑了一夜都没结果。这里有几个排查点:
先确认是不是伪加密。这个问题前文反复强调过,但实际操作中我见过太多人忽略检测直接开跑,白白浪费时间。
再确认密码是否就在题目描述或附件名里。比如题目叫“helloctf小明的生日”,附件里有一张明显是生日日期的图片,那你就要围绕日期去生成一个精简字典,而不是用默认字典。
最后再考虑真正的暴力破解。如果确认密码是纯数字且位数不长,用fcrackzip指定数字范围跑;如果是一段有意义的话,建议用john配合规则字典。但说实话,如果这个环节过了 10 分钟还没结果,我通常会把题目重新读一遍,因为我碰到过很多情况是题目里其实写明了线索,只是我一开始没注意。
4.3 流量包太大,Wireshark 卡顿怎么办
流量包动辄几十 MB、上百 MB,用 Wireshark 打开和操作都会卡。这时候我建议分两步:
先用tshark做命令行层面的快速分析。比如想提取所有 HTTP 请求中的 URI,可以执行:
tshark -r capture.pcap -Y "http.request" -T fields -e http.request.full_uri想搜索包含 “flag” 字符串的包:
tshark -r capture.pcap -Y "frame contains \"flag\""命令行跑完后,你会对流量内容有大致概念,然后再过滤出小分量的包,用 Wireshark 做细致的交互分析。这个流程可以显著减少卡顿带来的烦躁感。
4.4 拿到内容但不是 FLAG:编码与格式判断
我遇到过一个很有趣的情况:费尽千辛万苦拿到了一个字符串,但提交上去报错。后来才发现,这个字符串是经过编码的,需要进一步解码才是真正的 FLAG。
判断是不是最终 FLAG 有几个依据:
看格式。CTF 的 FLAG 一般有固定格式,比如flag{...}、ctf{...}或者题目自定义的包裹格式。如果你拿到的字符串长得不像,就要考虑解码。
看字符集。如果看到的是一串 base64 字符(大小写字母和数字加+/),先尝试 base64 解码;如果是形如%XX%XX的内容,考虑 URL 编码;如果是\x开头的一串十六进制,考虑转 ASCII。
考试环境里常用“随波逐流 CTF 工具箱”这类工具做编码解码,覆盖的编码类型很全,包括 base64、URL、Unicode、摩斯密码、栅栏密码、凯撒密码等。我个人的建议是:别指望一次性找到正确编码,可以按概率排序,先试最常见的几种,再结合题目场景判断。
4.5 命令执行被过滤得很死怎么办
有些题目对命令执行的过滤做得比较完善,比如过滤了空格、过滤了/、过滤了cat等。这时候不要急着放弃,因为过滤永远有绕过空间。空间可以用${IFS};cat可以用tac、more、less、head、tail替代;如果过滤了关键词,可以在关键词中间插入反斜杠或拼接变量。
下面是一条常用的绕过命令:
a=c;b=at;c=/flag;$a$b $c先用变量拼出cat,再用变量拼出/flag,最后执行。这种方式在关键词过滤的场景里非常好用。还有一种是利用通配符:
/bin/?at /fl?g?能匹配任意单个字符,所以会实际执行/bin/cat /flag。如果连问号都被过滤了,试试*,但要注意*可能匹配过多文件导致命令出错。总之,命令执行的绕过本质上是“拼接 → 变形 → 再拼接”的过程,多试几种组合,总有一条路能走通。
5. 常用的工具清单与使用场景
工欲善其事,必先利其器。下面这个表格是我日常练习中的高频工具,按使用场景做了分类,可以直接收藏:
| 方向 | 工具名称 | 典型用途 |
|---|---|---|
| 编码解码 | 随波逐流 CTF 工具箱 | 覆盖常见编码、古典密码、凯撒、栅栏、摩斯等 |
| 压缩包 | 7-Zip / ZipCenOp | 压缩包管理、修复伪加密标志位 |
| 压力破解 | fcrackzip / john | zip 密码破解、字典规则生成和高级破解 |
| 流量分析 | Wireshark / tshark | pcap 可视化分析、命令行快速筛选、提取文件 |
| 图片隐写 | zsteg / exiftool | LSB 隐写检测、EXIF 信息提取、附加数据扫描 |
| 文件分析 | strings / binwalk | 扫描文件中可打印字符串、检测和分析嵌入式文件 |
| Web 辅助 | Burp Suite / hackbar | 抓包改包、Payload 构造、请求重放 |
| Git 泄露 | GitHack | 一键恢复 .git 泄露的源码树 |
| 十六进制 | 010 Editor / Hex Fiend | 查看二进制文件、手动修复文件头、分析加密标志位 |
| 综合平台 | CTFd / 动态靶场 | 搭建本地练习环境,熟悉比赛平台和提交机制 |
工具固然重要,但我不建议刷题一上来就疯狂装工具。很多时候,strings和grep就能解决问题。工具是把“可能性”变大的助手,而不是解题的核心。真正核心的是脑子里对“FLAG 可能在哪”的判断。
6. 最终提醒:拿到 FLAG 后的规范动作
最后分享一个非常实用的小习惯。很多新手千辛万苦拿到 FLAG 后,直接复制粘贴去提交,结果提交格式错误。这里有一个容易被忽略的细节:FLAG 必须包含完整的头尾标识,常见的格式是flag{...}、FLAG{...}、ctf{...},不要把flag{和}漏掉,也不要在 FLAG 两边粘贴多余的空格或换行。
另外,部分比赛的 FLAG 提交对大小写敏感,所以提交前要认真核对,不要全凭记忆敲打。如果题目要求的是flag{...},你只提交...的内容,系统会判错;同理,如果你把FLAG{...}写成了Flag{...},也可能提交失败。
还有一个是我在实战中养成的习惯:拿到 FLAG 后顺手截个图或者复制到本地笔记里。因为比赛中你可能会同时解好几道题,提交完就忘;赛后复盘或者写 writeup 的时候,这些记录能帮你快速回忆起每道题的解法。CTF 训练的价值恰恰在于复盘,而不是单纯追求当场解出的快感。
按我个人经验,CTF 解题能力的提升速度,其实跟“总结方法”这件事高度相关。我不太赞成纯粹靠刷题数量堆经验,因为题目变化无穷,但 FLAG 的藏匿逻辑是有规律可循的。这套“总结获取 FLAG 方法”的笔记,我会持续更新下去。下一篇预计会重点补充 PWN 和逆向方向的拿 FLAG 思路,以及一些高强度比赛中的选题策略。如果你在实战中碰到什么有意思的 FLAG 获取方法,欢迎一起交流。