zoukankan      html  css  js  c++  java
  • phpMyAdmin本地文件包含漏洞

    4 phpMyAdmin本地文件包含漏洞

    4.1 摘要

    4.1.1 漏洞简介

    • phpMyAdmin是一个web端通用MySQL管理工具,上述版本在/libraries/gis/pma_gis_factory.php文件里的存在任意文件包含漏洞,攻击者利用此漏洞可以获取数据库中敏感信息,存在GETSHELL风险。

    4.1.2 漏洞环境

    • Windows XP
    • php 5.2.17
    • phpMyAdmin 4.0.1--4.2.12

    4.2 漏洞复现

    4.2.1 构造木马

    • 在网站根目录放置phpinfo.txt(包含phpinfo())包含一句话木马等操作,上传后达到GETSEHLL的目的。该文件内PHP代码可以打印出PHP详细的版本等信息,用来测试任意文件包含。

    4.2.2 构造URL直接访问

    • 打开http://localhost/页面来访问本地搭建的存在漏洞的phpMyAdmin,创建一个普通用户root,密码root,没有任何权限,登录后只能看到test、mysql、information_schema表:

    • 居然一片空白,没有出现我想要的phpinfo!?
    • 这又涉及到phpmyadmin的一个防御CSRF机制了,来到libraries/common.inc.php ,代码如下:
    $token_mismatch = true;
    if (PMA_isValid($_REQUEST['token'])) {
        $token_mismatch = ($_SESSION[' PMA_token '] != $_REQUEST['token']);
    }
    //检查$_SESSION[‘ PMA_token ‘] 是否等于 $_REQUEST[‘token’]
    if ($token_mismatch) {//如果不等于
        /**
         *  不安全源允许的参数列表
         */
        $allow_list = array(
            /* 直接访问所需,参见常见问题1.34
             * 此外,服务器需要cookie登录屏幕(多服务器)
             */
            'server', 'db', 'table', 'target', 'lang',
            /* Session ID */
            'phpMyAdmin',
            /* Cookie preferences */
            'pma_lang', 'pma_collation_connection',
            /* Possible login form */
            'pma_servername', 'pma_username', 'pma_password',
            /* Needed to send the correct reply */
            'ajax_request',
            /* Permit to log out even if there is a token mismatch */
            'old_usr'
        );
        /**
         * 允许在 test/theme.php中更改主题
         */
        if (defined('PMA_TEST_THEME')) {
            $allow_list[] = 'set_theme';
        }
        /**
         * Require cleanup functions
         */
        include './libraries/cleanup.lib.php';
        /**
         * Do actual cleanup
         */
        PMA_remove_request_vars($allow_list);//进入PMA_remove_request_vars函数
    }
    
    • 上面代码检查了$_SESSION[‘ PMA_token ‘] 是否等于 $_REQUEST[‘token’],如果不等于,最后会进入PMA_remove_request_vars函数,代码如下:
    function PMA_remove_request_vars(&$whitelist)
    {
        /* 
         * 不要只检查$_REQUEST因为它可能已被覆盖
         * 并使用类型转换,因为变量可能已经成为字符串
         */
        $keys = array_keys(
            array_merge((array)$_REQUEST, (array)$_GET, (array)$_POST, (array)$_COOKIE)
        );
        foreach ($keys as $key) {
            if (! in_array($key, $whitelist)) {
                unset($_REQUEST[$key], $_GET[$key], $_POST[$key], $GLOBALS[$key]);
            } else {
                // allowed stuff could be compromised so escape it
                // we require it to be a string
                if (isset($_REQUEST[$key]) && ! is_string($_REQUEST[$key])) {
                    unset($_REQUEST[$key]);
                }
                if (isset($_POST[$key]) && ! is_string($_POST[$key])) {
                    unset($_POST[$key]);
                }
                if (isset($_COOKIE[$key]) && ! is_string($_COOKIE[$key])) {
                    unset($_COOKIE[$key]);
                }
                if (isset($_GET[$key]) && ! is_string($_GET[$key])) {
                    unset($_GET[$key]);
                }
            }
        }
    }
    
    • 所有的$_REQUEST $_POST $_COOKIE $_GET都清空了,那么后面的操作肯定不能正常运转了。所以,必须带上token访问。

    4.2.3 获取token

    • 使用phpmyadmin时,注意到一般在访问pma的时候都会在url里看到token=xxx这个参数,点击hackbar工具的load url即可将url地址加载到hackbar的栏目中,把该token复制下来,准备下一步操作利用任意文件包含。
    • 在hackbar栏目中输入http://localhost/gis_data_editor.php?token=(上面操作复制的内容粘贴进入这里)&gis_data[gis_type]=/../../../phpinfo.txt%00 输入完毕后点击Execute提交即可,即可包含执行phpinfo.txt中的php代码, 如下图所示:
      ![](http://images2015.cnblogs.com/blog/885499/201706/885499-
      20170617182151243-741741458.png)
    • 打印出PHP详细的版本等信息,漏洞利用成功。

    4.3 漏洞分析

    • 分析官方发布的漏洞补丁,libraries/gis/pma_gis_factory.php代码如下:
    public static function factory($type)    {
            include_once './libraries/gis/pma_gis_geometry.php';
                   $type_lower = strtolower($type);
                   if (! file_exists('./libraries/gis/pma_gis_' . $type_lower . '.php')) {
                         if(! PMA_isValid($type_lower, PMA_Util: :getGISDatatypes())
                            || ! file_exits(' ./libraries/gis/pma_gis' . $type_lower . '.php)//file_exists判断文件是否存在
                          ){
                               return false;
                           }
                   if (include_once './libraries/gis/pma_gis_' . $type_lower . '.php') {//include_once包含这个文件
    
    • 对比下有漏洞的/libraries/gis/pma_gis_factory.php代码:
    public static function factory($type)    {
            include_once './libraries/gis/pma_gis_geometry.php';
                   $type_lower = strtolower($type);
                   if (! file_exists('./libraries/gis/pma_gis_' . $type_lower . '.php')) {
                       return false;
                   }
                   if (include_once './libraries/gis/pma_gis_' . $type_lower . '.php') {
    
    • 可以看到多了一段 PMA_isValid($type_lower, PMA_Util::getGISDatatypes()) $type_lower参数的判断,大概可以猜测漏洞和这个参数有关系。
    • 这里的代码逻辑是:用file_exists判断文件是否存在,如果存在,就用include_once包含这个文件。如果$type_lower这个变量是可控的,那么就造成文件包含漏洞。追溯$type_lower的来源,可以发现是factory函数的参数,所以找调用factory函数的地方gis_data_editor.php
    1     // Get data if any posted
    2     $gis_data = array();
    3     if (PMA_isValid($_REQUEST['gis_data'], 'array')) {
    4         $gis_data = $_REQUEST['gis_data'];//获取到gis_data参数的值
    5     }
    6     $gis_types = array(
    7         'POINT',
    8         'MULTIPOINT',
    9         'LINESTRING',
    10       'MULTILINESTRING',
    11       'POLYGON',
    12       'MULTIPOLYGON',
    13       'GEOMETRYCOLLECTION'
    14   );
    15   // 从初始调用中提取类型并确保它是有效的。
    16   // 判断$gis_data[‘gis_type’]是否已经存在
    17   if (! isset($gis_data['gis_type'])) {
    18       if (isset($_REQUEST['type']) && $_REQUEST['type'] != '') {
    19           $gis_data['gis_type'] = strtoupper($_REQUEST['type']);
    20       }
    21       if (isset($_REQUEST['value']) && trim($_REQUEST['value']) != '') {
    22           $start = (substr($_REQUEST['value'], 0, 1) == "'") ? 1 : 0;
    23           $gis_data['gis_type'] = substr(
    24               $_REQUEST['value'], $start, strpos($_REQUEST['value'], "(") - $start
    25           );
    26       }
    27       if ((! isset($gis_data['gis_type']))
    28           || (! in_array($gis_data['gis_type'], $gis_types))
    29       ) {
    30           $gis_data['gis_type'] = $gis_types[0];
    31       }
    32   }
    33   $geom_type = $gis_data['gis_type'];//赋值
    34   // 通过传递的值生成参数。
    35   $gis_obj = PMA_GIS_Factory::factory($geom_type);//传参
    
    • 首先调用了factory函数,并且参数等于$gis_data['gis_type'],第4行:$gis_data = $_REQUEST['gis_data'];获取到gis_data,判断$gis_data[‘gis_type’]是否已经存在,如果存在则跳过那 一大串if子句。最后就将$gis_data[‘gis_type’]赋值给$geom_type,并传入 PMA_GIS_Factory::factory函数。
    • 实际利用方法其实就是获取$_REQUEST[‘gis_data’][‘gis_type’]并拼接到include_once中,造成任意文件包含。

    4.4 修复建议

  • 相关阅读:
    智能指针shared_ptr新特性shared_from_this及weak_ptr
    reactor模型框架图和流程图 libevent
    memset struct含有string的崩溃
    对于socket发送数据时是否要加锁及write read的阻塞非阻塞
    记录智能指针使用shared_ptr使用错误
    本地缓存和redis
    关于数据结构跳表的一些介绍
    linux 下source、sh、bash、./执行脚本的区别
    socket端口复用问题一二
    MD5算法
  • 原文地址:https://www.cnblogs.com/yx20145312/p/7041065.html
Copyright © 2011-2022 走看看