CTFshow文件包含漏洞实战:五种绕过姿势与防御策略详解

发布时间:2026/7/31 7:08:02

CTFshow文件包含漏洞实战:五种绕过姿势与防御策略详解 1. 项目概述一次对文件包含漏洞的深度狩猎文件包含漏洞在Web安全领域里算是个“老朋友”了但它的“生命力”却异常顽强。尤其是在CTF竞赛和渗透测试的实战场景中它往往是突破边界、获取权限的关键跳板。这次我们聚焦于CTFshow平台上的文件包含挑战目标不仅仅是“做出来”而是要彻底搞懂从基础的PHP伪协议利用到进阶的日志文件包含再到各种过滤规则下的花式绕过这背后完整的攻击链和防御逻辑。如果你对PHP的include、require等函数如何被“玩坏”感到好奇或者在实际测试中遇到过滤了../、php://就束手无策那么这篇从实战中总结的笔记或许能给你带来一些新的思路。我们将拆解五种核心的绕过姿势每一种都配有详细的原理分析、实操步骤和踩坑记录力求让你不仅能复现更能理解为什么这么做能行。2. 漏洞原理与核心思路拆解2.1 文件包含漏洞的本质是什么简单来说文件包含漏洞源于应用程序在动态包含文件时未对用户控制的输入进行严格校验。在PHP中include、require、include_once、require_once这些函数如果其参数通常是文件路径可以被攻击者操控就可能导致非预期的文件被读取或执行。这不仅仅是读取敏感文件如/etc/passwd那么简单更深层的危害在于它能与其它漏洞如文件上传结合实现远程代码执行RCE。关键在于“可控的输入点”。这个输入点可能来自$_GET、$_POST、$_COOKIE甚至是$_SERVER中的某些变量如HTTP_REFERER。攻击者的目标就是通过这个输入点让程序去包含一个它本不该包含的文件。理解这一点就理解了所有绕过手法的出发点无论过滤规则多么复杂最终目的都是要构造一个能被目标系统正确解析、但又能指向我们恶意内容的“路径字符串”。2.2 CTFshow题目环境常见设防分析CTFshow的题目为了增加难度通常会设置多层过滤。常见的防御姿势包括关键词过滤过滤../目录遍历、php://伪协议、data://数据流、input等敏感字符串。可能使用str_replace()、preg_match()或waf进行拦截。后缀限制强制在包含路径后添加固定的后缀如.php、.txt。例如用户输入filetest实际代码执行的是include($_GET[file] . .php)。协议黑/白名单只允许http://或file://协议或者明确禁止php://等。目录限制使用chdir()将当前目录切换到非Web目录或者用basename()函数取文件名试图剥离路径。我们的绕过思路就是针对这些过滤机制利用PHP语言特性和系统环境特性进行“变形”和“替代”。核心思路可以归纳为当直接路径被禁就用编码当协议被禁就找替代协议或利用已有文件当后缀被追加就用截断或利用协议特性忽略它。3. 五种核心绕过姿势详解与实战3.1 姿势一利用PHP伪协议——php://filter的读取与编码绕过这是最经典也是最常用的一招。php://filter协议本身用于在数据流打开时应用过滤器但在文件包含中它成了读取源码的利器。核心利用读取PHP文件源码当直接包含一个.php文件时它会被服务器执行我们看不到源代码。但通过php://filter我们可以先让文件内容经过base64编码或rot13等转换再输出这样就能得到编码后的源码解码即可。?filephp://filter/readconvert.base64-encode/resourceindex.php实操要点resource后面跟的是你要读取的文件路径可以是相对路径也可以是绝对路径。convert.base64-encode是最常用的过滤器因为输出结果规范易于解码。string.rot13有时在特定过滤下也能用。得到的是base64编码字符串需要在线或本地解码。绕过过滤技巧 如果题目过滤了php://或者filter等关键词可以尝试以下变种大小写绕过PHP://Filter、Php://部分环境下有效。多重编码绕过对完整的payload进行URL编码一次或两次。例如将php://filter/readconvert.base64-encode/resourceflag.php整体进行URL编码后传入。有时WAF只检查一次解码后的内容。利用convert.iconv.*过滤器这是一个更隐蔽的过滤器可以用于字符集转换在某些情况下也能用于读取文件但不如base64通用。例如convert.iconv.UTF-8.UTF-16LE输出会是另一种格式的“乱码”需要对应解码。注意php://filter的write过滤器在仅包含场景下通常无法直接用于写文件它主要用于读取。写文件需要php://input结合特定条件。3.2 姿势二数据流伪装——php://input与POST组合拳当目标服务器允许包含php://input流并且allow_url_include配置为On时我们可以直接执行POST过去的PHP代码。利用方法GET: ?filephp://input POST Body: ?php system(ls);?原理php://input是一个只读流可以访问请求的原始数据。当我们包含它时POST过去的数据会被当作PHP代码执行。关键条件与绕过allow_url_includeOn这是大前提。在CTF题目中常为开启状态但实际生产环境极少开启。过滤php://input如果被过滤同样可以尝试大小写、双重URL编码。有时甚至可以用PHP://INPUT。data://协议作为备选如果php://input被禁但allow_url_include为On且data://未被过滤可以使用data://text/plain,?php phpinfo();?或data://text/plain;base64,PD9waHAgcGhwaW5mbygpOz8base64编码后的代码。data://协议同样将后续内容作为输入流包含并执行。实操心得 使用php://input时务必使用POST方法传参并且请求头Content-Type不一定需要是application/x-www-form-urlencoded纯文本即可。Burp Suite中直接写在Raw里发送很方便。如果执行命令没回显可以尝试将输出写入一个文件或者使用curl外带数据到自己的服务器。3.3 姿势三日志投毒与包含——利用系统固有文件这是非常巧妙且实用的一招尤其在无法上传文件但可以包含日志文件的情况下。Web服务器如Apache、Nginx的访问日志会记录每一个请求包括请求行、头部。如果我们把PHP代码作为请求的一部分发送这段代码就会被原样记录到日志文件中。然后我们再利用文件包含漏洞去包含这个日志文件其中的PHP代码就会被执行。标准步骤定位日志路径常见路径如/var/log/apache2/access.log、/var/www/logs/access_log、/usr/local/nginx/logs/access.log等。可以通过报错信息、读取/proc/self/environ或/etc/apache2/apache2.conf等配置文件来猜测。投毒向网站发送一个包含PHP代码的请求。例如在User-Agent中插入代码User-Agent: ?php eval($_POST[cmd]);?。因为UA头会被记录到日志。包含日志使用文件包含漏洞包含已知路径的访问日志文件如?file/var/log/apache2/access.log。执行代码包含成功后日志文件中的PHP标签会被解析此时我们就可以通过POST传递cmd参数来执行命令了。绕过与技巧日志文件过大如果日志文件太大包含可能导致超时或内存不足。可以尝试在投毒后立即进行包含或者利用工具频繁请求产生新日志行。路径未知需要结合目录遍历或信息泄露漏洞来获取路径。有时默认路径就可行。编码问题如果日志对特殊字符进行了转义可能导致代码无法执行。可以尝试将PHP代码进行Base64编码然后在投毒时使用?php eval(base64_decode(...));?的方式。或者使用短标签? system(ls) ?它更短被转义破坏的可能性稍低。利用其他日志除了访问日志错误日志error.log也可能被包含有时错误信息中会包含部分路径有助于定位。这个姿势的魅力在于它利用了系统固有的、看似无害的文件将文件包含漏洞的利用从“需要上传文件”提升到了“只需发起请求”的维度。3.4 姿势四编码与截断的艺术——应对后缀限制当题目给用户输入自动添加后缀时例如include($_GET[file] . .php)我们需要想办法“截断”这个后缀或者让它失效。1. URL编码与双重编码这是最基本的绕过。服务器在接收到参数后通常会进行一次URL解码。如果WAF在解码前检查我们可以对敏感字符进行编码。过滤../尝试使用..%2f/的URL编码或%2e%2e%2f.和/的编码。在Windows环境下还可以用..\反斜杠或它的URL编码..%5c。双重编码如果服务器进行了两次URL解码那么我们可以对payload编码两次。例如../-%2e%2e%2f一次编码 -%252e%252e%252f二次编码%被编码为%25。这经常能绕过简单的字符串匹配。2. 长度截断PHP版本 5.3.4这是一个历史漏洞但在老环境或特定CTF题目中可能出现。原理是PHP在包含文件时如果路径长度超过一定限制通常是4096字节超出的部分会被截断。我们可以构造很长的路径让系统添加的后缀.php被挤在4096字节之后从而被截断丢弃。?filetest.txt/././././...重复非常多次./././实操要点这种方法依赖特定PHP版本和环境现在较少见但需要了解。3. 零字节截断PHP版本 5.3.4magic_quotes_gpcOff同样是历史漏洞但非常经典。在C语言中字符串以空字符\0URL编码为%00作为结束符。早期PHP在处理包含路径时如果遇到%00会认为字符串到此结束后面的内容被忽略。?file../../../../etc/passwd%00这样即使代码是include($file . .php)实际拼接后变成../../../../etc/passwd%00.phpPHP在读取时遇到%00就停止了成功包含/etc/passwd。注意这两个截断方法在现代PHP环境中已修复仅在特定复古场景或CTF中可能遇到。它们提醒我们环境版本信息是渗透测试中极其重要的一环。3.5 姿势五协议替换与路径遍历的奇技淫巧当常见的协议和路径遍历符号被严格过滤时我们需要一些“偏门”的协议和技巧。1. 利用file://协议和绝对路径如果php://被禁但file://协议未被禁或默认允许可以直接使用绝对路径。file://是本地文件传输协议。?filefile:///etc/passwd ?filefile:///var/www/html/config.php绕过点即使过滤了../只要你知道目标的绝对路径依然可以直接包含。获取绝对路径可以通过报错信息、读取/proc/self/cwd当前进程工作目录等符号链接来实现。2. 利用phar://协议PHP Archivephar://协议用于访问PHARPHP归档文件内的内容。一个精妙的利用点是PHAR的元数据metadata在反序列化时会自动触发__wakeup()或__destruct()等魔术方法。这意味着我们可以构造一个包含恶意序列化对象的PHAR文件通过文件包含或其它能触发反序列化的点来触发漏洞实现RCE。这在文件上传文件包含的组合场景中潜力巨大。简要利用步骤编写一个包含恶意类的PHP脚本并生成一个包含该类序列化对象的PHAR文件。将PHAR文件上传到服务器后缀可能是.jpg通过文件上传漏洞。通过文件包含漏洞包含这个PHAR文件?filephar://./uploads/evil.jpg/内部文件路径。即使包含时指定了内部文件在解析phar时元数据反序列化过程依然会被触发。3. 利用zip://协议与phar://类似zip://可以访问ZIP压缩包中的文件。我们可以将一个PHP木马压缩成ZIP然后上传再通过包含ZIP包内的文件来执行代码。关键点zip://协议需要指定压缩包内的具体文件路径并且路径中要用#号且#需要URL编码为%23。# 假设上传了 shell.zip里面有一个 shell.php 文件 ?filezip://./uploads/shell.zip%23shell.php4. 目录遍历的变形绝对路径跳转如果当前在/var/www/html想包含根目录下的文件直接用/etc/passwd绝对路径可能比../../../../etc/passwd相对路径更有效尤其当../被过滤时。利用软链接如果服务器上存在一些指向敏感目录的软链接包含它们可能达到目的。例如某些环境下的/proc/self/fd/目录下的文件描述符可能指向有趣的文件。非预期文件包含包含/dev/fd/0标准输入或/proc/self/environ环境变量如果其中存在可控数据如User-Agent在environ中也可能造成代码执行但条件较为苛刻。4. 实战场景串联与综合绕过案例假设我们遇到一道综合题过滤规则如下过滤了php://、data://、phar://等字符串。过滤了../和..\。自动为输入添加.php后缀。无法直接上传文件。我们的攻击链可以这样设计第一步信息收集。尝试读取index.php源码看看过滤逻辑。使用php://filter被禁尝试大小写PHP://或双重URL编码。如果不行尝试包含/proc/self/cmdline查看进程启动命令或者包含/etc/nginx/nginx.conf等配置文件寻找日志路径、Web根目录等线索。第二步寻找替代包含点。如果找到了日志路径如/var/log/nginx/access.log尝试进行日志投毒。将一句话木马写入User-Agent然后包含该日志文件。第三步利用编码绕过后缀。如果包含日志时代码是include($file . .php)我们传入/var/log/nginx/access.log%00如果环境支持零字节截断或者传入一个超长路径的日志路径长度截断。如果都不行考虑是否日志文件本身有.log后缀包含后拼接.php变成access.log.php这个文件不存在会导致包含失败。这时需要思考是否有可能让日志文件本身被解析为PHP如果服务器配置错误将.log文件也交给PHP解析那么包含日志文件本身就能执行代码无需截断后缀。但这属于配置层面非本题考察重点。第四步获取Shell。通过日志包含成功执行代码后使用system或passthru函数执行命令查找flag文件。或者如果允许直接写入一个Webshell到可写目录。这个案例展示了如何将多种姿势串联起来根据实际情况灵活应对。核心是永远不要只依赖一种方法当一条路被堵死立即根据已有信息寻找下一条路。5. 防御视角与排查技巧理解了攻击才能更好地防御。从开发和安全运维角度以下措施至关重要1. 严格的输入校验白名单机制这是最有效的方法。如果只需要包含固定的几个文件直接使用白名单。路径固定避免将用户输入直接拼接进包含路径。如果需要动态包含应基于一个固定的基础目录然后只允许用户输入文件名不含路径并使用basename()函数确保只有文件名部分。// 错误示例 include($_GET[page] . .php); // 改进示例仍不完美但稍好 $page basename($_GET[page]); // 剥离路径 $allowed_pages [home, about, contact]; if (in_array($page, $allowed_pages)) { include(./pages/ . $page . .php); } else { include(./pages/404.php); }2. 关闭危险配置在php.ini中确保allow_url_fopen Off和allow_url_include Off。这是防止远程文件包含RFI和php://input、data://等协议被利用的底线。确保magic_quotes_gpc已弃用且关闭现代PHP版本默认如此但不要依赖它作为安全手段。3. 安全编程习惯使用require_once或include_once代替require/include避免重复包含可能导致的问题虽然对漏洞本身无直接影响。对动态包含的文件路径进行正则匹配严格限制字符集如只允许字母、数字、下划线、短横线。考虑使用安全的文件映射或路由机制完全避免动态包含。4. 运维层面定期进行代码审计特别是检查所有包含函数include,require,include_once,require_once,file_get_contents等的参数是否用户可控。配置Web服务器如Nginx的日志权限确保日志目录不可被Web用户执行。保持PHP、Web服务器等中间件版本更新避免已知的截断类漏洞。排查技巧实录 在内部代码审计或渗透测试中如何快速定位潜在的包含漏洞全局搜索在项目代码中全局搜索include、require、include_once、require_once、file_get_contents、fopen等函数。追踪变量检查这些函数的参数是否是变量并向上追踪该变量的来源是否来自$_GET、$_POST、$_COOKIE、$_REQUEST或$_SERVER中的某些字段如$_SERVER[QUERY_STRING]、$_SERVER[PHP_SELF]这些也可能被污染。检查过滤查看对用户输入是否有过滤过滤逻辑是否严谨是否存在被绕过的可能如大小写、编码、截断。模拟测试在测试环境尝试使用各种payload伪协议、路径遍历、编码进行测试观察响应。注意查看错误日志有时错误信息会泄露路径。文件包含漏洞的攻防是一场关于“输入控制”的博弈。攻击方绞尽脑汁让输入“变形”以通过检查防御方则需在每一个环节设卡最小化攻击面。通过这次对五种绕过姿势的深度剖析我希望你收获的不只是几个payload而是那种面对黑盒过滤时如何层层拆解、灵活组合的思维方式。在实际工作中无论是攻击测试还是防御建设这种思维方式都比单纯记忆漏洞利用代码要重要得多。最后一个小建议搭建一个带有各种过滤规则的靶场环境自己亲手去尝试绕过遇到问题再去查阅资料这个过程带来的理解是最深刻的。

相关新闻