不知道你是不是跟我一样,刚开始刷 BUUCTF 的时候,Web 分区第一页总能看到一个叫[极客大挑战 2019]Havefun的题目。题目名字像是来玩的,实际做起来也确实有“玩”的成分。它不考 SQL 注入、不考 XSS、不考反序列化,甚至连一个输入框都不给你,唯一要做的事就是:打开页面、看源代码、往 URL 里塞参数。
这道题我会反复推荐给 Web 方向的新手,就是因为它足够简单,但信息收集和 GET 传参这两个基础动作贯穿了后面所有 Web 题。这篇文章我把它完整拆开讲,从启动靶机到提交 flag 的一整个过程都会写清楚,顺手把我当年踩过的坑也一并交代了,保证零基础也能跟着做完。
1. 赛题初探:从拿到靶机地址开始
1.1 这题在 BUUCTF 里的定位
极客大挑战 2019 是某高校网络安全社团主办的一场 CTF 比赛,BUUCTF 平台收录了其中的部分题目,按方向分在 Web 分类下。“Havefun”在这个分类里属于那种星级很低、几乎“点击就送”的入门题,序列号也靠前。它的定位就是告诉新入坑的人:Web 题不一定都是复杂漏洞,很多时候只是线索藏在你看得见却容易忽略的地方。
题目考察的核心能力就两个:一个是源码审计意识,也就是打开页面的第一反应不是瞎点,而是先看清客户端能获取到哪些信息;另一个是HTTP 参数传递的基础,知道 URL 问号后面那串东西是怎么组装的、服务器怎么接收。这两点在后续的 SQL 注入、文件包含、命令执行题目里都会被反复用到,所以不要因为题目简单就跳过,地基打牢比什么都重要。
1.2 启动靶机与做题环境准备
在 BUUCTF 平台上进入题目页面后,右侧能看到一个“启动靶机”的按钮,点一下等待几秒钟,平台会给你一个动态生成的地址,一般长这样:
http://123.xxx.xxx.xxx:12345注意这个地址是带端口的,说明题目跑在一个临时容器里。我遇到过靶机启动失败的情况,多半是平台负载高或者容器启动超时,等几秒重新点一下,或者刷新页面再看就行。还有一点要记住:靶机环境可能因为长时间无操作而自动关闭,重新启动后地址会变,但题目和 flag 本身是固定的。
做题前建议准备几样东西:
- 一个 Chrome 或 Edge 浏览器,F12 开发者工具要顺手;
- HackBar 插件或者直接用地址栏改参数,新手不装插件也完全能玩;
- 如果你愿意,提前装一个支持 curl 的终端环境,Windows 用户直接用 PowerShell 或 CMD 就行。
说实话,这道题连 Burp Suite 都不用开,浏览器加终端足够。
1.3 打开页面第一眼看到什么
把靶机地址复制到浏览器,回车之后,页面上出现了一只猫,旁边配着一句简短的文字,画面很朴素,没有导航、没有输入框、没有跳转链接。很多新手到这里会蒙圈,心里想:这个题目让我干嘛?
这种状态非常正常,我带的很多学弟学妹做第一道 Web 题时都在这一步卡住过。他们习惯性地认为网站一定有“登录框”或者“提交按钮”,但 Web 安全的切入点恰恰是那些看起来没什么功能的页面。页面越简单,越说明问题不出在功能上,而是出在信息泄露上。到这一步,你只需要记住一件事:按 F12。
2. 信息收集是 Web 题的第一步
2.1 为什么第一步永远是看源码
你可以把浏览器理解成一个“翻译官”:服务器把一段 HTML 文本发给它,它负责把这堆文本渲染成好看的页面。可这段 HTML 文本本身是完整传到你电脑上的,里面写了什么,你全都看得到。很多出题人喜欢把线索藏在 HTML 注释、隐藏表单、JS 变量甚至响应头里,就是赌你不会去看。
这道“Havefun”正是典型的“源码泄露”思路。它没有用后台目录扫描、没有用 Git 泄露,而是直接把一段 PHP 逻辑的提示丢在 HTML 注释里。如果你连源码都不看,那这道题就永远做不出来;一旦你打开了源码,答案几乎是摆在脸上的。所以我在给学生讲 Web 入门时反复强调:所有客户端可见的内容,都可能是服务端故意或者不小心给你的线索,不要放过任何一个字符。
2.2 F12 开发者工具的三种打开姿势
查看源码这件事,方法不止一个,我建议你全部掌握:
- 在页面空白处点右键,选择“查看网页源代码”,浏览器会新开一个标签页显示服务器返回的原始 HTML;
- 直接按
Ctrl + U,效果和上面一样; - 按
F12打开开发者工具,切到 Elements(元素)面板,也能看到 DOM 结构。
前两种拿到的是“原始 HTML”,第三种拿到的是浏览器解析后的 DOM。对于查看注释这种需求,Ctrl + U比 F12 更清爽,因为原始 HTML 里不会混入动态脚本生成的节点。打开之后,立刻按Ctrl + F搜索关键词,我习惯先搜flag、再搜cat、最后搜注释符号<!--。新手往往在这一步就能找到突破口。
2.3 注释里到底藏了什么
在源码的末尾附近,你会看到这样一段被 HTML 注释包裹着的文字,简化后的核心逻辑类似:
$cat = $_GET['cat']; if ($cat == 'dog') { echo $flag; // 或者是包含某个文件后输出的内容 }不同版本复现这道题时,代码细节会有出入,有的版本是先include 'flag.php';再判断,有的版本是直接把 flag 变量输出,但核心意思一致:它接收一个名为cat的 GET 参数,把参数值和字符串dog做比较,如果相等,就输出 flag。
这里有一个特别关键的认知:出题人不是把完整的服务端源码发给你,而是故意在 HTML 注释里留了一段提示性的代码片段。它看着像“废码”,实际上就是解题说明书。你要做的不是重新实现这段逻辑,而是按照它的规则去“喂”参数。
3. 读懂这段“废码”:PHP 变量与 GET 传参
3.1 把半截代码在脑中补全
我们来看那段注释代码到底在说什么。虽然你看到的只是几行 PHP 片段,但它背后对应着服务端一个完整的文件,大概率长这样:
<?php $flag = "flag{some_string}"; $cat = $_GET['cat']; if ($cat == 'dog') { echo $flag; } ?>页面本身没有显示这段 PHP,因为服务器只把执行结果和注释输出到了浏览器。你看到的那只猫和那几句话,是 PHP 代码执行后拼接出来的 HTML。但服务端逻辑是存在的,它正在等待你传递一个符合要求的参数。
看懂逻辑之后,题目就很清晰了:这个页面定义了一个“暗号”,暗号是dog。只要你在请求的时候告诉服务器cat这个参数的值是dog,服务器就会把 flag 当作奖励吐出来。
3.2 GET 参数是什么
GET 参数是 HTTP 请求中最常见的数据传递方式,它的载体就是 URL。一个标准的带参数 URL 长这样:
http://123.xxx.xxx.xxx:12345/?cat=dog“?”后面是查询字符串,cat是参数名,dog是参数值。如果还有第二个参数,就用“&”连接,比如:
http://123.xxx.xxx.xxx:12345/?cat=dog&name=admin在 PHP 里,$_GET是一个超全局变量,它会把 URL 查询字符串里的键值对自动组装成数组。你传了cat=dog,那么$_GET['cat']的值就是字符串dog。整个流程就像你去食堂打饭:URL 的路径部分是“去哪个窗口”,问号后面是“告诉阿姨你要什么菜”。
3.3 为什么是 dog 而不是其他
这个问题听起来有点傻,但它是理解这个题的关键。因为注释代码里的判断条件是$cat == 'dog',是固定的。CTF 解题不是猜谜,而是读懂规则之后精准执行。这里没有爆破的必要,你不需要试cat=cat、cat=dog1,直接按照代码要求传cat=dog就完了。
当然你可以顺手做几个小实验,把值改成Dog、DOG或者dog(带一个空格),大概率都得不到 flag。原因很简单:PHP 的==虽然比较宽松,但字符串之间的值比较依然要求内容一致,大小写和空格都算差异。这里也埋了一个伏笔:将来你会接触到 PHP 弱类型比较的各种坑,比如'123' == 123这种,但现在完全不用纠结,先把cat=dog传上去。
4. 实战拿 flag:三种传参姿势
4.1 地址栏直接改 URL,最快的方式
在浏览器地址栏里,在原始靶机地址后面加上?cat=dog,然后回车:
http://123.xxx.xxx.xxx:12345/?cat=dog回车之后,页面会发生变化。根据平台复现版本的不同,有的页面会直接输出一长串 flag,有的页面表面上看起来没变化,但你再查看一次页面源码,flag 已经出现在响应内容里了。
我自己做这道题时,返回的结果是一段类似于:
flag{xxxxxxxxxxxxxxxxxxxxxxxx}的字符串,具体字符集和格式以你实际拿到为准。这里有个经验:不要只盯着页面渲染结果,返回的 HTTP 响应体里只要多出来的字符串都值得看两眼。如果你用 F12 打开 Network 面板,刷新请求,同样能在响应内容里看到 flag。
拿到 flag 之后,复制它,回到 BUUCTF 题目页面,找到“提交 Flag”输入框,粘贴进去,点击提交,页面提示正确,得分到账。
4.2 用 HackBar 构造请求,比手打 URL 更专业
浏览器地址栏直接改参数虽然快,但体验不太好,尤其是参数多了之后很容易改错。我更推荐装一个 HackBar 插件。老版 HackBar 在 Firefox 上用得顺手,新版 HackBar 在浏览器里需要先开启配置才能显示输入框,操作路径大约是在插件面板里勾选 Enable HackBar。如果你用的浏览器商店里找不到,可以直接搜一下离线安装方法,或者换 Firefox 版本。
用 HackBar 时,先把靶机地址粘到输入框,点击 Execute 执行 GET 请求,再修改 URL,用同样的方式执行,这样可以快速对比不同参数带来的响应差异。后面你做题做到 SQL 注入、XSS 的时候,会发现有一个能快速改请求、快速重放的工具太重要了。
4.3 用 curl 在终端完成整个流程,顺手养成习惯
我觉得这道题值得你多花三十秒,尝试用命令行来做。把靶机地址放在 curl 命令里:
curl -i "http://123.xxx.xxx.xxx:12345/?cat=dog"加-i参数的意思是显示响应头,因为有些题的线索会藏在响应头里。如果你想直接过滤出 flag,可以加个管道:
curl -s "http://123.xxx.xxx.xxx:12345/?cat=dog" | grep -o "flag{[^}]*}"-o是正则提取,只输出匹配到的 flag 字符串。这个习惯我会建议所有新手刻意练习,因为在 BUUCTF 后面的题目里,你需要频繁地发送请求、查看响应,图形界面没法做到每次都清晰可追溯,而终端里一条命令就能完成测试。
这里有个小坑要提醒:URL 里带着&的时候,在 Linux/Mac 的终端里一定要用双引号把整个 URL 包起来,否则 shell 会把&当成“把命令放到后台执行”的符号,导致请求发出来只是半个 URL,排错的时候很容易把人搞疯。
4.4 提交 flag 的正确姿势与常见翻车点
flag 拿到手,最后一步提交也翻车过不少人。BUUCTF 的 flag 提交框一般在题目详情页下方,提交的时候要注意:
- 格式必须完整,
flag{...}的前缀和后缀大括号都不能少; - 不要有多余的空格、换行,哪怕看起来是一样,粘贴时可能把换行符带进去;
- 确认题目的 flag 格式。BUUCTF 上统一以
flag{}包裹为主,但如果不确定,看响应里那个字符串原样复制即可。
我当年做这道题的时候,第一次提交就提示错误。我当时以为是自己参数传错了,反复试了几次还是不对,最后才发现是复制出来的字符串尾巴上多了一个空格,平台把它当成了 flag 的一部分。这种问题完全没有难度,但非常容易遇到,也算这道题送给新手的第一个“心态测试”。
| 常见现象 | 可能原因 | 解决办法 |
|---|---|---|
| 页面没有变化 | 参数没拼接对 / 靶机过期 | 检查 URL 是否包含?cat=dog,重新启动靶机再试 |
| 提交 flag 显示错误 | 复制的值带了空格、换行或缺失前缀 | 清空输入框,重新复制粘贴,肉眼确认格式 |
| 源代码里找不到注释 | 查看的不是原始 HTML | 用Ctrl+U查看网页源代码,不要只盯着 F12 的 Elements |
| 靶机打不开 | 容器启动失败 / 平台负载高 | 等几十秒后重新启动,或刷新页面 |
5. 从 Havefun 延伸:同类问题与入门学习路线
5.1 这类“注释泄露 + 参数触发”题目的套路
“Havefun”这道题最大的价值,是帮你把 Web 入门最重要的思维模式立起来:先收集信息,再构造请求。顺着这个思路,你会发现很多题都是同一套模板:
- 注释里提示参数名,让你传某个值触发逻辑;
- 注释里提示隐藏文件或者备份文件,你能顺着路径去访问它;
- JS 文件里暴露了接口地址,你直接对接口发起请求;
- 响应头或 Cookie 里藏着一个用于后续步骤的 token。
这些题目表面上各不相同,但解题节奏惊人地一致。比如后面常见的“备份文件泄露”类题目,从index.php.bak里能下载到源码;再比如“git 泄露”类题目,从.git目录能还原出全部代码逻辑。它们本质上都在说同一句话:服务端没藏好的信息,就是你解题的钥匙。
5.2 一道题牵出的知识清单
做完“Havefun”,我要帮你把知识点落到纸面上,别让这道题变成“我做过、我忘了”。它至少涉及四个层面的基础:
- PHP 层面:超全局变量
$_GET的用法,echo的输出机制,==比较运算符的语义,以及将来会遇到的==和===的区别、弱类型比较陷阱; - HTTP 层面:URL 的组成结构,查询字符串的格式,GET 请求和响应模型的完整过程,状态码与响应头基本含义;
- 工具层面:浏览器查看源代码、F12 Network 面板、HackBar、curl 命令行的基本用法;
- 思路层面:信息收集优先、参数可控意识、不放过任何响应内容。
我给新手的学习路线建议是这样的:先刷二十道 BUUCTF 的 Web 入门题,不用追求每道都独立做出来,但每一道题都要弄懂触发点和原理;然后去系统补一遍 PHP 基础语法和 HTTP 协议;最后再进入 SQL 注入、XSS、文件上传这些常规漏洞方向。为什么这么排?因为后面学 SQL 注入,你要能看懂代码里的拼接逻辑;学文件包含,你要能识别出参数里可以控制的路径。没有代码审计的基本功,你连注入点都找不到,更别说利用。
5.3 给 CTF 新手的几条实在建议
最后分享几条我带新手刷题时的经验,每一条都是不少人栽过跟头之后才总结出来的:
- 不要背题。知道这题传
cat=dog没有意义,你要理解的是“为什么这里有参数可控点、为什么参数值匹配的是 dog、如果不匹配会发生什么”。把题目背后的原理想通了,换一道变体你也一样能做。 - 建一个自己的 writeup 模板。我建议用笔记软件记录每道题:漏洞点、触发条件、payload、修复建议。不要直接复制别人的题解,自己整理一遍才是你的东西。
- 看题解不丢人,丢人的是看完不复现。很多新手看完别人的解法觉得“我会了”,第二天再做一遍还是卡住。正确做法是看完题解后关掉它,自己从零复现一遍,卡住了再回去看。复现一遍比看十遍都管用。
- 养成用 curl 的习惯。你用鼠标点十次页面,不如用 curl 发一次请求更能理解 HTTP,而且在后续做题和未来工作中,命令行是你甩不掉的基本功。
我个人在刚接触 CTF 时,是很不屑于做这种一星题的,觉得没技术含量。后来刷到后面才发现,“Havefun”给我打下的基础——遇到任何页面先看源码、任何输入都尝试变成请求参数、任何响应都别放过——让我在后面的 SQL 注入和反序列化题目里少栽了很多跟头。所以如果你现在刚接触 Web,别急着追求高端漏洞,先把这道题吃透,它会是你之后解题的一块好用的垫脚石。