目录
1.什么是文件包含漏洞
2.产生原因
3.文件包含的类型
3.1本地文件包含
3.2远程文件包含
4.攻击利用手法
4.1 file:协议
4.2 php://协议
4.3 zip://,bzip2://,zlib://协议
4.4 data://协议
4.5 PHP伪协议总结
5.如何防御?
6.常见系统的默认路径
7.文件包含漏洞的奇技淫巧
LFI+日志文件getshell
配合文件上传漏洞
配合路径遍历漏洞
配合session文件
配合SSH日志
配合运行环境
想要了解文件包含漏洞,我们首先需要了解一下什么是文件包含?
后端编程人员一般会把重复使用的函数写到单个文件中,需要使用时再直接调用此文件即可,该过程就被称为文件包含。
文件包含(File Inclusion)是一些对文件操作的函数未经过有效过滤,运行了恶意传入的非预期的文件路径,导致敏感信息泄露或代码执行。如果文件中存在恶意代码,无论什么样的后缀类型,文件内的恶意代码都会以当前服务器的脚本语言所执行,这就导致了文件包含漏洞的产生。
随着网站的业务的需求,程序开发人员一般希望代码更加灵活,所以将被包含的文件设置为变量,用来进行动态调用,但是正是这种灵活性通过动态变量的方式引入需要包含的文件时,用户对这个变量可控而且服务端又没有做合理的校检或者校检被绕过就造成了文件包含漏洞。
文件包含的存在使得开发变得更加灵活和方便,但同时也带了安全问题,导致客户端可以远程调用文件,造成文件包含漏洞。
实际上被包含文件可以是任意格式的,可以是图片、文本、源代码等等。只要文件被包含其内容也会被包含,并以当前服务器脚本语言执行。
大多数Web语言都支持文件包含操作,其中PHP语言所提供的文件包含功能太强大、太灵活,也就导致文件包含漏洞经常出现在PHP语言中。这里就以PHP语言为例,需要打开一下配置allow_url_fopen=On(默认为On)
规定是否允许从远程服务器或者网站检索数据。allow_url_include=On(php5.2之后默认为Off)
规定是否允许include/require远程文件。
PHP提供的可以文件包含的四个函数:
include():当使用该函数包含文件时,只有代码执行到include()函数时才将文件包含进来,发生错误时只给出一个警告(E_WARNING),继续向下执行;
include_once()功能与include()相同,区别在于当重复调用同一文件时,程序只调用一次;
require()与include()的区别在于require()执行如果发生错误(E_COMPILE_ERROR),函数会输出错误信息,并终止脚本的运行。
require_once()功能与require()相同,区别在于当重复调用同一文件时,程序只调用一次。
nighcight_file()、show_source()函数对文件进行语法高亮显示,通常能看到源代码。
readfile()、file_get_contents()函数读取一个文件,并写入输出缓冲。
fopen()函数打开一个文件或者url。
文件包含漏洞分为本地文件包含(Loacl File Inclusion,LFI)和远程文件包含(Remote File Inclusion,RFI)两种。攻击者通过文件包含漏洞可以读取系统中的敏感文件,还有可能导致任意代码执行漏洞。
本地文件包含漏洞指的是能打开并包含本地文件的漏洞,大部分情况下遇到的文件包含漏洞都是LFI,简单的测试用例如下所示。
远程文件包含漏洞是指能够包含远程服务器上的文件并执行。由于远程服务器的文件是我们可控的,因此漏洞一旦存在危害性会很大。
该类型需要当目标服务器开启一定配置时才可以使用,当php.ini中的配置选项allow_url_fopen=on、allow_url_include=on
PHP.ini:
file:// 协议在双off的情况下也可以正常使用;
allow_url_fopen :off/on
allow_url_include:off/on
file:// 用于访问本地文件系统,在CTF中通常用来读取本地文件的且不受allow_url_fopen与allow_url_include的影响
file:// [文件的绝对路径和文件名]
http://127.0.0.1/cmd.php?file=file://D:/software/phpStudy/WWW/phpinfo.txt
phpinfo.txt文件内容:
条件:
不需要开启allow_url_fopen,仅php://input、 php://stdin、 php://memory 和 php://temp 需要开启allow_url_include。
php:// 访问各个输入/输出流(I/O streams),在CTF中经常使用的是php://filter和php://input,php://filter用于读取源码,php://input用于执行php代码。
参考自:PHP: php:// - Manual
php://filter 读取源代码并进行base64编码输出,不然会直接当做php代码执行就看不到源代码内容了。
PHP.ini:
php://filter在双off的情况下也可以正常使用;
allow_url_fopen :off/on
allow_url_include:off/on
测试现象:
http://127.0.0.1/cmd.php?file=php://filter/read=convert.base64-encode/resource=./cmd.php
php://input 可以访问请求的原始数据的只读流, 将post请求中的数据作为PHP代码执行。
PHP.ini:
allow_url_fopen :off/on
allow_url_include:on
测试现象:
http://127.0.0.1/cmd.php?file=php://input
[POST DATA]
也可以POST如下内容生成一句话: ’);?>
PHP.ini:
zip://, bzip2://, zlib://协议在双off的情况下也可以正常使用;
allow_url_fopen :off/on
allow_url_include:off/on
zip://, bzip2://, zlib:// 均属于压缩流,可以访问压缩文件中的子文件,更重要的是不需要指定后缀名。
参考自:PHP: zlib:// - Manual
4.3.1 zip://协议
zip://archive.zip#dir/file.txt
zip:// [压缩文件绝对路径]#[压缩文件内的子文件名]
测试现象:
http://127.0.0.1/cmd.php?file=zip://D:/soft/phpStudy/WWW/file.jpg%23phpcode.txt
先将要执行的PHP代码写好文件名为phpcode.txt,将phpcode.txt进行zip压缩,压缩文件名为file.zip,如果可以上传zip文件便直接上传,若不能便将file.zip重命名为file.jpg后在上传,其他几种压缩格式也可以这样操作。
由于#在get请求中会将后面的参数忽略所以使用get请求时候应进行url编码为%23,且此处经过测试相对路径是不可行,所以只能用绝对路径。
4.3.2 bzip2://协议
使用方法:
compress.bzip2://file.bz2
测试现象:
http://127.0.0.1/cmd.php?file=compress.bzip2://D:/soft/phpStudy/WWW/file.jpg
or
http://127.0.0.1/cmd.php?file=compress.bzip2://./file.jpg
4.3.3 zlib://协议
使用方法:
compress.zlib://file.gz
测试现象:
http://127.0.0.1/cmd.php?file=compress.zlib://D:/soft/phpStudy/WWW/file.jpg
or
http://127.0.0.1/cmd.php?file=compress.zlib://./file.jpg
经过测试官方文档上存在一处问题,经过测试PHP版本5.2,5.3,5.5,7.0;data:// 协议是是受限于allow_url_fopen的,官方文档上给出的是NO,所以要使用data://协议需要满足双on条件
PHP.ini:
data://协议必须双在on才能正常使用;
allow_url_fopen :on
allow_url_include:on
参考自:PHP: data:// - Manual, 官方文档上allow_url_fopen应为yes。
测试现象:
http://127.0.0.1/cmd.php?file=data://text/plain,
or
http://127.0.0.1/cmd.php?file=data://text/plain;base64,PD9waHAgcGhwaW5mbygpPz4=
也可以:
http://127.0.0.1/cmd.php?file=data:text/plain,
or
http://127.0.0.1/cmd.php?file=data:text/plain;base64,PD9waHAgcGhwaW5mbygpPz4=
PHP封装协议在CTF蛮常见的,是经常会遇到的出题点,如下便是对本篇涉及的封装协议进行的总结,期待小伙伴的交流和补充。
上述中,php://filter,php://input,zip://这三个用的最多,就像data需要同时为on,条件太苛刻,几乎遇不到 。
1、访问路径限制
PHP中使用open_basedir
配置限制访问在指定的区域,限制被包含的文件只能是某一文件夹内。
2、过滤输入
过滤.
(点)/
(反斜杠)\
(反斜杠)等特殊字符,禁止目录跳转字符如../
。
3、关闭高危配置
PHP文件配置allow_url_include
和allow_url_fopen
最小权限化。
4、白名单 对需要包含的文件设置文件白名单,包含文件的验证。
5、避免参数
尽量不要使用动态包含,可以在需要包含的页面固定写好。
c:\boot.ini //查看系统版本
c:\windows\repair\sam //存储Windows系统初次安装的密码
c:\windows\win.ini
/etc/passwd //账户信息
/etc/shadow //账户密码文件
/var/lib/php/sess_PHPSESSID //存储session文件
日志文件往往会包含我们的请求记录,如果我们知道日志的文件位置,那么我们就可以将恶意的php代码写入到日志中,然后再通过文件包含漏洞就可以执行相关的代码。
很多时候,web服务器会将请求写入到日志文件中,比如说apache。在用户发起请求时,会将请求写入access.log,当发生错误时将错误写入error.log。默认情况下,日志保存路径在 /var/log/apache2/。
一般情况下,文件包含漏洞配合文件上传漏洞使用更佳。利用文件上传漏洞,将木马文件上传到相关目录下,再通过文件包含漏洞执行该木马。
一般情况下,通过路径遍历直接包含所要查看的文件。
php的session文件的保存路径可以在phpinfo的session.save_path看到。
常见存放位置:
session的文件名格式为sess_[phpsessid]。而phpsessid在发送的请求的cookie字段中可以看到。
使用ssh -pssh端口 ''@ip地址
将恶意代码注入日志中,然后可包含该日志文件。
默认情况下为/var/log/auth.log
php以cgi方式运行,这样environ才会保持UA头。environ文件存储位置已知且environ文件可读,/proc/self/environ
。
GET /index.php?file=../../../../proc/self/environ HTTP/1.1 Host: 127.0.0.1 User-Agent: Mozilla/5.0 Connection: close