0x01前言
再杭州西湖线下打AWD的时候web1就是这个漏洞,在这里复现一下,刚好巩固一下前几天学习的反序列化漏洞。
0x02漏洞分析
这是一个由unserialize()导致的一个反序列化漏洞,全局搜索unserialize(),在install.php的232行发现
<?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当中的__tyoecho_config的值,然后base解码在进行反序列化,get方法如下:
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;
}
通过给 v a l u e 赋 值 我 们 可 以 看 出 来 , 传 值 的 方 式 可 以 是 value赋值我们可以看出来,传值的方式可以是 value赋值我们可以看出来,传值的方式可以是_COOKIE也可以是$_POST。找到了输入点,回头找一下,看看运行到这一步需要那些条件
//判断是否已经安装
if (!isset($_GET['finish']) && file_exists(__TYPECHO_ROOT_DIR__ . '/config.inc.php') && empty($_SESSION['typecho'])) {
exit;
}
// 挡掉可能的跨站请求
if (!empty($_GET) || !empty($_POST)) {
if (empty($_SERVER['HTTP_REFERER'])) {
exit;
}
$parts = parse_url($_SERVER['HTTP_REFERER']);
if (!empty($parts['port'])) {
$parts['host'] = "{$parts['host']}:{$parts['port']}";
}
if (empty($parts['host']) || $_SERVER['HTTP_HOST'] != $parts['host']) {
exit;
}
}
需要finish不为空,同时需要满足REFERER检测,必须为站内的地址。
根据上面的代码继续运行会调用$config来实例化Typecho_Db这个类,跟进去看一下在Db.php中。在Typecho_Db类中发现了一个构造函数:
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();
}
发现了 a d p t e r N a m e 和 字 符 串 进 行 了 拼 接 , 而 adpterName和字符串进行了拼接,而 adpterName和字符串进行了拼接,而adapterName是我们传入的$config[‘adapter’]里面adapter的值,如果adpter是一个类,就形成了类和字符串的拼接,这样就可以触发这个类__toString()方法
$adapterName = 'Typecho_Db_Adapter_' . $adapterName;
全局搜索__toString()方法,找到了三处,只在Feed.php中可进行利用
foreach ($this->_items as $item) {
$content .= '<item>' . self::EOL;
$content .= '<title>' . htmlspecialchars($item['title']) . '</title>' . self::EOL;
$content .= '<link>' . $item['link'] . '</link>' . self::EOL;
$content .= '<guid>' . $item['link'] . '</guid>' . self::EOL;
$content .= '<pubDate>' . $this->dateFormat($item['date']) . '</pubDate>' . self::EOL;
$content .= '<dc:creator>' . htmlspecialchars($item['author']->screenName) . '</dc:creator>' . self::EOL;
i t e