在各种开发语言中都提供了内置的文件包含函数,其可以使开发人员在一个代码文件中直接包含(引入)另外一个代码文件。
通过文件包含函数将文件包含进来,直接使用包含文件中的代码。
大多数情况下,文件包含函数中包含的代码文件是固定的,因此也不会出现安全问题。
但有些时候,使用函数包含文件时,被包含的文件就设置为了函数的变量。
通过动态变量来引入需要包含的文件时,用户可以对变量的值可控而服务器端未对变量值进行合理地校验或者校验被绕过,这样就导致了文件包含漏洞。
通常文件包含漏洞出现在PHP语言中。
PHP中的常用文件包含函数:
include()
include_once()
require()
require_once()
include()
代码执行到这个函数时,include() 函数才行使功能,将文件包含进来。
如果发生错误会给出一个警告,继续向下执行。
include_once()
功能和前面的include() 函数是一样的,区别在于当重复调用同一文件时,程序只会调用一次。
require()
与include() 函数的区别在于,当 require() 这个文件包含函数执行错误的时候,函数会输出错误信息,并且整个脚本会停止运行。而 include() 函数会继续向下执行。
require_once()
功能和require() 函数相同,区别在于当重复调用同一文件时,程序只会调用一次。
例如:
$file = $_GET['file'];
include($file);
……
?>
这里的代码中就可以控制可控参数file 来控制包含的 $file 的值。
被包含的文件并不只限于一些编程语言的代码,可以为图片等等,只要文件被函数包含,文件的内容就会被包含,并以当前服务器脚本语言执行。
本地文件包含漏洞
远程文件包含漏洞
file:// (访问本地文件系统)
php:// (访问各个输入/输出流)
zip:// (压缩流)
data:// (数据)
phar:// (PHP归档)
php://filter(用于读取源码)
如:
php://filter/read=convert.base64-encode/resource=1.php(1.php为文件名)
php://input(用于执行php代码,需要post请求提交数据)
前提条件:allow_url_fopen=on
file://[文件的绝对路径与文件名]
如:
file://D:/flag.txt
zip://[压缩文件绝对路径]#[压缩文件里的子文件名]
如:
把文件1.php 解压为 1.zip ,那压缩文件 1.zip 里的子文件就是 1.php
index.php?file=zip://D:/1.zip%231.php (%23是‘#’的 url 编码)
phar://[压缩文件绝对路径] [压缩文件里的子文件名]
如:
把文件1.php 解压为1.zip ,那压缩文件 1.zip 里的子文件就是 1.php
index.php?file=phar://D:/1.zip/1.php
前提条件:php<5.3 ,include=on
date://text/plain;base64,xxx(base64编码后的数据)
如:
index.php?file=date://text/plain;base64, PD9waHAgcGhwaW5mbygpPz4=
( ‘PD9waHAgcGhwaW5mbygpPz4=’是‘ ’的base64编码)
1、%00截断
有前提条件:
PHP版本小于5.3(大于等于5.3的PHP版本已经修复了这个问题)
PHP的magic_quotes_gpc = off
%00截断有点像是注释符,把程序终止,后面的字符就相当于没有了。
如:
$file = $_GET['file'] . '.php';
include($file);
……
?>
这里的文件包含限制了包含的文件为 .php ,而包含的文件是 .txt 格式。
那就可以使用%00截断:?file.txt%00
2、双写绕过
有一些字符会被过滤掉,那就使用双写绕过。
就像sql注入里的双写绕过,如果过滤了union,就可以写出uniunionon,能把中间的union过滤掉。
这里如果过滤的 “…/”,那就可以写出“…/./”,中间的“…/”会被过滤掉。
3、编码绕过
比如会对“…/”进行过滤,那就对“…/”进行编码后再使用
“…/”的 url 编码:“…%2F”