news 2026/8/8 14:43:44

PHP文件包含漏洞实战:从Warmup题解析伪协议利用与防御

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
PHP文件包含漏洞实战:从Warmup题解析伪协议利用与防御

1. 项目概述:从一道Warmup题看PHP文件包含的攻防博弈

最近在带新人入门CTF,发现很多朋友在Web安全赛道的起点——Warmup题目上就卡住了。这类题目通常设计精巧,旨在考察一个核心漏洞点的基本利用,而PHP文件包含漏洞就是其中最经典的“敲门砖”之一。它不像SQL注入那样需要复杂的闭合与绕过,也不像命令执行那样直接粗暴,文件包含更像是一把精巧的“万能钥匙”,通过服务器对文件路径的信任,去开启一扇扇本不该开启的门。今天,我们就从一个典型的Warmup题目出发,彻底拆解PHP文件包含漏洞的实战利用链条。无论你是刚接触CTF的新手,还是想巩固Web安全基础的老兵,这篇文章都将带你从“看到题目一脸懵”到“理解每一步操作背后的逻辑”,真正掌握这种漏洞的攻防精髓。

2. 核心漏洞原理与PHP伪协议武器库

2.1 文件包含漏洞的本质:信任的滥用

文件包含漏洞的核心,源于开发者在编写代码时,将用户可控的输入直接或间接地传递给了文件包含函数(如includerequireinclude_oncerequire_once)。在PHP中,这些函数的本意是提高代码复用性,比如根据用户选择的语言加载不同的模板文件:include($_GET['lang'] . '.php')。问题就出在这个$_GET['lang']上。

攻击者可以传入的远不止'en''zh'。他们可以传入../../../etc/passwd尝试读取系统文件,或者传入php://input并附上恶意代码,让服务器执行。服务器之所以会照做,是因为它“信任”了开发者设定的逻辑路径,却没有对用户输入进行严格的“身份验证”和“路径限制”。这就好比你把家门钥匙藏在脚垫下,本意是方便自己,却让所有知道这个秘密的人都能随意进出。

在CTF中,这类漏洞的利用通常分为两个层次:本地文件包含(LFI)远程文件包含(RFI)。LFI是指包含服务器本地文件系统上的文件,而RFI则能包含远程服务器(攻击者控制)上的文件。由于现代PHP默认配置(allow_url_include=Off)通常禁用了RFI,因此CTF题目和真实环境中更常见的是LFI。我们的挑战就在于,如何在只能包含本地文件的前提下,实现信息泄露甚至代码执行。

2.2 PHP伪协议:将文件包含转化为攻击利刃

如果只能包含.php文件,那漏洞的危害将大打折扣。PHP内置的众多封装协议(Wrapper Protocols),俗称伪协议,彻底改变了游戏规则。它们允许我们以“协议”的方式去访问各种输入/输出流,在文件包含的上下文中,这成了攻击者的强大武器库。下面我们重点剖析几个在CTF中最常出镜的“明星协议”。

php://filter:文件内容读取器这是LFI漏洞中用于读取源码的“神器”。它的核心作用是给数据流套上一层或多层“过滤器”。在文件包含时,我们可以用它读取目标文件的原始内容,并以编码(如Base64)的形式输出,从而绕过源码直接执行导致的空白页问题。

?file=php://filter/read=convert.base64-encode/resource=flag.php

这行Payload的意思是:使用php://filter协议,应用convert.base64-encode过滤器,去读取resource指定的flag.php文件。服务器会执行包含操作,但由于我们要求将文件内容Base64编码后再包含,PHP引擎会先读取flag.php的源码,进行编码,然后将编码后的文本(而非可执行的PHP代码)作为内容输出。这样我们就得到了flag.php的Base64编码字符串,解码后即可查看源码寻找flag。

注意read=参数在某些情况下可以省略,因为php://filter默认的处理模式就是读取。但明确写上是个好习惯,也能帮助你理解数据流的方向。

php://input:直接代码执行通道这个协议允许你访问请求的原始主体(raw POST data)。当它被用于文件包含时,服务器会将POST过去的数据当作PHP代码来执行。

GET /?file=php://input POST <?php system('ls');?>

这里,file参数指定了包含php://input流。紧接着在POST数据体中,我们直接写入PHP代码system('ls');。服务器在包含这个“文件”时,实际上执行了我们POST的代码。这是一种非常直接的“无文件”代码执行方式,但前提是allow_url_include配置需要为On,在部分CTF题目中会特意开启此配置来考察。

data://:内联代码注入器data协议可以将一段定义好的数据内联为文件流。它非常适合用于注入简短的代码片段。

?file=data://text/plain,<?php phpinfo();?> // 或者使用Base64编码绕过某些过滤 ?file=data://text/plain;base64,PD9waHAgcGhwaW5mbygpOz8+

第一行直接注入phpinfo()代码。第二行做了同样的事情,但代码经过了Base64编码。data://协议会识别;base64,标识,自动解码后面的内容。这在绕过对<?php等标签的简单过滤时非常有效。

zip:// 与 phar://:压缩包内的攻击这两个协议常用于文件上传+包含的组合攻击场景。假设你能上传一个ZIP或PHAR压缩包,并且服务器存在文件包含点,且你知道压缩包在服务器上的存储路径,你就可以直接包含压缩包内的文件。

// 假设上传了shell.zip,内含shell.php,保存在/tmp/shell.zip ?file=zip:///tmp/shell.zip%23shell.php // 注意#号需要URL编码为%23

phar://协议功能更强大,它不仅能包含文件,其元数据(metadata)反序列化特性还能触发其他漏洞。这在CTF中属于进阶考点。

理解这些协议是第一步,更重要的是理解它们在不同过滤场景下的变形与组合。接下来,我们就进入实战,看看如何将这些知识应用于解题。

3. 实战案例深度拆解:一道Warmup题的完整攻防

我们虚构一道典型的Warmup题目,它融合了多个常见的考点。题目源码(index.php)简化后如下:

<?php highlight_file(__FILE__); $file = $_GET['file']; if(isset($file)){ if (stristr($file, 'flag') !== false) { die('Hacker!'); } if (stristr($file, 'php://') !== false || stristr($file, 'data://') !== false) { die('Filtered!'); } include($file); } else { show_source(__FILE__); } ?>

3.1 代码审计与思路分析

首先,我们进行代码审计,这是解题的“地图”。

  1. 漏洞点:第8行,include($file);,用户通过GET参数file完全控制包含的文件路径。
  2. 第一层过滤:第5-6行,使用stristr检查$file中是否包含字符串'flag'(不区分大小写)。如果包含,直接终止脚本并输出'Hacker!'。这阻止了我们直接包含flag.php或路径中含flag的文件。
  3. 第二层过滤:第7-8行,同样使用stristr检查$file是否包含'php://''data://'。如果包含,终止并输出'Filtered!'。这直接封堵了最常用的php://filterdata://协议。

题目意图很明显:它告诉你漏洞在哪,但给你设置了两道障碍。我们的任务就是寻找绕过方法。思路链如下:目标肯定是读取flag.php-> 直接包含被flag关键词拦截 -> 尝试用伪协议读取被php://data://拦截 -> 需要找到过滤的弱点或使用其他协议

3.2 绕过策略与实操Payload构造

绕过“flag”关键词过滤stristr是字符串匹配函数,我们的Payload中不能出现flag这个子串。但我们要读的文件名很可能就是flag.php。怎么办?

  1. 路径遍历:也许flag不在当前目录,或者文件名不叫flag.php?可以尝试包含常见路径或进行目录爆破,但Warmup题通常不会这么复杂。
  2. 编码绕过stristr检查的是原始字符串,不会自动解码。但PHP在包含文件时,会对路径进行一定的处理。我们可以尝试使用URL编码双重URL编码
    • flag的URL编码是%66%6c%61%67。尝试?file=php://filter/.../resource=%66%6c%61%67.php。但注意,第二层过滤也会检查php://
  3. 利用PHP伪协议的特性:这是本题的关键。php://filter在读取文件时,resource参数指定的文件名,是否也会被stristr检查呢?答案是:。因为stristr检查的是整个$file参数字符串。所以?file=php://filter/.../resource=flag.php会因为包含flag而被第一层过滤拦截。

那么突破口在哪?在于第二层过滤只检查了php://data://,但没有检查其他写法

绕过“php://”过滤stristr是大小写敏感的吗?不,stristr中的i就代表case-insensitive,即不区分大小写。所以PHP://PhP://都会被匹配到,此路不通。 但是,PHP在处理协议时,协议名本身是大小写不敏感的吗?是的!这是一个关键点。php://filterPHP://FILTER对PHP引擎来说是等价的。然而,过滤函数已经堵死了所有大小写变种。

我们需要换个思路:有没有其他伪协议?或者,有没有办法让php://这个字符串“消失”或“变形”?

终极绕过:利用php://filter的编码过滤器链php://filter的强大之处在于可以串联多个过滤器。其中有一类过滤器叫convert.iconv.*,用于字符集转换。我们可以利用它进行一个“偷梁换柱”的操作:在传输过程中改变Payload的样貌,使其绕过过滤,在被包含时又恢复原状

构造Payload如下:

?file=php://filter/convert.iconv.UTF-8.UTF-16/resource=flag.php

或者更复杂一些:

?file=php://filter/convert.iconv.UTF-8.UTF-16|convert.iconv.UTF-16.UTF-8/resource=flag.php

为什么这样能绕过?

  1. 绕过“flag”过滤:注意,我们的resource参数写的仍然是flag.php。如果直接提交,肯定被第一层过滤拦截。但是,convert.iconv过滤器会改变整个数据流(包括resource=flag.php这部分“元数据”吗?不,它主要改变的是resource指向的文件内容。对于绕过stristr($file, 'flag')的帮助可能有限,除非过滤逻辑有误。实际上,更常见的做法是利用编码将flag.php本身的内容进行转换输出。我们真正的目标不是让路径绕过检查,而是让包含flag.php这个动作能发生。既然路径中有flag会被拦,那我们能不能包含一个“中性”的文件,然后通过过滤器“间接”读到flag.php的内容?这通常需要配合其他漏洞,如日志注入、会话文件包含等。在本题的简单模型中,我们假设第一层过滤存在逻辑缺陷(例如只在某些情况下触发),或者flag文件的名字并非flag.php(如fl4g.php),我们通过目录遍历发现它后,用此Payload读取。这是一种综合考察。

  2. 更常见的场景是绕过php://过滤:有些粗糙的过滤函数(如str_replace)可能只替换一次php://。我们可以使用phphp://,经过一次替换后变成php://。但本题用的是stristr,是检查是否存在,此方法无效。

因此,对于本题的严谨解法,我们需要重新审视题目。一个更合理的Warmup考点是:过滤了php://data://,但没过滤zip://phar://,同时题目存在文件上传点(或已知服务器上存在一个可控的zip文件)。或者,过滤函数存在缺陷,例如:

if (str_replace('php://', '', $file) !== $file) { die('Filtered!'); }

这时,我们可以用?file=phphp://input来绕过,因为str_replace执行后,字符串中的php://被移除,但剩下的phinput组合不成php://,检查通过,而最终包含时,路径是phphp://input,PHP能正确识别吗?实际上,phphp://不是合法协议,会失败。正确的绕过是?file=php://filter/convert.base64-encode/resource=flag.php,但php://被替换为空,变成了filter/convert...,这也不是合法路径。所以这种过滤本身就有问题,但现实中确实存在。

假设本题过滤存在缺陷,我们最终构造的读取flag的Payload可能是:

?file=PHP://filter/convert.base64-encode/resource=./fl4g.php

(假设我们通过信息收集或目录遍历得知flag文件名为fl4g.php,且PHP://的大小写混淆成功绕过了过滤函数,因为有些自定义过滤函数可能只用strpos检查小写php://)。

实操心得:在真实解题中,第一步一定是测试过滤规则。通过提交?file=test?file=../?file=php://等,观察回显信息(是Hacker!还是Filtered!或是文件不存在的警告),来精确摸清过滤的边界和逻辑。这比盲目猜测要高效得多。

4. 漏洞利用的扩展场景与高级技巧

文件包含漏洞的威力远不止于读取一个flag.php。一旦与服务器其他特性或漏洞结合,就会形成强大的攻击链。

4.1 日志文件包含(Log Poisoning)

这是LFI实现代码执行最经典的手段之一。Web服务器(如Apache、Nginx)和PHP自身都会生成日志,记录访问请求、错误信息等。这些日志文件通常有固定的位置,且内容部分可控(如User-Agent、Referer、请求路径)。

  1. 原理:将PHP代码写入日志文件,然后通过LFI漏洞包含该日志文件,从而执行代码。
  2. 步骤
    • 定位日志路径:常见路径如/var/log/apache2/access.log/var/log/nginx/access.log/proc/self/fd/xx(指向当前进程的文件描述符,可能包含stderr)。
    • 投毒日志:发送一个请求,在User-Agent或Referer等字段中携带PHP代码,例如:User-Agent: <?php system($_GET['c']);?>
    • 包含日志:使用LFI漏洞包含这个日志文件:?file=/var/log/apache2/access.log&c=ls
  3. 注意事项:日志文件通常很大,包含可能导致超时或内存耗尽。需要精准定位到包含恶意代码的那一行附近,有时需要多次尝试或利用tail命令的思路(但需要通过LFI执行命令才行,这就成了先有鸡还是先有蛋的问题)。通常,先包含日志确认路径和格式,再投毒。

4.2 会话文件包含(Session Poisoning)

PHP的会话(Session)机制会将$_SESSION数组序列化后存储到服务器的一个临时文件中(如/tmp/sess_[sessionid])。如果我们可以控制$_SESSION中的部分数据,并且知道或能猜测会话文件的路径,就可以实现类似日志包含的效果。

  1. 原理:在用户可控的Session变量中注入PHP代码,然后通过LFI包含该会话文件。
  2. 条件
    • 存在LFI漏洞。
    • 服务器使用文件存储Session(默认)。
    • 我们知道Session ID(通常通过Cookie中的PHPSESSID传递),从而能推算出文件路径(/tmp/sess_[PHPSESSID])。
    • 有办法向$_SESSION中写入可控数据(例如,存在一个“设置昵称”的功能,将昵称存入$_SESSION['nickname'])。
  3. 利用:将昵称设置为<?php system($_GET['cmd']);?>,然后包含文件/tmp/sess_[你的PHPSESSID]

4.3 配合文件上传获取Webshell

这是非常常见的组合拳。如果网站同时存在文件上传漏洞文件包含漏洞,但上传的文件被重命名、移动到了非Web目录、或后缀被强制改为图片格式,导致无法直接访问执行。

  1. 原理:上传一个图片马(内容为<?php system($_GET[‘c’]);?>的图片文件),然后通过LFI漏洞去包含这个上传后的文件。因为include()函数并不关心文件后缀,只要文件内容包含有效的PHP代码,它就会执行。
  2. 关键:你需要知道上传文件在服务器上的完整绝对路径。这可能需要结合目录遍历、报错信息泄露或上传功能本身的回显来获取。

4.4 利用环境变量与特殊文件

Linux系统中有一些特殊的文件,其内容可以被用户间接影响,从而用于包含攻击。

  • /proc/self/environ:包含了当前进程的环境变量。其中HTTP_USER_AGENTHTTP_REFERER等可由HTTP请求控制。攻击方式与日志包含类似。
  • /proc/self/fd/[数字]:文件描述符。有时Web服务器的错误日志会重定向到某个文件描述符,包含它可能读到错误日志,进而实施日志投毒。
  • php://tempphp://memory:这些是可读写的临时流。如果脚本先向其中写入了数据(可能通过其他参数可控),再包含它,也可能造成代码执行。但这需要非常特殊的代码逻辑。

5. 防御策略与安全编程实践

理解了攻击,才能更好地防御。作为开发者,应如何避免文件包含漏洞?

5.1 白名单机制:最有效的防御

绝对不要信任用户输入。如果必须根据输入动态包含文件,请使用白名单

$allowed_pages = ['home.php', 'about.php', 'contact.php']; $page = $_GET['page']; if (in_array($page, $allowed_pages)) { include('./templates/' . $page); } else { include('./templates/404.php'); }

将允许包含的文件名限定在一个明确的、预定义的列表中。

5.2 固定目录与后缀剥离

如果做不到白名单,也要严格限制文件路径。

  • 固定目录:将所有可包含的文件放在一个特定目录下,并在包含前拼接固定路径。
    $file = './includes/' . basename($_GET['module'] . '.php'); include($file);
    basename()函数可以防止目录遍历(如../../../etc/passwd会被处理为passwd)。
  • 强制后缀:自动添加后缀,避免用户控制后缀名。
    $file = $_GET['file'] . '.php'; // 用户传入`data://text/plain,<?php...`会变成`data://text/plain,<?php....php`,无法被正确解析。 include('./pages/' . $file);
    但要注意,如果用户传入file=../../../etc/passwd%00,在旧版本PHP(<5.3.4)中,%00(空字节)会截断后面的.php,依然危险。因此要确保PHP版本安全,并结合其他方法。

5.3 禁用危险的PHP配置

php.ini中进行全局安全加固:

  • allow_url_fopen = Off:禁止通过URL打开文件。
  • allow_url_include = Off务必关闭,这是防止RFI的最重要设置。
  • open_basedir = /var/www/html:将PHP可操作的文件范围限制在Web目录内,可以有效防御目录遍历读取系统文件。
  • disable_functions = system,exec,passthru,shell_exec,...:禁用危险函数,即使攻击者实现了代码执行,也无法调用系统命令,极大增加攻击难度。

5.4 输入验证与过滤

对用户输入进行严格的验证和过滤。

  • 类型检查:确保输入是预期的类型。
  • 正则表达式过滤:过滤掉../php://data://等危险字符串。但要注意绕过技巧(如双重编码、大小写、超长字符串截断等)。
  • 使用安全函数:如realpath()函数可以解析路径中的符号链接和../,并返回绝对路径,然后检查这个绝对路径是否在白名单目录下。

5.5 代码审计与安全测试

在开发流程中引入安全环节。

  • 静态代码分析(SAST):使用工具自动扫描代码中的危险函数(include,require等)和用户输入点。
  • 动态应用安全测试(DAST):使用漏洞扫描器对运行中的应用进行测试。
  • 人工代码审计:对涉及文件操作、包含、加载的代码进行重点审查。

文件包含漏洞如同一面镜子,映照出“信任”与“安全”的永恒博弈。从一道简单的Warmup题目入手,我们不仅看到了一个漏洞点的利用,更串联起了代码审计、协议理解、绕过技巧、组合利用和最终防御的完整知识链。在CTF中破解它,是为了在真实世界里更好地守护它。每一次成功的绕过,都应当转化为一行更严谨的代码。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/8/8 14:41:41

从BLG闭麦事件看技术团队协作:沟通机制与单点故障预防

最近关注LPL的观众可能都注意到了&#xff0c;BLG战队在季后赛关键阶段传出的“队内氛围”问题。这并非简单的赛场失误讨论&#xff0c;而是一个在高压竞技环境下&#xff0c;团队协作如何从内部瓦解的典型案例。对于从事技术开发、项目管理的我们而言&#xff0c;这支顶尖队伍…

作者头像 李华
网站建设 2026/8/8 14:40:39

打破窗口限制:用Window Resizer强制调整任意窗口大小的终极指南

打破窗口限制&#xff1a;用Window Resizer强制调整任意窗口大小的终极指南 【免费下载链接】WindowResizer 一个可以强制调整应用程序窗口大小的工具 项目地址: https://gitcode.com/gh_mirrors/wi/WindowResizer 还在为那些顽固的Windows窗口而烦恼吗&#xff1f;有些…

作者头像 李华
网站建设 2026/8/8 14:36:39

洛雪音乐音源配置终极指南:如何免费解锁全网无损音乐

洛雪音乐音源配置终极指南&#xff1a;如何免费解锁全网无损音乐 【免费下载链接】lxmusic- lxmusic(洛雪音乐)全网最新最全音源 项目地址: https://gitcode.com/gh_mirrors/lx/lxmusic- 想要在洛雪音乐中畅听全网音乐资源吗&#xff1f;lxmusic-项目为你提供了最完整的…

作者头像 李华
网站建设 2026/8/8 14:35:01

项目管理铁三角:范围、时间、成本的动态平衡艺术

1. 从“救火队长”到“掌舵人”&#xff1a;为什么你需要理解项目管理铁三角 如果你在项目经理这个位置上待过一段时间&#xff0c;大概率经历过这样的场景&#xff1a;客户突然提出要增加一个“小功能”&#xff0c;拍着胸脯说“就改一点点&#xff0c;不影响进度”&#xff1…

作者头像 李华
网站建设 2026/8/8 14:33:22

MyBatis-Plus乐观锁机制原理与高并发实战

1. MyBatis-Plus乐观锁机制深度解析 在涉及资金交易、库存管理等高频并发场景时&#xff0c;如何保证数据一致性是每个开发者必须面对的难题。传统方案往往直接使用数据库锁&#xff0c;但这会带来性能瓶颈和死锁风险。MyBatis-Plus提供的乐观锁机制&#xff0c;通过版本号比对…

作者头像 李华
网站建设 2026/8/8 14:33:15

Unity移动开发:Easy Touch 5.0.8手势控制插件深度解析与实战

1. 项目概述&#xff1a;为什么Easy Touch 5依然是移动开发的“瑞士军刀” 在Unity移动端开发里&#xff0c;输入处理是个绕不开的坎。尤其是多点触控&#xff0c;从简单的点击、拖拽到复杂的双指缩放、旋转&#xff0c;如果自己从零开始写&#xff0c;不仅要处理不同平台&…

作者头像 李华