什么是文件包含
为了更好地使用代码的重用性,引入了文件包含函数,通过文件包含函数将文件包含进来,直接使用包含文件的代码,简单来说就是一个文件里面包含另外一个或多个文件。
漏洞成因
文件包含函数加载的参数没有经过过滤或者严格的定义,可以被用户控制,包含其他恶意文件,导致了执行了非预期的代码。
php
常见文件包含函数
函数 | 作用 |
---|---|
include_once() | 功能和include()相同,区别在于当重复调用同意文件时,程序只调用一次 |
require() | 使用require函数包含文件时,只要程序一执行,立即调用脚本;如果前者执行发生错误,函数或输出错误信息,并终止脚本运行 |
require_once() | 功能与require()相同,区别在于当重复调用同一文件时,程序只调用一次 |
include() | 当使用该函数包含文件时,只有代码执行到include()函数是才将文件包含进来,发生错误时只给出一个警告,继续向下执行 |
本地包含
新建一个test.txt
文件,仅仅调用phpinfo
来测试:
<?php phpinfo(); ?>
然后我们在相同目录下放置一个fileinclude.php
,如下:
第一行参数是为了获得file的内容,接下来如果file不为空的话,输出其内容,并将其作为文件名称包含。
我们将其部署在localhost
下,之后访问http://localhost/fileinclude.php?file=test.txt,会看到phpinfo
的输出。
这里之所以用txt
是想说明这个漏洞是可以无视拓展名的,与文件上传漏洞不一样,文件上传漏洞如果我们上传的文件不是".php"
就执行不了(也有一些绕过手段),但是文件包含的拓展名是任意的,这里我们上传的是txt
文件,jpg
文件也是可以的
要注意的是,如果我们被包含的文件中没有PHP
标签,就会被当成HTML内容显示出来
技巧
00截断
有些程序会给被包含内容加一些后缀,比如如果fileinclude.php
是这样
程序会在$file后面添加.php
,如果我们传入的是file=test的话,$file=test.php
这样是没有问题的,但是如果我们传入的是test.txt
,那么$file=test.txt.php
,从而会造成文件包含失败
如果 PHP
版本小于 5.3,并且magic_quotes_gpc
已取消,我们就可以使用%00
来截断。我们传入file=test.txt%00
,就可以实现包含。
PHP
伪协议
php://
协议
-
条件
allow_url_fopen:off/on
allow_url_include
:仅php://input,php://stdin,php://memory,php://temp
需要on
-
作用
php://
访问各个输入/输出流(I/O streams),在CTF
中经常使用的是php://filter
和php://input
,php://filter
用于读取源码,php://input
用于执行php
代码。
-
php://filter
参数详解
该协议的参数会在该协议路径上进行传递,多个参数都可以在一个路径上传递。具体参考如下:
php://filter 参数 | 描述 |
---|---|
resource=<要过滤的数据流> | 必须项。它指定了你要筛选过滤的数据流。 |
read=<读链的过滤器> | 可选项。可以设定一个或多个过滤器名称,以管道符(**)分隔 |
write=<写链的过滤器> | 可选项。可以设定一个或多个过滤器名称,以管道符()分隔 |
<; 两个链的过滤器> | 任何没有以 read= 或 write= 作前缀的筛选器列表会视情况应用于读或写链。 |
转换过滤器 | 作用 |
---|---|
convert.base64-encode & convert.base64-decode |
等同于base64_encode() 和base64_decode() ,base64 编码解码 |
convert.quoted-printable-encode & convert.quoted-printable-decode |
quoted-printable 字符串与 8-bit 字符串编码解码 |
-
实例
-
php://filter/read=convert.base64-encode/resource=[文件名]
读取文件源码(针对php
文件需要进行base64
编码)
比如php://filter/read=convert.base64-encode/resource=text.txt
,我们这里加了一个过滤器让它显示为base64
编码形式。如果我们要获取的文件里面包含不可打印的字符,或者我们想要获取代码的内容,那我们就可以用这种方式获取,之后解码即可。
-
php://input+[POST DATA]
可以读取原始的HTTP正文内容。如果我们将file设置为php://input
,并且在HTTP正文中传入代码,例如
<?php phpinfo(); ?>
即可执行代码。若有写入权限,可以写入一句话木马
<?php fputs(fopen('shell.php','w'),'<?php @eval($_GET[cmd]); ?>'); ?>
碰到file_get_contents()
函数的话,还可以用来读取POST
数据
<?php
echo file_get_contents($_GET['test']);
?>
data:test/plain
协议
这个和php
伪协议的input
类似,也可以执行任意代码,但利用条件和用法不同
条件:
allow_url_fopen: On
allow_url_include: On
-
data:test/plain,<?php 执行内容 ?>
-
data:test/plain;base64,编码后的php代码