discuz discuz_admincp.php 讲解,Discuz! 1.5-2.5 命令执行漏洞分析(CVE-2018-14729)

158270

0x00 漏洞简述

漏洞信息

8月27号有人在GitHub上公布了有关Discuz 1.5-2.5版本中后台数据库备份功能存在的命令执行漏洞的细节。

漏洞影响版本

Discuz! 1.5-2.5

0x01 漏洞复现

官方论坛下载相应版本就好。

0x02 漏洞分析

需要注意的是这个漏洞其实是需要登录后台的,并且能有数据库备份权限,所以比较鸡肋。

我这边是用Discuz! 2.5完成漏洞复现的,并用此进行漏洞分析的。

漏洞点在:

source/admincp/admincp_db.php

第296行:

@shell_exec($mysqlbin.'mysqldump --force --quick '.($db->version() > '4.1' ? '--skip-opt --create-options' : '-all').' --add-drop-table'.($_GET['extendins'] == 1 ? ' --extended-insert' : '').''.($db->version() > '4.1' && $_GET['sqlcompat'] == 'MYSQL40' ? ' --compatible=mysql40' : '').' --host="'.$dbhost.($dbport ? (is_numeric($dbport) ? ' --port='.$dbport : ' --socket="'.$dbport.'"') : '').'" --user="'.$dbuser.'" --password="'.$dbpw.'" "'.$dbname.'" '.$tablesstr.' > '.$dumpfile);

在shell_exec()函数中可控点在$tablesstr,向上看到第281行:

$tablesstr = '';

foreach($tables as $table) {

$tablesstr .= '"'.$table.'" ';

}

跟一下$table的获取流程,在上面的第143行:

if($_GET['type'] == 'discuz' || $_GET['type'] == 'discuz_uc')

{

$tables = arraykeys2(fetchtablelist($tablepre), 'Name');

}

elseif($_GET['type'] == 'custom')

{

$tables = array();

if(empty($_GET['setup']))

{

$tables = C::t('common_setting')->fetch('custombackup', true);

}

else

{

C::t('common_setting')->update('custombackup', empty($_GET['customtables'])? '' : $_GET['customtables']);

$tables = & $_GET['customtables'];

}

if( !is_array($tables) || empty($tables))

{

cpmsg('database_export_custom_invalid', '', 'error');

}

}

可以看到:

C::t('common_setting')->update('custombackup', empty($_GET['customtables'])? '' : $_GET['customtables']);

$tables = & $_GET['customtables'];

首先会从$_GET的数组中获取customtables字段的内容,判断内容是否为空,不为空则将从外部获取到的customtables字段内容写入common_setting表的skey=custombackup的svalue字段,写入过程中会将这个字段做序列化存储:

158270

之后再将该值赋给$tables。

至此可以看到漏洞产生的原因是由于shell_exec()中的$tablesstr可控,导致代码注入。

0x03 漏洞利用

漏洞的调用栈如下:

admin.php->source/class/discuz/discuz_admincp.php->source/admincp/admincp_db.php

跟着漏洞的调用栈看一下如何利用。

首先在admin.php中:

if(empty($action) || $frames != null) {

$admincp->show_admincp_main();

} elseif($action == 'logout') {

$admincp->do_admin_logout();

dheader("Location: ./index.php");

} elseif(in_array($action, $admincp_actions_normal) || ($admincp->isfounder && in_array($action, $admincp_actions_founder))) {

if($admincp->allow($action, $operation, $do) || $action == 'index') {

require $admincp->admincpfile($action);

} else {

cpheader();

cpmsg('action_noaccess', '', 'error');

}

} else {

cpheader();

cpmsg('action_noaccess', '', 'error');

}

关键点在构造参数满足require $admincp->admincpfile($action);且$action为db。也就说需要构造参数满足:

$admincp->isfounder && in_array($action, $admincp_actions_founder) # 为真

$admincp->allow($action, $operation, $do) # 为真

$admincp->isfounder是确认当前用户的,返回为True,这里只需要构造$action为db。

跟进require $admincp->admincpfile($action);:

function admincpfile($action) {

return './source/admincp/admincp_'.$action.'.php';

}

这里就包含了source/admincp/admincp_db.php。跟进看一下:

158270

这边需要满足$operation == ‘export’,同时存在exportsubmit字段。

之后,

158270

需要构造file字段,同时$_GET[‘type’] == ‘custom’且$_GET[‘setup’]和$_GET[‘customtables’]非空。向下跟,还需要满足最后一个条件$_GET[‘method’] != ‘multivol’,这样才能调用else中的操作,完成代码注入。

有了上面的这些基础分析,我们抓个符合上方条件的包来看一下。经过测试,

158270

158270

这样可以抓到符合我们条件的请求包。

158270

接下来只需要将customtables的内容更改一下就可以造成命令执行了:

158270

158270

效果为:

158270

0x04 参数获取问题

通过上面的分析可以看到最终可控参数的获取都是利用$_GET来获取的,但是我们在构造时发送的是post数据,那么为什么会照常获取到呢?

在admin.php第18行包含了source/class/class_core.php:跟进看一下:

...

C::creatapp();

class core

{

...

public static function creatapp() {

if(!is_object(self::$_app)) {

self::$_app = discuz_application::instance();

}

return self::$_app;

}

...

}

跟进到source/class/discuz/discuz_application.php中:

public function __construct() {

$this->_init_env();

$this->_init_config();

$this->_init_input();

$this->_init_output();

}

接着跟进到_init_input()中:

...

if($_SERVER['REQUEST_METHOD'] == 'POST' && !empty($_POST)) {

$_GET = array_merge($_GET, $_POST);

}

...

可以看到如果构造了post请求,Discuz的核心类会将$_GET和$_POST这两个list拼接到一起,赋给$_GET数组。

0x05 修复方法

可以利用addslashes()对可控点进行限制,同时利用escapeshellarg()函数来限制$tablesstr执行命令。

0x06 Discuz 3.4的做法

Discuz 3.4非常有趣的一点不是把这个漏洞修了,而是直接在source/admincp/admincp_db.php第307行写了一个错误…:

list(, $mysql_base) = DB::fetch($query, DB::$drivertype == 'mysqli' ? MYSQLI_NUM : MYSQL_NUM);

调用了一个未声明的静态变量,所以该功能直接是挂掉的,没有办法使用,可谓是简单粗暴…

0x07 参考资料

FoolMitAh/CVE-2018-14729

[1]  https://github.com/FoolMitAh/CVE-2018-14729

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是Discuz! X的config_global.php配置文件的注释教程: ```php <?php /** * Discuz! X - 配置文件 * * 版权所有 (C) 2001-2019 Comsenz Inc. * This is NOT a freeware, use is subject to license terms * * $Id: config_global.php 36360 2019-11-18 00:28:51Z nemohou $ */ // ---------------------------- CONFIG DB ----------------------------- // /** * 数据库设置 * * type 数据库类型,可选值为 mysql 或 mysqli * server 数据库服务器 * port 数据库端口 * username 数据库用户名 * password 数据库密码 * dbname 数据库名 * pconnect 是否启用持久连接 * charset 数据库字符集,可选值为 gbk, big5, utf8, latin1, etc. * setnames 是否将字符集强制设为 utf8 * tablepre 表名前缀 * dbdebug 是否启用数据库调试模式 */ $_config['db']['1']['dbtype'] = 'mysql'; $_config['db']['1']['dbhost'] = 'localhost'; $_config['db']['1']['dbport'] = '3306'; $_config['db']['1']['dbuser'] = 'root'; $_config['db']['1']['dbpw'] = 'password'; $_config['db']['1']['dbname'] = 'discuz'; $_config['db']['1']['pconnect'] = '0'; $_config['db']['1']['charset'] = 'utf8'; $_config['db']['1']['setnames'] = '1'; $_config['db']['1']['tablepre'] = 'pre_'; $_config['db']['1']['dbdebug'] = 'false'; // -------------------------- CONFIG MEMORY --------------------------- // /** * 内存变量缓存设置 * * type 缓存类型,可选值为 filecache 或 memcache 或 apc * ttl 缓存失效时间,单位为秒 * prefix 缓存前缀,建议修改,避免同服务器中的程序引起冲突 * servers memcache 缓存服务器地址和端口,可指定多个,格式为数组 */ $_config['memory']['prefix'] = 'discuz_'; $_config['memory']['eaccelerator'] = false; $_config['memory']['apc'] = false; $_config['memory']['xcache'] = false; $_config['memory']['file']['server'] = array(); $_config['memory']['memcache']['server'] = array( array('127.0.0.1', 11211, 1), // 第一个参数为 memcache 服务器的地址,第二个参数为端口,第三个参数为权重,用于负载均衡,默认为1 ); // ----------------------------- CONFIG CACHE --------------------------- // /** * 数据缓存设置 * * type 缓存类型,可选值为 filecache 或 memcache 或 apc * ttl 缓存失效时间,单位为秒 * prefix 缓存前缀,建议修改,避免同服务器中的程序引起冲突 * filecache 设置缓存的目录,仅对 filecache 缓存有效 * servers memcache 缓存服务器地址和端口,可指定多个,格式为数组 * compress 是否启用 memcache 的压缩功能 */ $_config['cache']['type'] = 'filecache'; $_config['cache']['file']['server'] = array( array('localhost', 11211, 1), ); $_config['cache']['memcache']['server'] = array( array('localhost', 11211, 1), ); $_config['cache']['apc'] = false; $_config['cache']['ttl'] = 0; $_config['cache']['prefix'] = 'discuz_'; $_config['cache']['file']['dir'] = './data/cache/'; $_config['cache']['memcache']['compress'] = false; // ----------------------------- CONFIG SMTP --------------------------- // /** * 邮件设置 * * maildefault 默认的邮件发送方式,可选值为 smtp 或 sendmail * smtp 以下 SMTP 设置仅在 maildefault 为 smtp 时有效 * server SMTP 服务器地址 * port SMTP 服务器端口 * auth 是否启用 SMTP 认证,可选值为 true 或 false * username SMTP 服务器用户名 * password SMTP 服务器密码 * sendmail 以下 Sendmail 设置仅在 maildefault 为 sendmail 时有效 * server Sendmail 服务器地址 * sendmail_path Sendmail 程序路径 * * 注意:不同的邮件发送方式对应的设置选项不同,具体请参见官方文档 */ $_config['mail']['maildefault'] = 'smtp'; $_config['mail']['smtp']['server'] = 'smtp.exmail.qq.com'; $_config['mail']['smtp']['port'] = '25'; $_config['mail']['smtp']['auth'] = '1'; $_config['mail']['smtp']['username'] = 'admin@example.com'; $_config['mail']['smtp']['password'] = 'password'; $_config['mail']['sendmail']['server'] = '/usr/sbin/sendmail'; $_config['mail']['sendmail']['sendmail_path'] = ''; // ----------------------------- CONFIG SECURITY --------------------------- // /** * 安全设置 * * authkey 论坛加密密钥,建议修改,长度为 64 个字符 * cookiepre cookie 前缀,建议修改,避免同服务器中的程序引起冲突 * cachelist 缓存前缀列表,建议修改,避免同服务器中的程序引起冲突 * attackevasive 是否启用防抵制攻击功能,可选值为 0、1、2、3 或 4 * 0 表示关闭防抵制攻击功能 * 1 表示启用 cookie 刷新方式防抵制攻击功能 * 2 表示启用限制代理访问功能防抵制攻击功能 * 3 表示启用 cookie 刷新与限制代理访问两种方式的防抵制攻击功能 * 4 表示启用加强版防抵制攻击功能 * 注意:启用加强版防抵制攻击功能后,可能会影响网站的访问速度 * admincp_allow_ip 允许访问后台的 IP 地址列表,多个 IP 之间用英文逗号隔开 * admincp_check_ip 是否启用后台 IP 验证功能,可选值为 0 或 1 * admincp_cpsession 是否启用后台 session 验证功能,可选值为 0 或 1 */ $_config['security']['authkey'] = '1234567890123456789012345678901234567890123456789012345678901234'; $_config['security']['cookiepre'] = 'discuz_'; $_config['security']['cachelist'] = ''; $_config['security']['attackevasive'] = '0'; $_config['security']['admincp_allow_ip'] = ''; $_config['security']['admincp_check_ip'] = '1'; $_config['security']['admincp_cpsession'] = '1'; // ----------------------------- CONFIG SYSTEM --------------------------- // /** * 系统设置 * * debug 是否启用调试模式,可选值为 true 或 false * cookie_domain cookie 作用域 * cookie_path cookie 作用路径 * attachdir 附件上传目录,相对于论坛根目录的路径 * attachurl 附件 URL 地址 * attachimgpost 是否允许在帖子中显示图片附件,可选值为 0 或 1 * attachrefcheck 是否检查附件引用,可选值为 0 或 1 * attachsave 是否在服务器上保存上传的附件,可选值为 0 或 1 * attachimgmaxsize 图片类附件上传大小,单位为字节 * attachimgthumb 是否生成缩略图,可选值为 0 或 1 * attachimgquality 缩略图质量,取值范围为 1-100 * attachimgwatermark 是否添加水印,可选值为 0 或 1 * attachimgwatermarktype 水印类型,可选值为 text、image 或 none * attachimgwatermarktext 水印文字,当水印类型为 text 时有效 * attachimgwatermarktrans 水印透明度,取值范围为 1-100,当水印类型为 text 时有效 * attachimgwatermarkfile 水印图片文件名,当水印类型为 image 时有效 * attachimgwatermarkpos 水印位置,可选值为 1-9,当水印类型为 image 时有效 * refererhotlink 是否开启防盗链功能,可选值为 0 或 1 * hotlink_protect_key 防盗链密钥,如果不设置,则系统自动生成一个密钥 */ $_config['debug'] = false; $_config['cookie']['cookie_domain'] = ''; $_config['cookie']['cookie_path'] = '/'; $_config['attachdir'] = './data/attachment'; $_config['attachurl'] = 'attachment/'; $_config['attachimgpost'] = '1'; $_config['attachrefcheck'] = '1'; $_config['attachsave'] = '1'; $_config['attachimgmaxsize'] = '2048000'; $_config['attachimgthumb'] = '1'; $_config['attachimgquality'] = '80'; $_config['attachimgwatermark'] = '1'; $_config['attachimgwatermarktype'] = 'text'; $_config['attachimgwatermarktext'] = 'Discuz!'; $_config['attachimgwatermarktrans'] = '50'; $_config['attachimgwatermarkfile'] = ''; $_config['attachimgwatermarkpos'] = '9'; $_config['refererhotlink'] = '0'; $_config['hotlink_protect_key'] = ''; // ----------------------------- CONFIG OUTPUT --------------------------- // /** * 输出设置 * * output_gzip 是否启用 Gzip 压缩输出,可选值为 0 或 1 * output_charset 输出页面字符集,可选值为 gb2312、gbk、big5、utf-8 或 iso-8859-1 * output_language 输出页面语言,可选值为 en、zh-cn、zh-tw * output_encoding 输出页面编码格式,可选值为 xml、html、xhtml */ $_config['output']['gzip'] = '0'; $_config['output']['charset'] = 'utf-8'; $_config['output']['language'] = 'zh-cn'; $_config['output']['encoding'] = 'html'; ``` 以上就是Discuz! X的config_global.php配置文件的注释教程,希望能够帮助到你。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值