File Inclusion(文件包含)
有些 web 应用程序允许用户指定直接文件流的输入,或允许用户将文件上载到服务器。稍后 web 应用程序访问 web 应用程序上下文中用户提供的输入。通过这样种操作,web 应用程序允许恶意文件执行。
如果选择要包含的文件是目标计算机上的本地文件,则称为“本地文件包含(LFI)”。但是文件也可能包含在其他机器上,这样的攻击就是“远程文件包含(RFI)”。
当 RFI 不是一个选项时。在 LFI 中使用另一个漏洞(例如文件上传和目录遍历)通常可以达到相同的效果。注意术语“文件包含”与“任意文件访问”或“文件泄漏”不同。
请在仅使用文件包含的情况下,阅读 “../hackable/flags/fi.php” 中的五句名言。
Low Level
这允许直接输入到许多 PHP 函数中的一个,这些函数将在执行时包含内容。RFI 是否可行取决于 web 的服务配置。
源码审计
源码很简单,用 GET 方法接收文件路径,然后将其包含进来。服务器包含文件时,无论文件是否是 PHP 文件,都会尝试当做 PHP 文件来执行。如果文件内容确实是 PHP 文件,则会正常执行并返回结果,如果不是则会将文件内容回显到网页中,所以文件包含漏洞常常会导致任意文件读取与任意命令执行。
<?php
// The page we wish to display
$file = $_GET['page'];
?>
例如靶场提供了 3 个文件,单击这些文件就会显示其执行内容,同时 url 也会用 GET 方法传递参数。
攻击方式
page 参数指的是文件路径,因此我们先传个不存在的文件名测试一下,看看有没有报错信息。传递 page 参数为 “1.php”,靶场报错并返回了文件所在的路径。
Warning: include(1.php): failed to open stream: No such file or directory in D:\DVWA-master\vulnerabilities\fi\index.php on line 36
Warning: include(): Failed opening '1.php' for inclusion (include_path='.;C:\php\pear;../../external/phpids/0.6/lib/') in D:\DVWA-master\vulnerabilities\fi\index.php on line 36
因此我们直接用相对路径,访问上两级文件夹下的 “fi.php'” 文件,构造出 payload。
?page=..\..\hackable\flags\fi.php
漏洞复现
(1)先点击下面的链接,进行尝试,可以看到有结果直接返回了
(2)这个地方因为我知道他的phpinfo.php文件在他的前两级目录文件中,所以我在这里就直接进行尝试了(本地包含)
(3)尝试一下包含百度页面,发现直接成功了(远程包含)
Medium Level
开发人员已经阅读了 LFI/RFI 的一些问题,并决定过滤输入,然而他所使用的过滤还不够。
源码审计
源码如下,代码增加了 str_replace 函数对 page 参数进行了过滤。将 “http://”、“https://”替换为空阻止远程包含漏洞,将“../”、“..\” 替换为空阻止用相对路径访问文件。
<?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 函数替换后的 page 参数是文件路径即可。
?page=...\.\...\.\hackable\flags\fi.php
漏洞复现
构造一个新的
hthttp://tp://www.baidu.com
过滤后
http://www.baidu.com
过滤前
…/./
过滤后
../
High Level
开发者已经受够了,他们决定只允许使用某些文件。但是由于存在多个具有相同基名的文件,因此它们使用通配符将它们全部包括在内。
源码审计
源码如下,使用 fnmatch 根据指定的模式来匹配文件名或字符串。该函数将检查 page 参数开头必须是否是 file,若是服务器才会去包含相应的文件。
<?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;
}
?>
攻击方式
虽然是只能包含 file 开头的文件,但是实在太巧合了,我们依然可以利用 file 伪协议读取到文件内容。
?page=file:///D:\DVWA-master\hackable\flags\fi.php
漏洞复现
使用伪协议:file:// (访问本地文件系统)(伪协议文章参考:https://blog.csdn.net/Wu000999/article/details/101925271)
Impossible Level
开发人员只对允许的页面进行硬编码,并提供精确的文件名,这样做消除了所有的攻击途径。
<?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难度的代码使用了白名单机制进行防护,简单粗暴,page参数必须为“include.php”、“file1.php”、“file2.php”、“file3.php”之一,彻底杜绝了文件包含漏洞。
总结与防御
为了更好地使用代码的重用性,可以使用文件包含函数将文件包含进来,直接使用文件中的代码来提高重用性。但是这也产生了文件包含漏洞,产生原因是在通过 PHP 的函数引入文件时,为了灵活包含文件会将被包含文件设置为变量,通过动态变量来引入需要包含的文件。此时用户可以对变量的值可控,而服务器端未对变量值进行合理地校验或者校验被绕过,就会导致文件包含漏洞。常用的文件包含函数有 include()、include_once()、require()、require_once()。
包含漏洞分为本地包含和原创包含 2 类,当包含的文件在服务器本地时,就形成了本地文件包含。文件包含可以包含任意文件,被包含的文件可以不是 PHP 代码,可以是文本或图片等。只要文件被包含就会被服务器脚本语言执行,如果包含的文件内容不符合 php 语法,会直接将文件内容输出。例如下面这段简易的代码:
<?php
$file = $_GET['file'];
include($file);
?>
当包含的文件在远程服务器上时,就形成了远程文件包含。所包含远程服务器的文件后缀不能与目标服务器语言相同,远程文件包含需要在 php.ini 中设置:
allow_url_include = on(是否允许 include/require 远程文件)
allow_url_fopen = on(是否允许打开远程文件)