一文读懂文件包含

目录:

一:概念:

​ 文件包含(File Inclusion)是一些对文件操作的函数未经过有效过滤,运行了恶意传入的非预期的文件路径,导致敏感信息泄露或代码执行。如果文件中存在恶意代码,无论什么样的后缀类型,文件内的恶意代码都会被解析执行,这就导致了文件包含漏洞的产生。

​ 随着网站的业务的需求,程序开发人员一般希望代码更加灵活,所以将被包含的文件设置为变量,用来进行动态调用,但是正是这种灵活性通过动态变量的方式引入需要包含的文件时,用户对这个变量可控而且服务端又没有做合理的校检或者校检被绕过就造成了文件包含漏洞。

二:成因:

​ 后端编程人员一般会把重复使用的函数写到单个文件中,需要使用时再直接调用此文件即可,该过程也就被称为文件包含。文件包含的存在使得开发变得更加灵活和方便,但同时也带了安全问题,导致客户端可以远程调用文件,造成文件包含漏洞。

​ 实际上被包含文件可以是任意格式的,可以是图片、文本、源代码等等。只要文件被包含其内容也会被包含,并以当前服务器脚本语言执行

​ 大多数Web语言都支持文件包含操作,其中PHP语言所提供的文件包含功能太强大、太灵活,也就导致文件包含漏洞经常出现在PHP语言中。这里就以PHP语言为例,需要打开一下配置allow_url_fopen=On(默认为On)规定是否允许从远程服务器或者网站检索数据。allow_url_include=On(php5.2之后默认为Off)规定是否允许include/require远程文件。

三:文件包含函数:

3.1:include(),require():

​ include():当使用该函数包含文件时,只有代码执行到include()函数时才将文件包含进来,发生错误时只给出一个警告(E_WARNING),继续向下执行.

​ require()与include()的区别在于require()执行如果发生错误(E_COMPILE_ERROR),函数会输出错误信息,并终止脚本的运行

3.2:include_once(),require_once():

​ 功能与Include(),require()相同,区别在于当重复调用同一文件时,程序只调用一次

四:类别:

4.1:本地文件包含(Loacl File Inclusion,LFI):

​ 当被包含的文件在服务器本地。本地文件包含漏洞指的是能打开并包含本地文件的漏洞,大部分情况下遇到的文件包含漏洞都是LFI

4.1.1:示例:

<?php
	$filename = $_GET['filename'];
	if(isset($filename)){
	include("$filename");
	}else{
	echo "file not found!";
	}
?>

image-20230805101046558

image-20230805101203796

image-20230805101227132

4.2:远程文件包含(Remote File Inclusion,RFI):

​ 当被包含的文件在远程服务器。由于远程服务器的文件是我们可控的,因此漏洞一旦存在危害性会很大。

远程文件包含需要在php.ini中开启php的allow_url_fopen=Onallow_url_include=On

image-20230805101419129

4.2.1:示例:

//远程文件包含漏洞,需要php.ini的配置文件符合相关的配置
$html='';
if(isset($_GET['submit']) && $_GET['filename']!=null){
    $filename=$_GET['filename'];
    include "$filename";//变量传进来直接包含,没做任何的安全限制
}

image-20230805102509498

五:漏洞利用:

伪协议:PHP 带有很多内置 URL 风格的封装协议,可用于类似 fopen()、 copy()、 file_exists() 和 filesize() 的文件系统函数。

file:// — 访问本地文件系统
http:// — 访问 HTTP(s) 网址
ftp:// — 访问 FTP(s) URLs
php:// — 访问各个输入/输出流(I/O streams)
zlib:// — 压缩流
data:// — 数据(RFC 2397)
glob:// — 查找匹配的文件路径模式
phar:// — PHP 归档
ssh2:// — Secure Shell 2
rar:// — RAR
ogg:// — 音频流
expect:// — 处理交互式的流

5.1:php://input:

php://input可以访问请求的原始数据的只读流将post请求的数据当作php代码执行。当传入的参数作为文件名打开时,可以将参数设为php://input,同时post想设置的文件内容,php执行时会将post内容当作文件内容。从而导致任意代码执行。

image-20230805125926315

image-20230805130000931

tips:需要开启allow_url_include=on,对allow_url_fopen不做要求。

5.2:php://filter:

php://filter可以获取指定文件源码。当它与包含函数结合时,php://filter流会被当作php文件执行。所以我们一般对其进行编码让其不执行。从而导致任意文件读取。

5.2.1:适用范围:

include()
file()
file_get_contents()
readfile()
file_put_contents()
可以用于读取、写入文件等函数

5.2.2:通用语法:

php://filter/过滤器|过滤器/resource=待过滤的数据流
过滤器可以设置多个,按照链式的方式依次对数据进行过滤处理
对phpinfo()进行两次base64编码
php://filter/read=convert.base64-encode|convert.base64-encode/resource=data://text/plain,<?php phpinfo();?>

5.2.3:四类过滤器:

5.2.3.1:字符串过滤器:

​ 以string字符串开头,常见的过滤器有rot13touppertolowerstrip_tags等,例如:

# string.rot13即对数据流进行str_rot13函数处理
echo php://filter/read=string.rot13/resource=data://text/plain,abcdefg
# 输出结果为nopqrst
# toupper、tolower是对字符串进行大小写转换处理:
php://filter/read=string.toupper/resource=data://text/plain,abcdefg
# 输出结果为ABCDEFG

image-20230805132152635

# strip_tags对数据流进行strip_tags函数的处理,该函数功能为剥去字符串中的 HTML、XML 以及 PHP 的标签,简单理解就是包含有尖括号中的东西
php://filter/read=string.strip_tags/resource=data://text/plain,<a>s</a>
# 输出结果为s

image-20230805132444110

5.2.3.2:转换过滤器:

​ 主要含有三类,分别是base64的编码转换、quoted-printable的编码转换以及iconv字符编码的转换。该类过滤器以convert开头

# base64的编码转换操作,例如:
php://filter/read=convert.base64-encode/resource=data://text/plain,hel
# 输出aGVs

image-20230805134418107

# Quoted-printable可译为可打印字符引用编码,可以理解为将一些不可打印的ASCII字符进行一个编码转换,转换成=后面跟两个十六进制数
php://filter/read=convert.quoted-printable-encode/resource=data://text/plain,m1sn0w".chr(12)
# 输出为m1sn0w=0C
# iconv过滤器也就是对输入输出的数据进行一个编码转换,其格式为convert.iconv.<input-encoding>.<output-encoding>或者convert.iconv.<input-encoding>/<output-encoding>,表达的意思都是相同的,即将输入的字符串编码转换成输出指定的编码
php://filter/read=convert.iconv.utf-8.utf-16/resource=data://text/plain,m1sn0w

5.2.3.3:压缩过滤器

主要有两类,zlibbzip2,其中zlib.deflate和bzip2.compress用于压缩,zlib.inflate和bzip2.decompress用于解压缩。

5.2.3.4:加密过滤器

以mcrypt开头,后面指定一个加密算法。本特性已自PHP 7.1.0起废弃。强烈建议不要使用本特性。

5.3:zip://伪协议:

zip://可以访问压缩文件中的文件

前提:使用zip协议,需要使用绝对路径,并且要将#编码为%23,所以需要PHP的版本>=5.3.0,如果因为版本的问题无法将#编码成%23,可以手动把#改成%23。
file=zip://test.zip(文件后缀)#shell.php

5.4:phar://伪协议:

​ 与zip://协议类似但用法不同,zip://伪协议中是用#把压缩文件路径和压缩文件的子文件名隔开,而phar://伪协议中是用/把压缩文件路径和压缩文件的子文件名隔开

file=phar://test.phar(文件后缀)/shell.php

5.5:data:text/plain:

​ 和php伪协议的input类似,也可以执行任意代码,但利用条件和用法不同

前提:PHP的版本>=5.2,并且allow_url_fopen参数与allow_url_include都需开启
file=data:text/plain,<?php echo "1" ?>
file=data://,<?php phpinfo();
file=data://text//plain,<?php phpinfo();?>
file=data://text/plain;base64,xxxxxxxxx

image-20230805142233527

5.6:file://伪协议:

file://用于访问本地文件系统,且不受allow_url_fopenallow_url_include的影响。

前提:包含的文件要文件的绝对路径
filename=file://E:/phpstudy_pro/WWW/test/2.txt(文件绝对路径)

image-20230805142509851

六:修复以及防护:

6.1:访问路径限制

​ PHP中使用open_basedir配置限制访问在指定的区域,限制被包含的文件只能是某一文件夹内。

6.2:过滤输入

​ 过滤.(点)/(反斜杠)\(反斜杠)等特殊字符,禁止目录跳转字符如../。

6.3:关闭高危配置

​ PHP文件配置allow_url_include和allow_url_fopen最小权限化。

6.4:白名单

​ 对需要包含的文件设置文件白名单,包含文件的验证。

6.5:避免参数

​ 尽量不要使用动态包含,可以在需要包含的页面固定写好。

posted @ 2023-08-29 14:28  2xixi3  阅读(111)  评论(0编辑  收藏  举报