• CVE-2018-12613 的一些思考


    复现 CVE-2018-12613 的一些思考,关于文件包含路径的问题

    漏洞

    /index.php 第 55 行

    $target_blacklist = array (
        'import.php', 'export.php'
    );
    
    if (! empty($_REQUEST['target'])
        && is_string($_REQUEST['target'])
        && ! preg_match('/^index/', $_REQUEST['target'])
        && ! in_array($_REQUEST['target'], $target_blacklist)
        && Core::checkPageValidity($_REQUEST['target'])
    ) {
        include $_REQUEST['target'];
        exit;
    }
    

    传入参数 target 需要满足

    1. 不以 index.php 开头
    2. 不在 target_blacklist 中
    3. 通过 checkPageValidity() 函数检验

    checkPageValidity() 函数

    public static function checkPageValidity(&$page, array $whitelist = [])
        {
            if (empty($whitelist)) {
                $whitelist = self::$goto_whitelist;
            }
            if (! isset($page) || !is_string($page)) {
                return false;
            }
    
            if (in_array($page, $whitelist)) {
                return true;
            }
    
            $_page = mb_substr(
                $page,
                0,
                mb_strpos($page . '?', '?')
            );
            if (in_array($_page, $whitelist)) {
                return true;
            }
    
            $_page = urldecode($page);
            $_page = mb_substr(
                $_page,
                0,
                mb_strpos($_page . '?', '?')
            );
            if (in_array($_page, $whitelist)) {
                return true;
            }
    
            return false;
        }
    

    第一个返回 True 的地方,直接将 page 与 whitelist 比较,传入的必须是白名单里的文件名,无法绕过

     if (in_array($page, $whitelist)) {
                return true;
     }
    

    第二个返回 True 的地方,mb_strpos($x, $y) 函数查找 $y 在 $x 中首次出现的位置。mb_substr($str, $start, $length) 函数从 $str 中,截取从 $start 位置开始,长度为 $length 的字符串。

    但是在这里如果直接构造 payload : ?target=db_sql.php?/../../../cookie.txt 并不能跨路径包含,? 后面的字符串会被当做传入 db_sql.php 的参数,这就要利用后面的 urldecode 了

     $_page = mb_substr(
                $page,
                0,
                mb_strpos($page . '?', '?')
            );
     if (in_array($_page, $whitelist)) {
                return true;
     }
    

    第三个返回 True 的地方,可以利用双重编码绕过,将 ? 经过两次编码 %253f 就可以绕过白名单验证。%253f 传入时,首先会被自动解码一次,变成 %3f,然后urldecode() 再解码一次,就变成了 ?

    此时的 payload : ?target=db_sql.php%253f/../../../cookie.txt

      $_page = urldecode($page);
      $_page = mb_substr(
               $_page,
                0,
                mb_strpos($_page . '?', '?')
            );
      if (in_array($_page, $whitelist)) {
                return true;
            }
    
    

    问题

    1. include 'db_sql.php%253f/../../../cookie.txt' 为什么只会包含 cookie.txt 而不会包含 db_sql.php

    2. 传入 db_sql.php%253f/../../../cookie.txt 为什么会在 in_array($_page, $whitelist) 处返回 True

    3. 如图,z.php 中 include 两个 ../ 可以包含,y.php 中一个 include 也可以包含

    4. 在 php 的 include 中,include 'hint.php?/../cookie.txt'; 会报错,include 'hint.php%3f/../cookie.txt'; 不会报错,且可以成功包含

    一些解释

    在 include 中,举个例子,假设 x.php 代码包含 include '1source.phps/../cookie.txt'; ,假设 1source.phps 不存在,那么这个文件包含等同于 : 在 1source.phps 文件夹目录下的上一级中的 cookie.txt ,也就是和 x.php 在同一目录下的 cookie.txt ,如果 1source.phps 存在,并且它是一个文件,那么肯定会报错,如果它是一个文件夹,也会成功包含 cookie.txt 。如果变为 include '1source.phps/./cookie.txt'; ,道理和上面相同

    重新思考

    • 代码如下 :

       <?php
          highlight_file(__FILE__);
          class emmm
          {
              public static function checkFile(&$page)
              {
                  $whitelist = ["source"=>"source.php","hint"=>"hint.php"];
                  if (! isset($page) || !is_string($page)) {
                      echo "you can't see it";
                      return false;
                  }
                  if (in_array($page, $whitelist)) {
                      return true;
                  }
                  $_page = mb_substr(
                      $page,
                      0,
                      mb_strpos($page . '?', '?')
                  );
                  if (in_array($_page, $whitelist)) {
                      return true;
                  }
                  $_page = urldecode($page);
                  $_page = mb_substr(
                      $_page,
                      0,
                      mb_strpos($_page . '?', '?')
                  );
      			
      			
                  if (in_array($_page, $whitelist)) {
                      return true;
                  }
                  echo "you can't see it";
                  return false;
              }
          }
      
          if (! empty($_REQUEST['file'])
              && is_string($_REQUEST['file'])
              && emmm::checkFile($_REQUEST['file'])
          ) {
              include $_REQUEST['file'];
              exit;
          } else {
              echo "<br><img src="https://i.loli.net/2018/11/01/5bdb0d93dc794.jpg" />";
          }  
      ?>
      
      • 传入 file=hint.php ,在第一个 in_array 处会返回 true,然后直接包含 hint.php
      • 传入 file=hint.php?/../cookie.txt ,在第二个 in_array 处会返回 true,第二个 in_array 中的 _pagehint.php ,然后包含 hint.php?/../cookie.txt ,但是这里的 ? 起到传递参数的作用而不是破坏路径
      • 传入 file=hint.php%253f/../cookie.txt ,在第三个 in_array 处会返回 true ,第三个 in_array 中的 _pagehint.php ,然后包含 hint.php%3f/../cookie.txt ,这里的 %3f? ,破坏了路径,前面部分的路径不存在,可以包含后面的文件
  • 相关阅读:
    python解析网页中js动态添加的内容
    pycharm索引index时间很长的原因
    github/gitlab ssh-keys全局唯一
    使用gc、objgraph干掉python内存泄露与循环引用!
    ImportError: No module named _curses;Color support is disabled, python-curses is not installed.解决办法
    jinjia2模板学习
    flask可以通过缓存模板或者页面达到性能提升
    python的websocket实现Tornado
    flask的debug模式下,网页输入pin码进行调试
    Git 环境安装
  • 原文地址:https://www.cnblogs.com/peri0d/p/11508866.html
Copyright © 2020-2023  润新知