phpMyAdmin本地文件包含漏洞

文件libraries/gis/pma_gis_factory.php存在文件包含漏洞

函数factory()中对参数$type变小写,然后file_exists存在则调用include_once包含此文件

Github中pma_gis_factory.php漏洞修复代码,添加了PMA_isValid()判断

需要定位参数$type来源,查看函数factory()的调用,phpmyadmin/gis_data_editor.php

// Get data if any posted
$gis_data = array();
if (PMA_isValid($_REQUEST['gis_data'], 'array')) {
    $gis_data = $_REQUEST['gis_data'];
}

$gis_types = array(
    'POINT',
    'MULTIPOINT',
    'LINESTRING',
    'MULTILINESTRING',
    'POLYGON',
    'MULTIPOLYGON',
    'GEOMETRYCOLLECTION'
);

// Extract type from the initial call and make sure that it's a valid one.
// Extract from field's values if availbale, if not use the column type passed.
if (! isset($gis_data['gis_type'])) {
    if (isset($_REQUEST['type']) && $_REQUEST['type'] != '') {
        $gis_data['gis_type'] = strtoupper($_REQUEST['type']);
    }
    if (isset($_REQUEST['value']) && trim($_REQUEST['value']) != '') {
        $start = (substr($_REQUEST['value'], 0, 1) == "'") ? 1 : 0;
        $gis_data['gis_type'] = substr(
            $_REQUEST['value'], $start, strpos($_REQUEST['value'], "(") - $start
        );
    }
    if ((! isset($gis_data['gis_type']))
        || (! in_array($gis_data['gis_type'], $gis_types))
    ) {
        $gis_data['gis_type'] = $gis_types[0];
    }
}
$geom_type = $gis_data['gis_type'];

// Generate parameters from value passed.
$gis_obj = PMA_GIS_Factory::factory($geom_type);

从参数传递过程来看,

$type = $gis_data[‘gis_type’]

  $gis_data = $_REQUEST[‘gis_data’]

所以需要构造

$type = $_REQUEST[‘gis_data’][‘gis_type’]并拼接到include_once()中

构造页面并进行访问,但是并没有显示phpinfo()

Phpmyadmin本身的防CSRF机制,查看libraries/common.inc.php文件,关于token的处理

$token_mismatch = true;
if (PMA_isValid($_REQUEST['token'])) {
    $token_mismatch = ($_SESSION[' PMA_token '] != $_REQUEST['token']);
}

if ($token_mismatch) {
    /**
     *  List of parameters which are allowed from unsafe source
     */
    $allow_list = array(
        /* needed for direct access, see FAQ 1.34
         * also, server needed for cookie login screen (multi-server)
         */
        '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'
    );
    /**
     * Allow changing themes in 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);

}

会对 $_SESSION[‘ PMA_token ‘] 以及 $_REQUEST[‘token’]进行比较,如果不相等,则调用libraries/cleanup.lib.php中PMA_remove_request_vars()函数

function PMA_remove_request_vars(&$whitelist)
{
    // do not check only $_REQUEST because it could have been overwritten
    // and use type casting because the variables could have become
    // strings
    $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值置零,所以会影响后续的访问操作

在phpmyadmin测试中,token的值可以在url中看到

在网站根目录下创建phpinfo.txt文件,内容为phpinfo()

访问phpmyadmin后台地址,输入用户名/密码 = root/root

记录url和token值

访问存在漏洞的页面gis_data_editor.php,构造参数gist_data[gis_type] = /../../../phpinfo.txt,最后跟%00截断,运行可以看到phpinfo()访问成功。

参考资料

https://github.com/phpmyadmin/phpmyadmin/commit/2e3f0b9457b3c8f78beb864120bd9d55617a11b5#diff-b0ac1fef45bedd74074f3b4fb14abdd7
https://www.leavesongs.com/PENETRATION/phpmyadmin-local-file-include-vul.html

您可能还喜欢...