☰
从BUUCTF签到题看CTF Web入门:源码注释与GET参数构造
2026/9/26 15:51:48 网站建设 项目流程

刚刷完BUUCTF的Web模块,有个现象特别有意思:真正让我觉得“CTF Web入门也就这么回事”的,不是那些需要各种骚操作的难题,反而是[极客大挑战 2019]Havefun这道签到题。题目名就叫Havefun,一眼看去页面也特别简单,但就是有新手在这卡住。这篇文章我会顺着完整解题过程,把从查看源码到构造GET参数这条链路讲透。无论你是第一次接触CTF,还是刚入Web安全想补基础,都可以把这道题当作镜子,看看自己有没有养成最基本的做题习惯。

这道题的答案网上已经有很多,无非就是查看页面源码,发现注释里有一段PHP代码提示,然后拼接/?cat=dog就能拿到flag。但我要聊的不只是这个答案,而是为什么答案长这样、源码注释里的逻辑怎么读、GET参数到底怎么构造、以及我刷题过程中亲眼见过别人踩进去的坑。把这些东西串起来,你才算真正“Havefun”了。

1. 题目背景与解题思路整体拆解

1.1 这题到底在考什么:信息收集才是Web第一课

[极客大挑战 2019]Havefun是极客大挑战2019比赛里的Web签到题,后来被BUUCTF平台收录,成了很多人的入坑第一题。题目本身没有任何难度,但它的出题思路非常典型:考的是“你会不会去看网页源码”。我刚拿到这道题的时候,页面就是一个非常干净的入口,没有登录框,没有上传点,没有明显的注入参数,乍一看好像什么都做不了。如果这时候只会盯着页面发呆,那大概率是要卡住的。

Web题和传统逆向题不一样,逆向题至少给你一个二进制文件让你逆,而Web题很多时候连攻击面都要自己找。题目不会主动告诉你“你去翻源码”,但所有线索其实都藏在你看得到的地方。HTML源码、注释、隐藏的input标签、响应头里的自定义字段、JS文件里的接口地址,这些都是Web信息收集的基础。Havefun这道题把这些基本功浓缩成了一个最简单的场景:查看页面源代码,找到藏在HTML注释里的PHP代码提示,再用URL传参满足条件。

所以我说它是“信息收集+参数控制”的组合题。用一个生活类比就是:门根本没锁,钥匙就放在门口地垫下面,出题人甚至在地垫上画了个箭头,但你必须养成“翻地垫”的习惯才会发现。Web安全里最怕的不是不知道高深技术,而是连送分的信息收集都没做全。

1.2 为什么很多新手卡在第一步:路径依赖和信息遗漏

我在不少CTF交流群里看到有人卡在这道题上,第一个反应是“这也太简单了”,第二个反应是想不通为什么有人会卡住。但后来我发现,卡住的人往往不是因为笨,而是因为之前的做题习惯太糟糕。他们拿到题目以后,第一反应是去扫目录、去测SQL注入、去试各种工具,唯独没有老老实实按F12看源码。工具用多了以后,很多人会形成路径依赖,总觉得所有题目都需要复杂攻击,反而把最基础的信息收集漏掉了。

另一个典型问题是不知道“源代码里的注释也能是提示”。很多新手看源码只看标签结构和文字内容,遇到<!-- ... -->这种HTML注释直接跳过,觉得注释只是给开发人员看的。但在CTF里,注释就是出题人的小纸条,有时候是路径,有时候是参数名,有时候干脆就是半个flag。从那以后我养成了一个习惯:无论页面多简单,先按Ctrl+U看完整源码,再按F12看网络请求,最后才考虑用什么工具去打。这个顺序放到今天依然管用。

还有一类新手卡住,是因为对HTTP的GET参数没有概念。看到?cat=dog不知道这是什么,也不知道往哪里填。这恰恰说明基础还不牢固,后面我把GET参数单独拿出来讲。

2. 核心细节解析:从源码注释到参数传递

2.1 源码注释里的PHP代码到底在说什么

这题的突破口在页面源码底部,有一行HTML注释,里面封装了一段PHP代码。不同版本的环境下代码可能略微不同,但核心逻辑基本是下面这个样子:

<!-- $cat = $_GET['cat']; echo $cat; if ($cat == 'dog') { echo 'flag{...}'; } -->

首先要明确一点,这段代码写在HTML注释里,浏览器只会把它当成普通注释显示,并不会执行,真正的服务端逻辑其实藏在另一个PHP文件里。出题人是故意把这个代码片段放在源码注释中,相当于给你递了一张纸条,告诉你后端大概是怎么判断的。所以我们看这段代码,看的不是它的语法,而是它透露出来的“判定条件”。

逐行解读一下。$cat = $_GET['cat'];意思是后端脚本会从URL的查询字符串中读取一个名为cat的参数。echo $cat;会把接收到的参数值原样回显到页面上,这既方便调试,也让选手能看到自己传了什么。if ($cat == 'dog')是关键条件:只要参数cat的值等于字符串dog,就会执行echo输出flag。所以这题的目标非常清晰:向脚本发送一个GET请求,让cat等于dog。

这里也顺带提一个PHP比较的小细节:代码用的是==,不是===。在PHP里==是宽松比较,===是严格比较。如果一边是数字,一边是字符串,==可能产生弱类型绕过问题。但这道题里$cat通过$_GET取到的是字符串,和字符串'dog'做==比较,逻辑上仍然需要传入dog这个值才能触发flag。至于弱类型比较的更多玩法,我会在后面的常见问题部分展开。

2.2 GET参数的本质:URL上的钥匙怎么拼

看到$_GET['cat']以后,接下来要解决的问题就是“怎么传这个参数”。GET参数是HTTP请求中很基础的一部分,它直接拼在URL的路径后面,用?起始,多个参数之间用&分隔。比如:

http://example.com/index.php?cat=dog&page=2

这个URL表示向example.com的index.php发起GET请求,同时传递了两个参数:cat的值是dog,page的值是2。在Havefun这道题里,我们只需要一个参数,所以构造出来的URL就是:

http://目标地址/?cat=dog

如果目标站点本身有路径,比如http://xxx/yyy/index.php,那就把参数拼接在原有路径之后,变成http://xxx/yyy/index.php?cat=dog。需要注意,参数值dog不需要加引号,因为URL查询字符串本身没有引号概念,加引号反而会让后端收到一个带引号的字符串,导致比较失败。

有些新手会问:为什么不用POST?因为源码里用的是$_GET,这是PHP专门用来接收URL查询字符串参数的超全局变量。如果你改成POST提交,把cat=dog放进请求体里,后端根本拿不到这个值。所以做题之前看清楚代码用的是$_GET还是$_POST,是非常关键的一步。这也能解释为什么很多人在Burp里把请求体改了却没反应,因为根本没改对地方。

3. 实操过程:完整复现拿到flag

3.1 第一步:查看源码,找到代码注释

实际操作的时候,第一步永远是打开题目页面,然后查看页面源代码。浏览器里查看源码的快捷方式一般是Ctrl+U,或者右键点击页面空白处选择“查看页面源代码”。如果你习惯用开发者工具,按F12以后切换到Elements面板也能看到同样的HTML结构,但我更推荐Ctrl+U直接看原始源码,因为原始源码不会因为DOM动态变化而干扰你的视线。

打开源码以后,不要急着从上往下扫,先按Ctrl+F搜索几个关键词:flag、cat、dog、php、comment。这道题的注释一般在页面底部,搜索cat或者dog能很快定位到那段PHP代码。查看源码的时候一定要把所有注释都看完,尤其是<!--开头的内容。很多CTF题都会把提示放在注释里,有的是URL路径,有的是备份文件名,有的是直接给出一段代码逻辑。就算这次没用到,养成这个习惯也能让后面刷题少走弯路。

找到那段PHP注释以后,不要激动得直接复制粘贴去提交,先冷静读一遍代码,确认参数名是cat,判定值是dog,再继续下一步。我见过有选手把参数名记反了,以为要传?dog=cat,结果怎么试都不出flag,回头重新看源码才发现自己看反了。

3.2 第二步:构造cat=dog并验证结果

确认了代码逻辑,接下来就是最快乐的一步:拼参数。假设当前浏览器地址栏是http://目标/,直接在后面添加?cat=dog,完整地址变成:

http://目标/?cat=dog

回车以后,页面会先把你传入的dog回显出来,紧接着就能看到一串flag字符串。如果BUUCTF环境做了动态flag,那生成的flag可能就是类似flag{一串随机字符}的格式。看到这个结果,这道题就算打通了。整个过程不到十秒钟,但就是这十秒,把“查看源码→理解逻辑→构造参数→验证结果”的完整链路走了一遍。

如果直接拼在地址栏里没有反应,先检查一下URL有没有拼错,尤其要注意是不是把参数拼到了错误的路径上。有些题目页面是一个二级路径,比如http://xxx/note/index.php?m=show,这时候应该保留原有路径,只在后面加上cat=dog,变成http://xxx/note/index.php?m=show&cat=dog,而不是重新拼成http://xxx/?cat=dog。另外还要注意浏览器可能会因为HTTPS或HTTP混合内容拦截请求,但这类题目环境一般不会出现这种问题。

3.3 第三步:用Burp和curl做交叉验证

浏览器地址栏直接传参虽然直观,但不能只依赖这一种方式。实战中我更推荐用Burp Suite或者curl再验证一次,一方面能确认响应内容,另一方面也能加深对HTTP请求结构的理解。用curl的话,命令很简单:

curl -v "http://目标/?cat=dog"

-v参数会输出完整的请求和响应头,响应正文里就能看到flag。如果用Burp Suite,先打开Proxy拦截,浏览器访问题目页面时抓到请求,这时候把请求行改成:

GET /?cat=dog HTTP/1.1

然后放行,在HTTP响应里就能看到flag。用Burp的好处是你能清清楚楚看到请求被发送到了哪里、响应里带上了什么内容,还能直接在Repeater里反复修改参数测试,不用在浏览器和Burp之间来回切换。对于这道题,虽然浏览器就够用了,但拿它当练习Burp的基础案例非常合适。

交叉验证还有一个隐蔽的好处:避免浏览器缓存骗你。有时候你改了URL参数,但浏览器显示的还是上一次缓存的页面,导致你以为没成功,实际上请求早就发了。用curl或者Burp发出的请求是全新请求,不存在缓存问题,结果更可信。

3.4 为什么这题叫Havefun:从解出flag反推设计意图

解出flag以后,我反而会回头想想题目名为什么叫Havefun。极客大挑战2019是一场面向新人的CTF比赛,出题人把它放在Web签到题的位置,用一个轻松的标题告诉你:“Web题也可以很好玩,别紧张,先来热个身。”这题的考点没有超出任何一本Web安全入门教程的范畴,但它非常精准地考察了选手面对一个陌生Web应用时的第一反应。

很多人刷题喜欢直接看writeup,但我更建议自己先做一遍,因为只有亲自卡过、急过、再解决掉,才能真正理解题目背后的考点。Havefun这道题最好的打开方式,就是抱着“玩一玩”的心态,把页面翻个底朝天,看看出题人到底把flag藏在了哪里。等你习惯了这个流程,再往后刷难度更高的Web题时,你会发现很多复杂攻击链的第一步,依然是信息收集。

4. 常见问题与排查技巧实录

4.1 传参没反应?先对照这四件事

我见过不少人在Havefun上翻车,翻车点翻来覆去就那么几个。第一,参数名写错了。有人以为题目是“猫和狗”,所以理所当然地传dog=cat,但源码里写的是$_GET['cat'],参数名必须是cat,值是dog。第二,拼接位置错了。没有保留原有的路径,直接拿根域名加参数,导致请求到了错误的路由。第三,浏览器缓存。修改URL以后,页面显示的还是旧内容,这种情况按Ctrl+F5强制刷新即可。第四,看错源码提示。有些选手找到的是页面里其他无关的注释,没有定位到真正的PHP提示代码就开始盲猜参数。

可以列一个简单的排查顺序:

  • 确认当前URL路径是否正确,保留原有路径。
  • 确认参数名是cat,值是小写的dog。
  • 用Burp Repeater重新发送请求,绕过浏览器缓存。
  • 检查响应中是否有flag,而不是只看页面是否变化。

按照这个顺序走下来,百分之九十九的情况都能解决。如果还是不行,那就要怀疑是不是题目环境本身出问题了,可以重新开启一道新的实例再试。

4.2 浏览器只会打开页面,不会做请求分析怎么办

很多新手没有用过抓包工具,打开浏览器只看到最终渲染后的页面,不知道请求是怎么构造的、响应里有什么东西。这个问题在Havefun上不致命,但会成为后面刷题的大障碍。我建议从今天开始,至少学会看浏览器开发者工具里的Network面板。按F12打开面板,切到Network,刷新题目页面,你会看到浏览器发出的每一个请求。点击请求可以看到请求方法、URL、请求头、还有服务器返回的响应体。

如果浏览器开发者工具不够用,再上Burp Suite。Burp是CTF Web题最常用的工具之一,开启代理以后,浏览器的所有请求都会经过Burp,你可以在Proxy的HTTP history里看到完整的请求包,用Repeater可以手动修改请求包并反复发送。Havefun这道题非常适合用来练习Repeater:把请求行改成GET /?cat=dog HTTP/1.1,点Send,就能看到flag。这个动作学会了,后面做任何Web题都离不开。

抓包这块我多说一句:抓包不是“黑客专属技能”,它的本质就是“看清你和服务器之间到底说了什么”。Web安全里最忌讳盲打,连请求包长什么样都不知道就乱猜参数,那和闭着眼睛投篮没什么区别。只要能把请求看懂,哪怕不会高深攻击,排查问题的效率也能翻一倍。

4.3 从Havefun延伸到PHP弱类型比较漏洞

Havefun本身很简单,但PHP的==比较这行代码,在CTF里可以延伸出非常多的考法。PHP是弱类型语言,==在比较时会自动进行类型转换,导致很多看起来不相等的东西最终会被判定为相等。最典型的例子是0e开头的字符串。在PHP里,如果一个字符串形如0e123,它会被当作科学计数法解释,数值等于0。于是两个完全不同的字符串0e123和0e456用==比较时,结果居然是true。这个特性常被用在MD5绕过题里,比如要求传两个不同的参数,但它们的MD5值相等,就可以拿0e开头的两个字符串直接绕过。

再看另一个例子:在PHP 7的环境下,字符串"admin"和数字0用==比较时,"admin"会被转换成整数0,结果"admin" == 0为true。如果某个登录逻辑写的是if ($user == 0),那传入user=0可能就能绕过身份校验。这些弱类型比较的坑在真实代码里是安全隐患,在CTF题里就是出题人喜欢埋的考点。

回到Havefun,它用的是$cat == 'dog',两边都是字符串,PHP不会把'dog'转换成数字,所以不存在弱类型绕过的问题,老老实实传dog就行。但理解了==的特性,以后看到类似代码就不会只盯着“怎么传普通值”这一个角度,而是会多问一句:有没有可能通过类型转换绕过?这个思路上的转变,比多刷十道简单题更有价值。

4.4 我给新手的三条刷题建议

最后分享三条我自己刷题以来一直坚持的习惯,都是踩过坑换来的经验。第一条,信息收集永远从看源码开始。任何Web题拿到手,先按Ctrl+U看完整源码,再看Network面板里的请求,最后才考虑用什么扫描器。很多题目的突破口就藏在注释、JS文件、响应头里,不看源码直接上工具,等于放弃最便宜的线索。第二条,参数测试要有记录。Havefun只测一个参数,但后面有些题可能要测十几个参数,手动乱试很容易把自己搞晕。我习惯用一个表格记录每次测试的参数名、值、响应特征,哪怕只是简单的“有变化/没变化”,也能帮你快速定位逻辑。

第三条,多写自己的writeup。每做完一道题,花十分钟把过程整理成笔记,不用很华丽,写清楚“题目考点是什么、我怎么想到的、卡在哪里”就够了。人脑的记忆是会骗人的,今天能做的题,下周可能就忘了关键步骤。笔记不是为了发出去炫耀,而是为了在下次遇到类似考点时,能一秒想起当年的坑。这个习惯我从刷BUUCTF第一天开始坚持,后来回头看,那本笔记就是最值钱的财富。

说到底,Havefun这道题就是一道开胃菜,它不会让你变成Web安全大师,但它能帮你把最基础、最容易被忽略的习惯建立起来。字符串比较、GET参数、源码注释,每一个概念单独拎出来都不难,难的是在遇到陌生题目时,能条件反射地想到“先去看源码,再去找参数”。这个条件反射,才是这道题真正想送给你的礼物。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询