刷 BugKu 的 Web 篇,是我带新人和自己重新梳理知识体系时都会干的一件事。这平台的好处在于,题目梯度拉得比较开,从送分题一路爬到需要静下心来做代码审计和逻辑绕过的题都有,而且不需要你自己搭靶场环境,浏览器打开就能开打。这篇 wp 我不打算按题目编号一篇篇复读答案,那对新手没有任何营养。我更想把 Web 方向常见的题型、判断链路、卡壳点拆开讲清楚,当你自己手头拿到别的靶场题目时,也能套用同一套思路去解。标题虽然是“BugKu Web 篇通关”,但我真正想交付的,是一套能迁移到其他 CTF 比赛的 Web 解题方法论。
如果你目前还处在“拿到题目不知道从哪下手”的状态,这篇内容应该能帮你把解题顺序固定下来:先看什么、怎么测、用什么工具、什么时候该换思路、哪些坑是踩过一次就该长记性的。如果你已经刷过一部分题,那后面关于注入判断链路和上传绕过、代码审计的部分,值得重点看。
1. BugKu Web篇到底在考什么——先建立全局视角
很多新人一上来就盯着“这一题怎么解”,恨不得每道题都有人手把手喂答案。但通关整个 Web 篇之后再回头看,题目之间是有脉络的。BugKu Web 篇的题目大致能分成这么几类,我把它们梳理成一个表,方便你有个全局概念。
| 题型 | 常见考点 | 典型难度 | 通关后你应该掌握的能力 |
|---|---|---|---|
| 信息搜集类 | 响应头、源码注释、robots.txt、备份文件、CMS 指纹 | 低 | 养成“拿到题目先看源码和响应头”的肌肉记忆 |
| HTTP 基础类 | User-Agent、Referer、Cookie、请求方法、X-Forwarded-For | 低 | 会用 Burp Suite 改包重放,理解请求与响应结构 |
| 注入类 | SQL 注入(联合、报错、布尔盲注)、命令注入 | 中 | 能手工判断注入点闭合方式,写出盲注脚本或熟练用 sqlmap |
| 文件上传类 | 后缀校验、Content-Type 校验、内容头校验、解析漏洞 | 中 | 知道各类校验在服务端大致怎么实现,怎么绕过 |
| 代码审计类 | PHP 危险函数、变量覆盖、反序列化入口 | 中高 | 能定位输入点,顺着输入点追踪到危险函数 |
| 逻辑漏洞类 | 越权、验证码绕过、弱口令、业务逻辑绕过 | 中 | 懂得修改请求参数、遍历 ID 或者绕过前端限制 |
这个分类是通关后复盘出来的,不是刷题之前就有的。我建议你也用这种方式管理自己的题单,每做一道题,就把它归类到某一个或多个题型下面,顺便记一下自己当时卡在哪里。刷到后期你会发现,很多题是复合型的,比如上传题会结合代码审计,SQL 注入题会结合绕过 WAF 的思路。
难度梯度上,BugKu Web 篇前三分之一基本属于“鼓励题”,只要你愿意打开 F12 看源码、愿意装个 Burp Suite 改改包,就能过。中段开始需要一点 SQL 注入基础,后段则偏向代码审计和逻辑发散。整体设计对新手非常友好的一点是:平台不限制你尝试次数,也不搞什么环境隔离,你可以在里面反复试错,直到把原理弄明白。
2. 从第一道题开始:HTTP协议的“常识”才是Web题的根基
Web 题的本质,是你在跟服务器进行 HTTP 对话。很多人忽略这一点,一上来就想着怎么搞注入、怎么传马,结果最基础的送分题反而卡了半天。BugKu Web 篇开头的题目,几乎都是围绕 HTTP 协议的基础字段做文章,这其实是在帮你建立“一切攻击都基于合法请求被服务器误解”这个核心认知。
2.1 改包前你得有个顺手的工具
做题的第一步,先把工具链装好。我的建议是三件套:
- Burp Suite Community 版:抓包、改包、重放请求的标配。社区版足够打完整套 Web 题,不要一上来就去找 Pro 破解版,没必要。
- 浏览器开发者工具(F12):查看源码、看网络面板、改前端临时状态。做 Web 题不开 F12 等于闭着眼走路。
- curl:有些时候你只需要快速发一个带特定 Header 的请求,开 Burp 有点小题大做,curl 一条命令搞定。
拿 BugKu 的一道典型签到题举例:页面打开只有一个输入框,让你提交一个什么东西。很多新手会直接在输入框里乱试,试不出来就卡住。正确顺序是:先按 F12 看源码,然后切到 Network 面板,看页面加载了哪些请求,响应里有没有什么提示字段。如果源码和响应头里都找不到线索,再用 Burp 对提交动作抓一次包,看看 POST 请求体长什么样、服务端返回了什么。
2.2 服务端真正会检查的几个 HTTP 字段
HTTP 基础题翻来覆去都是在考你对以下几个字段的理解,我一个个说。
User-Agent(UA):标识客户端身份的字段。服务端可以读取它来判断访问者是不是浏览器、是 Chrome 还是某些脚本工具。有些题要求你以指定 UA 访问,比如改成浏览器版本字符串或者题目指定的 SpecialAgent。用 Burp 改一行就能过。
Referer(或新标准里的 Referrer):表示“你是从哪个页面跳过来的”。有些服务端会校验这个字段,要求访问某个页面时必须带有来自特定站点的 Referer 跳转来源——它本质上是一种很弱的防盗链和访问控制手段,但也确实常被拿来做题。
X-Forwarded-For(XFF):HTTP 标准里用于透传客户端真实 IP 的扩展头,服务端后面挂代理服务器时非常常见。CTF 题里最常见的考法,是要求你必须用某个 IP 地址访问才能看到 flag,比如本地 127.0.0.1。因为你自己改包就能伪造这个字段,所以这类题本质上考的是“你知不知道改 XFF”。改成X-Forwarded-For: 127.0.0.1,重放,走人。
Cookie 与 Session:服务端用 Cookie 标识客户端身份状态。有些题把判断逻辑放在 Cookie 里,比如admin=0,你改成admin=1再刷新,权限就变了。也有些题让你带着特定 Cookie 访问,甚至直接让你从 Cookie 里找到 flag 字段值。
请求方法:GET、POST 是最常见的,但服务端如果只实现了 GET 接口,你发 POST 就会 405。反过来也一样。有些题要求你用 PUT、OPTIONS、TRACE 等冷门方法访问,属于纯考 HTTP 方法认知的送分题。用 Burp 把请求方法改掉就行,或者用 curl 的-X参数直接指定。
2.3 响应头里的 Flag 和源码注释里的惊喜
除了主动构造请求,还要养成看响应的习惯。有些题的 flag 直接就藏在响应头里,比如X-Flag: flag{...}这种,专治不做题就着急提交的人。还有一些藏在 HTML 注释里,注释里通常会写“flag is here”或者给你一个跳转提示。这类题在 BugKu 里属于“做完会觉得自己被温柔对待”的类型,但说真的,很多人就是在这里养成了不看源码的坏习惯,导致后面中高难度的题寸步难行。
我的建议是:拿到任何一道 Web 题,先不做任何攻击性测试,就先把源码从头到尾读一遍,把响应头看一遍,把页面里所有可见的输入点列出来。这套动作做完,至少有三成题目已经能出答案了。剩下的七成,再进入下面要说的注入和绕过环节。
3. SQL注入题的精髓:闭合、报错与布尔盲注的完整判断链路
SQL 注入在 BugKu Web 篇里的权重很高,也是新手从“送分题”迈向“真正 Web 安全”遇到的第一道槛。很多人卡住的原因不是不知道 SQL 注入是什么,而是拿到一个注入点之后不知道下一步该干什么、怎么判断注入了什么类型的库、怎么把数据提出来。问题出在:他们没把 SQL 注入当成一条“判断链路”来走,而是企图一步到位、直接一把梭哈掏出 flag。
3.1 第一步永远不是丢 sqlmap,而是手工找闭合方式
注入的本质,是程序把用户输入拼进了 SQL 语句,而且没做参数化处理。你要做的第一件事,是搞明白输入点拼在了什么位置、用什么符号闭合。假设后台代码长这样:
$sql = "SELECT * FROM users WHERE id = '$id'";你在输入框里填1,拼出来的 SQL 就是:
SELECT * FROM users WHERE id = '1'这时候你输入一个单引号',拼出来的是:
SELECT * FROM users WHERE id = '' '这个语句的语法已经错乱了,因为闭合单引号后面多了个多余的引号,服务端通常就会报数据库错误。如果它没有把错误信息藏起来,而是直接回显给你,那就说明注入点大概率存在。
观察报错是第一步,接下来要判断的就是“怎么闭合才能让这个 SQL 语句是合法的”。这是整个 SQL 注入里最考经验的一步,常见情况我给你列出来:
| 后台拼接方式 | 你输入的内容 | 完整 SQL 效果 |
|---|---|---|
$id直接拼 | 1' OR '1'='1 | WHERE id = 1' OR '1'='1 |
'$id'单引号包裹 | 1' OR '1'='1 | WHERE id = '1' OR '1'='1' |
"$id"双引号包裹 | 1" OR "1"="1 | WHERE id = "1" OR "1"="1" |
('$id')括号加单引号 | 1') OR ('1'='1 | WHERE id = ('1') OR ('1'='1') |
你可以在输入框里输入这些测试语句,观察页面显示是否恢复正常、回显的数据是否有变化。能正常显示,说明闭合成功了。闭合一旦成功,后面想查什么数据就有了一个合法的“语法上下文”。
3.2 联合查询:有回显时的最高效手段
闭合找到之后,最简单的数据提取方式就是联合查询。原理是UNION SELECT会把查询结果和后端查询结果拼在一起,只要列数一致,后面的查询结果就会原样显示在页面上。
首先用ORDER BY探测列数。输入:
1' ORDER BY 3 -- -如果页面正常,说明表至少有 3 列。再试ORDER BY 4,如果报错,说明列数就是 3。
知道列数之后,就能用联合查询了:
1' UNION SELECT 1,2,3 -- -页面通常会回显某个数字,比如显示了 2 和 3,说明这两个位置是回显位。把 2 换成database(),3 换成version(),就能拿到当前数据库名和版本信息。接下来通过information_schema.tables查表名、information_schema.columns查字段名,一步步把目标表的数据捞出来。整条链路对新手来说其实非常固定,多练几道就能形成肌肉记忆。
3.3 没有回显也报错不出错?那就走布尔盲注
真实的题目往往不会让你那么痛快。你测试单引号的时候页面既不报错,也没有任何回显变化,只有“正常内容”和“空白/异常内容”两种状态。这就是典型的无回显场景,需要用布尔盲注:构造一个“真假条件”,根据页面反应来判断条件是否成立。
核心函数搭配是substr()和ascii()。比如要猜当前数据库名的第一个字符,可以先猜它的 ASCII 码是不是 97(对应字母 a):
1' AND ASCII(SUBSTR(database(),1,1)) = 97 -- -页面正常,说明第一位确实是 a,接着猜第二位;页面没反应,就换成 98、99 挨个试。手工试几十次确实累,但如果你是来学思路的,我强烈建议先用笨办法跑通一次,哪怕只是确认一下字符集前几位,然后再考虑写脚本。
我用 Python 写过一个最简单的布尔盲注脚本模板,核心逻辑就三步:构造条件、发请求、根据响应判断真假:
import requests url = "http://目标地址/index.php?id=" flag = "" # 假设已知库名长度是 8 for i in range(1, 9): for ascii_code in range(32, 127): # 每个字符转成 ascii 为 victim payload = f"1' AND ASCII(SUBSTR(database(),{i},1))={ascii_code} -- -" r = requests.get(url + payload) if "特征字符串" in r.text: # 页面正常时的特征内容 flag += chr(ascii_code) print(flag) break脚本的思路是:对每个位置依次尝试所有可打印字符的 ASCII 码,一旦页面出现“条件为真”的特征,就记录下这个字符,继续下一位。实际做题时,除了substr和ascii,也可以用left()、mid()搭配ord(),看后台用的什么数据库。MySQL 和 SQLite 的语法有细微差别,报错信息里一般能看出来。
3.4 报错注入和常见过滤绕过
如果页面开启了大面积报错信息但又不支持 UNION,那可以试试报错注入,常见的是 MySQL 的updatexml或extractvalue。原理是让函数解析的 XPath 字符串非法,触发报错,而报错内容里会包含你传入的 SQL 查询结果。典型写法如下:
1' AND updatexml(1, concat(0x7e, (SELECT database())), 1) -- -报错信息里就会出现~数据库名。这种方式不需要回显位,也不需要列数,非常省事。
关于绕过,BugKu 的题大多不会上很恶心的 WAF,但空格过滤、注释符过滤这类基础操作还是时有发生。空格被过滤时,可以用/**/或%0a代替;注释符-- -被过滤时,用#或直接闭合后面的引号。这些技巧说穿了不值钱,但确实能卡住没见过的人。
4. 文件上传、命令执行与代码审计——三板斧打穿中阶题
过了注入这一关,BugKu Web 篇的体验会明显提升一个档次,因为后面的题目不再是“一个输入点打到死”,而是要求你综合运用文件上传、命令执行、代码审计等多项能力。这三块在实战中经常是连在一起的:审计一段代码发现命令执行漏洞,或者通过上传点拿下一句话木马。
4.1 文件上传:校验逻辑比文件内容更值得研究
文件上传题的经典场景是:页面提供一个上传入口,让你传一个文件。目标通常是把一句话木马传上去,然后通过 Web 访问它,执行系统命令。
服务端常见的校验逻辑有四种,对应的绕过思路也完全不同,我整理成表格:
| 校验位置 | 校验逻辑 | 常见绕过方式 |
|---|---|---|
| 后缀名 | 只允许.jpg/.png/.gif | 改成.php3/.phtml/.php5、大小写混合、末尾加空格或. |
| Content-Type | 检查请求头里的 MIME 类型 | Burp 改包,把Content-Type改成image/jpeg |
| 文件内容头 | 检查文件开头的幻数,如GIF89a | 在 PHP 代码前拼一段图片头字节 |
| 目录/路径 | 上传路径拼接不可控 | 尝试路径穿越等方式(少数题会考) |
绕过的核心思路是:服务端只校验了某一个维度,而解析文件的逻辑又恰好容忍了你绕过的这个维度。比如它检查了文件后缀,但没有检查文件内容,那你就可以传一个内容为<?php @eval($_POST['x']); ?>、后缀命名为shell.jpg的文件,然后看看服务器会不会把它当作 PHP 解析。如果服务器配置了 Nginx 解析漏洞或者 Apache 多重后缀解析特性,shell.jpg在某些路径下就能直接被当成 PHP 执行。BugKu 的题不一定会走到解析漏洞那么深的程度,但“后缀校验不严”这种经典漏洞是必然会出现的。
一句话木马的本体是:
<?php @eval($_POST['shell']); ?>连上之后用工具或者手工 POST 参数shell=system('ls');,就能通过页面回显看到服务器上的文件列表。在 CTF 靶场上这一步是拿 flag 的关键。但这里我要多说一句:这套能力只能在授权靶场里玩,上传一句话木马到真实第三方站点,那是违法的,而且现在很多服务器都有防护,也不会给你这么简单的机会。
4.2 命令执行:过滤了关键字不等于过滤了命令
命令执行题一般长这样:页面有个输入框,传入一个 IP,后台执行ping命令,然后把结果回显出来。后台代码大致是:
$ip = $_GET['ip']; system("ping -c 1 " . $ip);如果服务端没有做任何过滤,那直接输入127.0.0.1; ls就能看到目录列表。分号;、管道符|、换行符%0a、&&、||都是常见的命令拼接符号,具体用哪个取决于后台用的是system()、exec()、passthru()还是shell_exec(),以及它有没有做简单的过滤。
如果它过滤了空格、ls、cat这些关键词,也不要慌。空格可以用${IFS}替代,cat可以用tac、more、less、head、tail替代,ls可以用dir或者用通配符l*来绕过。我之前在一道题里遇到过过滤了cat和空格的环境,最终用这段拿到了命令执行结果:
127.0.0.1;tac${IFS}/flag*tac是cat的反向输出,${IFS}替代空格,/flag*用通配符匹配 flag 文件名,过滤规则直接失效。这类题的核心不是让你背命令,而是让你理解:过滤总是基于字符串匹配的,而命令行的解析逻辑比字符串匹配复杂得多,只要有一丝缝隙,命令才能被拼起来。
4.3 简单代码审计:从输入点出发,追到危险函数
BugKu Web 篇后段的题目,很多会直接给你 PHP 源码。新手看到一坨代码就发怵,其实关键就两步:
- 找输入点。看有哪些变量来自
$_GET、$_POST、$_REQUEST、$_COOKIE、$_FILES,这些是你能控制的。 - 顺藤摸瓜,看输入点最终有没有流入危险函数。高危函数列表不多,背下来就行:
eval()、system()、exec()、shell_exec()、assert()、include()、file_get_contents()、unserialize()。
举一个最简单的例子:
<?php $action = $_GET['action']; include $action . '.php'; ?>这个代码里,$action是输入点,include是危险函数。虽然它强制拼接了.php后缀,但如果你传php://filter/convert.base64-encode/resource=index,就能用 PHP 流包装器把源码读出来,后缀拼接在resource=index.php上完全正常。这就是利用 PHP 内置协议的思路。
做代码审计题一定要有个好习惯:不要一行行从头读,要先找输入点和危险函数,然后把两者之间的路径打通。绝大多数 CTF 题的代码都不长,可控点也就一两个,用这个思路几分钟就能定位到可利用的位置。
5. 卡关时最值得留意的几个“隐藏考点”
做题最气人的不是题难,而是题里塞了一些“小彩蛋”,你没注意到就永远卡在同一关。BugKu Web 篇的题目很喜欢在常规考点之外埋一些隐藏信息,按照我的经验,下面这几个位置值得形成条件反射式的关注。
5.1 响应头、JS 混淆和编码跳转
有些题目在页面上只显示一张图或者一句话,看起来毫无线索。这时候去翻响应头,往往有意外收获。不只是X-Flag这种直白的字段,有些题会把提示藏在Set-Cookie里,或者藏在某个 JS 文件的注释里,甚至在页面引用的某个.js文件末尾加了一段 base64 编码字符串。处理这类问题,我习惯把所有返回内容复制下来,如果里面有可疑字符串,就丢到 CyberChef 里面试试 base64 解码、URL 解码、十六进制转 ASCII,基本上一两轮就能解开。
还有一种“跳转型”题目:JS 里面加密混淆了一串字符,或者页面通过 JS 不断跳转到别的路径。这类题不适合用肉眼盯代码,直接把源码里看起来像编码结果的长字符串复制出来,按 base64 或者十六进制解一遍,大多数能发现 flag 或跳转地址。
5.2 备份文件、robots.txt 和隐藏目录
信息收集不仅仅是“看看页面”,还要考虑站点上有哪些隐藏资源。做题时我用dirsearch或手写一个简短的目录字典去扫靶机上的路径,发现过.git目录泄露、index.php.bak、www.zip、robots.txt这类东西。
robots.txt是搜索引擎爬虫规则文件,它经常会被网站管理员用来“屏蔽”不想被收录的路径,结果反而是把敏感路径直接告诉了你。遇到 403 或者 404 的路径列表,如果出现在robots.txt里,值得换个请求方法或者补充路径再访问一遍。
.git泄露则是另一个经典考点。如果扫描到/.git/目录存在,说明站点把整个 Git 版本库暴露到了 Web 目录,工具可以直接把源码拖下来,里面往往有修改历史,而 flag 可能藏在某次提交里被删掉了但历史版本里还有。不过这类题在 BugKu 里不是主流,属于中后期才会遇到的花活。
5.3 逻辑层漏洞:越权和弱口令
不要以为 CTF 题都是高大上的技术漏洞,越权和弱口令也是常客。越权的典型场景是:用户登录后访问一个profile.php?id=100,把自己的 ID 改成 101,页面如果显示了别人敏感信息甚至变成管理员权限,就是水平越权。有些题甚至懒得上登录流程,直接给你一个可遍历的 ID 参数,你就能依次读取其他用户的数据。
弱口令更是老生常谈。遇到登录框,先试试admin/admin、admin/123456、test/test这种经典组合。BugKu 有些题的登录提示其实写在页面标题或者源码注释里,就是变着法提醒你别死磕技术漏洞,先试试弱口令。如果登录成功并且出现了文件上传功能,那就回到上一节的上传思路继续往后打。
6. 我这轮刷题踩过的坑,和我的排除顺序
最后这部分,我想说说我自己从头到尾刷 BugKu Web 篇时踩过的坑。这些坑不是知识点层面的,而是做题习惯和心态层面的,但说实话,它们比知识点更能决定你能不能通关。
6.1 我踩过的几个具体坑
第一个坑:不抓包,只在浏览器里折腾。我前期有段时间,遇到输入框就在页面上乱输,完全忽略了 Burp Suite。直到遇到一道题,页面上怎么输入都不对,但抓包一看,原来提交的请求里有一个隐藏参数,前端页面根本没显示出来。从那以后我养成了“任何提交动作都必须抓包看一遍”的习惯。
第二个坑:拿到源码之后没有主次,浪费大量时间一行行读。后来我发现,只要先把$_GET、$_POST、$_REQUEST、$_COOKIE这些输入搜出来,再把eval、system、include、unserialize这些高危函数搜出来,中间一连接,漏洞点基本就水落石出了。
第三个坑:过分依赖 sqlmap。工具确实快,但我曾经在一道题里直接跑 sqlmap,跑了二十分钟什么都跑不出来,后来手工注入发现后台是 SQLite,sqlmap 默认的 MySQL 指纹压根不匹配。快到的东西不一定可靠,手工理解判定链路才是根治方案。
第四个坑:不看提示,死磕一个点。BugKu 的题目描述里经常带着很明显的提示,比如“请用 admin 身份访问”“某文件泄露了敏感信息”。我有一道题卡了两个小时,最后发现题目描述第二行就写着“试试看备份文件”,思路直接崩了。现在做题,我第一件事就是反复读题面,一个字都不会漏。
6.2 我固定的解题顺序,直接抄即可
把上面所有经验浓缩成一条可执行的流程,我现在做题的固定顺序是这样的:
- 读题面至少两遍,画出所有线索关键词。
- F12 看源码,看 HTML 注释,看引用的 JS 文件。
- Network 面板看请求和响应头,尤其注意有没有开给爬虫或者调试用的字段。
- 对页面上所有输入点、按钮做一次正常请求,抓包记录正常响应。
- 根据前面信息判断题型:注入 / 上传 / 命令执行 / 逻辑漏洞 / 代码审计 / 信息搜集。
- 按题型进入对应链路,每一步基于上一个响应结果决定下一步。
- 卡住超过 30 分钟,就放弃当前思路,回去复查题面和响应头,看看是不是漏了隐藏信息。
- 解出 flag 后,把 payload 和思路记录到自己的笔记里,标注“当时卡在哪一步”。
这个顺序看起来朴素,但真的能救命。尤其在比赛现场或者限时环境下,思路一旦乱了,最有效的动作就是退回去从头看请求和响应,而不是在一个错误的方向上继续消耗情绪。
6.3 几件值得长期坚持的事情
- 养一个“漏洞知识笔记”仓库,按题型分类,记录每次做题用的 payload 和绕坑过程。
- 刷题时不追求题目数量,而是追求“我能从头到尾讲清楚为什么这样闭合、为什么能绕过”。
- 常用工具提前配好:Burp Suite、dirsearch、CyberChef、一个顺手的 Python 环境,别到了用的时候才想起来装。
- 学会写简单的 Python 请求脚本,因为盲注、遍历、爆破这类重复劳动,写脚本的速度比手点快得多,而且不容易出错。
我第一次完整刷完 BugKu Web 篇的时候,其实没有立刻感觉“我变强了”,更多是松了一口气:原来 Web 题就这些东西,翻来覆去都是那几个考点。但后来再去打别的平台的题目,我发现那些题目表面上换了皮,内核却完全一致——闭合、绕过、信息收集、逻辑设计,全都是 BugKu 这边练过的底子。这也是为什么我愿意花篇幅把这套思路完整写下来,而不是给一份简单的答案列表。答案会过期,但解题链路和排查习惯,能陪你在 CTF 这条路上走很久。