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

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

缺陷编号:wooyun-2015-0103092

漏洞标题:KingCms最新版目录遍历及任意文件读取漏洞(无需截断)

相关厂商:KingCms

漏洞作者: 路人甲

提交时间:2015-03-24 14:40

修复时间:2015-05-08 14:42

公开时间:2015-05-08 14:42

漏洞类型:敏感信息泄露

危害等级:高

自评Rank:10

漏洞状态:未联系到厂商或者厂商积极忽略

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

Tags标签:

4人收藏 收藏
分享漏洞:


漏洞详情

披露状态:

2015-03-24: 积极联系厂商并且等待厂商认领中,细节不对外公开
2015-05-08: 厂商已经主动忽略漏洞,细节向公众公开

简要描述:

KingCms最新版目录遍历及任意文件读取漏洞(无需截断)

详细说明:

朋友的公司想购买kingcms的授权,让我帮忙看下。发现kingcms很长一段时间没更新了,憋了一段时间放出了最新版的k9(2014-12-13更新),官网下下来学习一下。
在wooyun上看到了几个漏洞,如: WooYun: kingcms最新版sql注入漏洞
问题出在这里:/api/conn.php
先首需要说明的是,这里的目录遍历与文件读取并不是因为服务器配置不当等引起的,而是该cms的某些函数没过过滤+使用不当引起的。
0x00:先来看看如何目录遍历的。

无关代码
$get=$_GET;
if(empty($get['jsoncallback'])) exit('非法提交!');
$jsoncallback=$_GET['jsoncallback'];
if(empty($_GET['USERID'])) exit($jsoncallback.'('.json_encode(array('error'=>"用户ID不能为空,请求失败!")).')');
$str=new str;
$db=new db;
$file=new file;
if(empty($get['SIGN'])) exit($jsoncallback.'('.json_encode(array('error'=>'丢失密文,禁止解析!')).')');
//验证权限
$sign=$get['SIGN'];
unset($get['SIGN'],$get['_'],$get['jsoncallback']);
$arr=array();
foreach($get as $key => $val){
$arr[$key]=$key.'='.urlencode($val);
}
ksort($arr);
$param=implode('&',$arr);
$sign1=md5($param.kc_config('system.salt'));
if($sign!=$sign1) exit($jsoncallback.'('.json_encode(array('error'=>'审核失败,数据不一致!')).')');
$arr=array();
$get=array_map('base64_decode',$get);
foreach($get as $key=>$val){
if(substr($key,0,8)=='data_one'){
$arr[$key]=$db->get_one($val);
}elseif(substr($key,0,4)=='data'){
$arr[$key]=$db->get($val);
}elseif(substr($key,0,5)=='count'){
$res=$db->get($val);
$arr[$key]=empty($res[0]['c']) ? 0 : $res[0]['c'];
}elseif(substr($key,0,5)=='newid'){
list($table,$id)=explode('|',$val,2);
$arr[$key]=$db->newid($table,'',$id);
}elseif(substr($key,0,6)=='getdir'){
list($path,$filetype)=explode('|',$val,2);
if(empty($filetype)) $filetype='*';
$arr[$key]=$file->getDir($path,$filetype);
}elseif(substr($key,0,7)=='getfile'){
$arr[$key]=$file->get($val);
}elseif(substr($key,0,6)=='config'){
$arr[$key]=kc_config($val);
}elseif(substr($key,0,6)=='isfile'){
$arr[$key]=is_file(ROOT.$val)?1:0;
}
}
//判断url值,如果有这个值就有分页,count,rn和pid不能为空
if(!empty($get['url'])){
$url=$get['url'];
$rows=$arr['count'];
$pid=$get['pid'];
$rn=$get['rn'];
$arr['pagelist']=$str->pagelist($url, $rows, $pid, $rn,'<em>[count]</em>[standard][next]');
}


先来看看怎么绕过上面代码中的权限验证,也即要使得$sign1与$sign相等,其中$sing是用户输入的,$sign1的计算方法如下:

$arr=array();
foreach($get as $key => $val){
$arr[$key]=$key.'='.urlencode($val);
}
ksort($arr);
$param=implode('&',$arr);
$sign1=md5($param.kc_config('system.salt'));


计算过程中所用到的参数只有kc_config('system.salt')不是用户输入的,但是在kingcms中,这个参数全都是空的,也就是说$sing1完全由用户的输入参数按上述算法计算得来,因此,这里可以轻松绕过。然后执行到这句$arr[$key]=$file->getDir($path,$filetype);
跟进

function getDir($path='',$type='*',$ignore=array('.','..','.svn')){
if (!is_dir(ROOT.$path)) {
return array();
}
$dirs = $files = array();
$handle=opendir(ROOT.$path);
if($handle){
while (false !== ($file=@readdir($handle))){
$file=$this->encode($file);
if(!in_array($file,$ignore)){
if(is_dir(ROOT.$path.$file)){//如果是dir
if($type=='*'||$type=='dir')
$dirs[$path.$file]=$file;
}else{//文件
if($type=='*'||$type=='file'||preg_match("/^.+\.({$type})$/i",$file))
$files[$path.$file]=$file;
}
}
}
closedir($handle);
//sort($files);
//sort($dirs);
$union=array_merge($dirs,$files);
return $union;
}else{
kc_tip('找不到指定的目录<br/>'.$path);
}
}


直接把目录列出来了。路径是从根文件开始的,这里输入路径时,要base64 encode一下。成功列目录,如图

列目录成功副本.jpg


0x01:再来看看如何实现任意文件读取的。
代码与上面第一段相同,这里就不贴出来了。看到这句$arr[$key]=$file->get($val);
跟进

public function get($filename){
$s='';
$filename=$this->encode($filename,1);
if(empty($GLOBALS['file_get_contents_array']))
$GLOBALS['file_get_contents_array']=array();
if(is_file(ROOT.$filename)){//如果存在则读取
$s='';
$fh = fopen(ROOT.$filename,"r");
while (!feof($fh)) {
$s.=fgets($fh);
}
fclose($fh);
}
return $s;
}


直接把文件读出并返回了,路径是从根文件开始的,这里输入文件名及路径时,要base64 encode一下。文件成功读取,如图

读取过程副本.jpg

漏洞证明:

见 详细说明

修复方案:

验证

版权声明:转载请注明来源 路人甲@乌云


漏洞回应

厂商回应:

未能联系到厂商或者厂商积极拒绝