☰
BUUCTF Web第二页实战:文件包含、伪协议与上传绕过全解析
2026/10/9 10:38:54 网站建设 项目流程

打开BUUCTF的Web题列表,翻过第一页,大多数人第一次意识到自己的"新手期"结束了。第一页的题目很善良,SQL注入会告诉你注入点在哪,命令执行会留好回显,弱口令甚至把用户名直接写在注释里。可到了第二页,题风就变了:页面干干净净,只给一个压缩包下载,或者一个上传框,再或者一串不知道从哪入口的URL参数。你要自己找漏洞点,自己拼链路,自己处理各种意想不到的过滤规则。这篇文章想聊的就是这一阶段的东西——BUUCTF Web第二页的题目设计逻辑、高频考点、实际解题链路,以及我在反复踩坑之后总结出来的刷题方法。

先说清楚一个范围:所有讨论都发生在BUUCTF这个公共CTF靶场内,解题对象是平台提供的题目环境,不是真实业务系统。CTF本身就是网络安全领域最常见的训练方式,在授权环境下练习漏洞挖掘和利用,是攻防技术学习里的常规动作。

1. 从第一页到第二页:BUUCTF Web 到底在考什么

1.1 BUUCTF平台与Web方向的价值

BUUCTF全称BUU Cluster of Test Flag,是国内覆盖非常广的CTF练习平台,Web方向的题目来源几乎涵盖了近年各大知名赛事的真题。平台把题目环境统一容器化,点开就能看到题目页面,不用自己搭环境,也不用管题目背后的服务器配置,对练习者来说省掉了大量非核心时间。

很多人问:为什么推荐从这里刷Web题,而不是直接上HackTheBox或者去GitHub翻开源靶场?我的看法是:BUUCTF的Web题在"入门到进阶"这个区间的梯度设计是最平滑的。第一页的题会让你很舒服地建立信心,第二页开始难度阶梯式上升,但又不至于像顶尖比赛压轴题那样完全无从下手。更重要的是,BUUCTF的题源来自真实比赛,不是教材里那种刻意设计的单点漏洞,而是把多个基础考点组合在一起,这种组合方式恰恰是实际业务环境中漏洞利用的常态。

1.2 "第二页"隐藏的能力跃迁信号

如果只看题目数量,第二页和第一页没有本质区别,但题目风格有一个很明显的分水岭:第一页的题基本是"已知漏洞点"题型,题目里会直接暗示或暴露某个漏洞入口,你只需要选对payload就能出flag;第二页开始大量出现"未知入口"题型,页面可能只是一张图片、一个压缩包、一个上传框,没有任何文字提示,你需要自己完成信息收集、源码审计、漏洞定位、利用链构造的全流程。

这种变化背后,实际上是三种能力的跃迁。

第一是信息收集粒度的变化。第二页的题目经常把关键线索藏在不起眼的地方:响应头里的自定义字段、页面源码注释、备份文件、附件元数据、Cookie里的序列化字符串。第一页你只需要关注"漏洞在哪",第二页你必须先回答"信息在哪"。

第二是知识点串联能力的变化。单独看zip伪协议、二次渲染绕过、文件包含,每个点都不算难,但题目把它们串成"爆破压缩包密码→审计解压后的源码→构造恶意压缩包→上传触发→伪协议执行代码"这条完整的链时,难度就不是加法而是乘法了。[GUET-CTF2019]zips这道题就是一个典型代表,它几乎是为这种综合链路量身定制的。

第三是工具熟练度的变化。第一页用浏览器F12就能解决大部分问题,第二页开始,Burp Suite的Repeater、Intruder、Decoder,Python的requests脚本,Webshell管理工具,这些都是刚需。工具不熟,即使思路对了也会在操作环节卡住。

2. 第二页高频命中:文件包含与伪协议利用

2.1 伪协议是Web题的"瑞士军刀"

在BUUCTF Web第二页的题目里,PHP伪协议的出现频率高得惊人。这也合理,因为大量题目是PHP写的,而PHP内置的伪协议提供了非常多实用的文件读写与代码执行能力。我刷到后来形成了一种条件反射:只要题目里出现文件读取、文件包含、文件上传相关的点,第一反应就是尝试各种伪协议。

最常用的几个:

  • php://filter:用于读取文件源码,同时用base64编码输出避免特殊字符破坏当前上下文。一个最常见的请求长这样:?page=php://filter/read=convert.base64-encode/resource=index.php。看到page、file、include这类参数,先试这个准没错。
  • php://input:允许把POST请求的请求体当作PHP代码执行。配合文件包含漏洞,可以直接注入<?php system('ls'); ?>这样的代码。
  • data://:可以把数据流当作文件内容,同样可以用来执行PHP代码,而且不需要依赖POST体。
  • zip://和phar://:这两个协议可以访问压缩包内部的文件。zip://archive.zip#shell.php可以读取zip包内的指定文件;phar://除了文件包含,还经常用在反序列化攻击里。
  • file://:直接读取本地文件系统,绕过一些URL过滤。

这里有个容易忽略的细节:php://filter不仅能读源码,还能配合编码器做更复杂的操作。比如convert.base64-encode是读源码的标配,但如果题目过滤了一些关键字,可以试试string.rot13或者组合多个过滤器。还有一点,php://filter的resource参数在Linux环境下往往用绝对路径更稳,因为在包含嵌套路径时相对路径可能会出问题。

2.2 压缩包协议链:从解压到RCE的常规路径

伪协议里最让新手头疼的就是zip://这条链路,因为它的利用场景往往不是单独出现的,而是作为整道题的最后一步。以常见的"上传zip文件"场景为例,完整链路通常长这样:

  1. 题目提供了一个上传点,限制只允许上传zip压缩包,且后端会把zip解压到某个目录。
  2. 解题的关键是:上传的zip包内可以包含一个PHP后门文件,上传后可以通过某种方式触发执行。
  3. 触发方式通常是文件包含漏洞,配合zip://协议直接"穿过"解压层读取压缩包内的PHP文件。

构造zip://请求时要特别注意URL编码问题。zip://uploads/xxx.zip#shell.php中的#在URL里是有含义的,会被当成锚点而不传给服务器,所以实际请求要写成zip://uploads/xxx.zip%23shell.php。这是我见过最多的翻车点,思路完全正确,就因为少做了一步URL编码,页面一直没反应。

另外要注意的是,zip://协议访问的是压缩包内的文件,所以payload文件的命名、在压缩包内的路径,都会影响最终请求的URL。有的题目要求你把php文件放在压缩包根目录,有的则允许带子目录,这就需要回到源码审计结果里去确认。

3. 文件上传与绕过:findme这类题的底层逻辑

3.1 上传题的五道关卡

BUUCTF第二页的Web题里,文件上传是另一个高频组合考点。跟第一页那种"上传个一句话木马就能连"的幼稚题不同,第二页的上传题几乎都存在多层校验。我把常见的校验点归纳成五道关卡:

  • Content-Type校验:前端或后端检查Content-Type是否为image/jpeg等图片格式。这个最弱,改请求头就能过。
  • 扩展名校验:黑名单或白名单机制。黑名单可以直接双写、大小写、加空格、加.、加::$DATA等方式绕过;白名单则麻烦得多,常见思路是找解析漏洞、配合.htaccess或者利用容器特性。
  • 文件头校验:检查文件内容的magic bytes,比如JPEG要求文件头是FF D8 FF,PNG要求89 50 4E 47。对应办法是在payload前面拼上合法的图片字节。
  • 内容二次渲染:后端起一个图片处理库(GD库等)重新生成图片,过滤掉原始文件里的非图片数据。这种校验最坑,因为简单拼接很容易失败。
  • 路径与包含点可控性:上传成功之后,还需要找到一个文件包含或路径解析的触发点,否则文件只是躺在服务器上,无法变成代码执行。

3.2 二次渲染与图片马的正确姿势

二次渲染是很多人卡住的地方。我第一次遇到带二次渲染的上传题时,想法很简单:随便找一个图片,把<?php @eval($_POST[1]); ?>插到图片末尾,改扩展名上传,再用包含点去触发。结果服务器直接返回一个"图片解析失败"或者干脆白屏。原因就是GD库重新生成了图片,PHP代码被我插在元数据或者多余字节里,被渲染过程直接丢弃了。

正确的做法是:先上传一张正常的图片,下载服务器渲染后返回的图片,对比原始图片和渲染后图片的字节差异,找到那些在渲染后依然保留的字节区域,把payload插到这些区域里。这个过程可以手动,也可以借助工具脚本完成。对PNG来说,常见的是在关键数据块(比如IDAT)中寻找可用偏移;对JPEG则要找注释段或者填充段。

当时我做这类题时还发现一个小技巧:如果二次渲染使用的是GD库,有些版本的GD库对超长注释、特殊压缩率的PNG存在解析缺陷,可以构造特殊图片让GD渲染后仍保留部分payload。这类"杀软绕过"的思路到了真实渗透测试场景里也同样值钱。

4. 两道第二页经典题的完整解题实录

4.1 [GUET-CTF2019]zips 解题链路

这是我刷第二页时印象很深的一道题,也是把"信息收集—爆破—审计—协议利用"串得最漂亮的一道题。题目页面上只存在一个压缩包资源,下载下来是一个加密的zip文件,直接解压会提示需要密码。

我的第一步是查看压缩包内容。用ZipCenOp或者直接命令行zipinfo看文件列表,发现包里是某些PHP文件,这说明真正的源码藏在压缩包内部,所以必须先解决密码问题。题目环境没有给出明显提示,但我注意到压缩包文件名或者附件属性里带有"6位数字"的字样(具体以你实际拿到的题面为准)。这种提示在CTF题里非常常见——密码范围被限定得越小,爆破成本就越低。

直接用ARCHPR跑6位纯数字字典不是不行,但我更喜欢用Python脚本自己控制流程,因为还能顺手做后续的自动化操作。核心代码其实非常短:

import zipfile z = zipfile.ZipFile('zips.zip') for i in range(0, 1000000): pwd = str(i).zfill(6) try: z.extractall(pwd=pwd.encode()) print(f'[+] Password found: {pwd}') break except Exception: continue

跑出密码后,解压出来的文件里一般会有一个PHP文件,我的情况里是upload.php。到了这一步,审计源码就成了重头戏。我看到的是一个典型的文件上传接口:接收POST上传的zip文件,并把压缩包内容解压到指定目录,同时文件内容没有太多安全检查,也没有限制zip包内的文件类型。

到这里思路就清晰了:我可以构造一个包含PHP后门的zip包上传上去,然后利用题目环境中的某个文件包含参数,通过zip://协议访问压缩包内的PHP文件。构造恶意压缩包时我用的是Python的zipfile模块,把后门文件放进去,然后直接上传。之后本地验证zip://请求时发现,#号必须编码为%23才能正确路由到压缩包内的文件,这是最容易被忽略的一环。

整道题的链路是:下载加密包 → 数字爆破 → 解压审源码 → 构造后门压缩包 → 上传 → 伪协议触发 → 执行命令读取flag。链路本身不复杂,但每一步都要求你熟练掌握对应工具,任何一处卡住都走不下去。

4.2 [湖南省赛2019]findme 解题实录

findme这道题与zips的切入点完全不同,它更侧重信息收集和源码审计。打开题目页面,表面上是普通的Web界面,但我检查页面源码时发现一个可疑的page参数,通过它间接加载一些页面片段。看到参数名里有"page"这个单词,我几乎是本能地尝试了文件包含探测。

先试普通的路径穿越,没什么反应;接着试PHP伪协议读取源码:

GET /?page=php://filter/read=convert.base64-encode/resource=index.php

返回了一段base64字符串,解码后终于拿到了首页的PHP源码。读下来发现存在一个文件包含的点,同时对参数内容做了简单的过滤,不允许出现...路径穿越关键字,但允许伪协议。继续用同样的方式读取其它文件,比如上传组件文件、配置文件和路由文件,逐渐摸清了整个应用的结构。

源码里暴露了一个上传接口,允许上传图片,但是有MIME类型校验和文件头校验。我构造了一个图片马:图片正常显示内容,但在字节流中嵌入<?php system($_GET['cmd']); ?>这样的payload。第一次上传成功,但访问时发现代码没执行,因为后端对图片做了一次重新采样,payload被当成无效数据清掉了。

于是回到二次渲染的绕过思路上:先上传一张完全正常的带特定结构的PNG,然后下载渲染后的返回图片,对比两个文件的差异,找到一个渲染后仍保留的字节区间,把payload插入这个区间。重新上传后,再回到文件包含点,包含这个图片的路径,并传入cmd参数。这一步成功后在服务器上执行了命令,最终在flag文件里拿到了目标字符串。

findme这道题的价值不在于某一个单独技巧多高级,而在于它要求你按"读源码→找上传点→构造文件→找触发点→执行命令"的完整逻辑推进。很多人做到"图片上传成功"就以为结束了,忘了真正的关键是那个隐藏在路由里的文件包含触发点。

5. 刷题工具链与排查实战

5.1 高效工具组合与配置建议

做BUUCTF Web第二页的题,工具配置直接影响解题效率。我的建议是至少配齐下面这一套:

  • Burp Suite:Repeater负责手工改包调试,Intruder负责跑字典爆破,Decoder处理编码转换。Burp是Web刷题的核心工具,没有之一。
  • Python + requests库:很多题目的解法无法用浏览器直接完成,需要脚本自动化提交、循环爆破、构造恶意包。写个小脚本比手工操作快得多。
  • HackBar / F12开发者工具:快速构造GET/POST请求、查看页面响应和Cookie。HackBar的编码功能很适合处理伪协议参数。
  • Yakit:国产的集成化安全测试工具,功能与Burp高度重合,界面更友好,官方插件市场也有很多CTF相关的插件可用。
  • Webshell连接工具:遇到需要已拿到后门但需要交互式操作的情况,比如AntSword或类似工具,可以方便地连接和管理webshell。
  • 字典库:常见路径字典、参数名字典、弱口令字典。不要自己一点点攒,直接复用开源项目里的fuzzDicts或SecLists中相关分类。

另外我的习惯是准备一个专门记录每道题解题链路的笔记文件,记录题目名称、协议/技术点、关键payload、踩坑点。刷题多了之后回查笔记,效率提升非常明显。

5.2 Web题环境的常见痛点与解决办法

BUUCTF的容器环境偶尔会有一些让人抓狂的问题,这里把我实际遇到的典型情况列一下:

  • 环境启动失败或超时:题目容器需要时间初始化。等待并刷新页面通常是第一方案,如果长时间无响应可以尝试重新生成环境。
  • 页面内容没更新:浏览器缓存非常容易干扰。建议打开调试工具并勾选"Disable cache",或者直接用Burp的Repeater查看原始响应。
  • 路径大小写敏感:BUUCTF的题目环境多为Linux,目录和文件名是严格区分大小写的,Upload.php和upload.php完全是两个文件。
  • PHP版本差异:同一道赛题在网上找到的writeup可能是旧环境、旧PHP版本,某些绕过手法在新版本中可能失效。这时候要回归到源码本身去分析,而不是盲目抄payload。
  • 容器外网访问限制:有些题目环境内部会主动对外发起请求,但外网回连被限制。做带出数据的题目时,优先考虑平台提供的交互接口,而不是自己的外网服务器。
  • flag文件路径:很多题目的flag不是放在当前目录,而是在根目录、/flag、flag.txt等不同位置。执行命令时建议先find / -name "*flag*"或ls -la /,不要死等当前目录。

这些看似环境性的问题,在真实刷题过程中消耗的时间往往比漏洞利用本身还多。我后来养成了一个习惯:拿到题目先把环境基础信息摸清楚——PHP版本、是否Linux、flag常见位置,再开始正式解题,能避免很多无用功。

最后说点实在的

刷BUUCTF Web第二页这件事,本质上不是刷"第二页",而是从"会用工具"走向"会组合思路"的过程。我在这个阶段最大的体会是:所有看起来高级的利用链,拆开之后都是基础知识的组合。反而是那些最基础的伪协议、上传校验、参数审计,一旦理解透了,遇到什么五花八门的题目都有章可循。

如果你正在这个阶段挣扎,别急着跟别人对writeup,先自己把完整链路走一遍。哪怕卡在一个环节几小时,只要是你自己推出来的结论,后面遇到类似题目就会形成肌肉记忆。等到某天你看到一个陌生的题目页面,脑子里能自动开始推"这里可能有上传,那里可能可以读源码,这个参数有可能触发包含"的时候,真正的成长就发生了。

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

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

立即咨询