74cms二次注入以及利用

  • A+
所属分类:漏洞时代
摘要

漏洞文件:/Application/Common/Model/CompanyProfileModel.class.php首先从数据库中根据当前用户的uid查询出companyname内容然后一个判断是否为真,当真的情况下则把数据库中的内容提出来并进行一个赋值(这里就想到了查询出的内容可控话且下面没有安全防护的情况下,那就存在二次注入)

漏洞文件:/Application/Common/Model/CompanyProfileModel.class.php

public function add_company_profile($data,$user)     {         $this->_user = $user;         // 替换原名称         $company = =$this->field('id,companyname')->where(array('uid'=>$user['uid']))->find();         if($company){             $data['companyname'] = $company['companyname'];         }         if(false === $this->create($data))         {             return array('state'=>0,'error'=>$this->getError());         }         else         {             if($company['id'])             {                 if(false === $num = $this->save()){                     return array('state'=>0,'error'=>'数据添加失败!');                 }                 if(!$user['mobile_audit']){                     $setsqlarr['telephone'] = $data['telephone'];                 }                 if(!$user['email_audit']){                     $setsqlarr['email'] = $data['email'];                 }                 $setsqlarr && D('Members')->update_user_info($setsqlarr,$user);             }

首先从数据库中根据当前用户的uid查询出companyname内容然后一个判断是否为真,当真的情况下则把数据库中的内容提出来并进行一个赋值(这里就想到了查询出的内容可控话且下面没有安全防护的情况下,那就存在二次注入)

if($company['id'])             {                 if(false === $num = $this->save()){                     return array('state'=>0,'error'=>'数据添加失败!');                 }                 if(!$user['mobile_audit']){                     $setsqlarr['telephone'] = $data['telephone'];                 }                 if(!$user['email_audit']){                     $setsqlarr['email'] = $data['email'];                 }                 $setsqlarr && D('Members')->update_user_info($setsqlarr,$user);             }

$company['id']为TRUE时进入操作SAVE的数据库执行操作,后面内容太多我就不细说了,就是没有二次转义之类的
关联文件:/Application/Home/Controller/CompanyController.class.php

$setsqlarr['companyname']=I('post.companyname','','trim,badword');              $setsqlarr['registered']=I('post.registered','','trim,badword');              $setsqlarr['currency']=I('post.currency','','trim,badword');              $setsqlarr['address']=I('post.address','','trim,badword');              $setsqlarr['contact']=I('post.contact','','trim,badword');             $setsqlarr['telephone'] = C('visitor.mobile_audit') ? C('visitor.mobile') : I('post.telephone','','trim,badword');             $setsqlarr['email'] = C('visitor.email_audit') ? C('visitor.email') : I('post.email','','trim,badword');             $setsqlarr['website']=I('post.website','','trim,badword');             $setsqlarr['contents']=I('post.contents','','trim,badword');             $setsqlarr['contact_show']=I('post.contact_show',1,'intval');             $setsqlarr['telephone_show']=I('post.telephone_show',1,'intval');             $setsqlarr['landline_tel_show']=I('post.landline_tel_show',1,'intval');             $setsqlarr['email_show']=I('post.email_show',1,'intval');             $setsqlarr['qq']=I('post.qq',0,'intval');             $setsqlarr['map_x']=I('post.map_x',0,'trim,badword');             !$setsqlarr['map_x'] && $setsqlarr['map_x'] = 0;             $setsqlarr['map_y']=I('post.map_y',0,'trim,badword');             !$setsqlarr['map_y'] && $setsqlarr['map_y'] = 0;             $setsqlarr['map_zoom']=I('post.map_zoom',0,'intval');                           //座机             $landline_tel_first=I('post.landline_tel_first',0,'trim,badword');             $landline_tel_next=I('post.landline_tel_next',0,'trim,badword');             $landline_tel_last=I('post.landline_tel_last',0,'trim,badword');             $setsqlarr['landline_tel']=$landline_tel_first.'-'.$landline_tel_next.($landline_tel_last?('-'.$landline_tel_last):'');             $posttag = I('post.tag','','trim,badword');               if($posttag){                 $tagArr = explode(",",$posttag);                 $r_arr = array();                 foreach ($tagArr as $key => $value) {                     $r_arr[] = $value.'|'.$category['QS_jobtag'][$value];                 }                 if(!empty($r_arr)){                     $setsqlarr['tag'] = implode(",",$r_arr);                 }else{                     $setsqlarr['tag'] = '';                 }             }               if($company_profile['contents'])              {                 $setsqlarr['id']=$company_profile['id'];                 C('qscms_audit_edit_com')<>"-1"?$setsqlarr['audit']=C('qscms_audit_edit_com'):$setsqlarr['audit']=$company_profile['audit'];             }             else             {                 $setsqlarr['audit']=0;             }             $setsqlarr['sync'] = I('post.sync',0,'intval');             // 插入数据             // //              $rst = D('CompanyProfile')->add_company_profile($setsqlarr,C('visitor'));

进入add中的setsqlarr数组内容均是由post传递,这里也能看见companame这个参数可控,但后面因为又从数据库查询出来进行赋值导致二次注入,现在要找到第一次写入的位置
关联文件:/Application/Home/Controller/MembersController.class.php

if(IS_POST && IS_AJAX){             $data['reg_type'] = I('post.reg_type',0,'intval');//注册方式(1:手机,2:邮箱,3:微信)             $array = array(1 => 'mobile',2 => 'email');             if(!$reg = $array[$data['reg_type']]) $this->ajaxReturn(0,'正确选择注册方式!');             $data['utype'] = I('post.utype',0,'intval');             if($data['utype'] != 1 && $data['utype'] != 2) $this->ajaxReturn(0,'请正确选择会员类型!');             if($data['reg_type'] == 1){                 $data['mobile'] = I('post.mobile',0,'trim');                 $smsVerify = session('reg_smsVerify');                 if(!$smsVerify) $this->ajaxReturn(0,'验证码错误!');                 if($data['mobile'] != $smsVerify['mobile']) $this->ajaxReturn(0,'手机号不一致!',$smsVerify);//手机号不一致                 if(time()>$smsVerify['time']+600) $this->ajaxReturn(0,'验证码过期!');//验证码过期                 $vcode_sms = I('post.mobile_vcode',0,'intval');                 $mobile_rand=substr(md5($vcode_sms), 8,16);                 if($mobile_rand!=$smsVerify['rand']) $this->ajaxReturn(0,'验证码错误!');//验证码错误!                 $data['password'] = I('post.password','','trim');                 $passwordVerify = I('post.passwordVerify','','trim');             }else{                 if($data['utype'] == 1){                     $data['password'] = I('post.cpassword','','trim');                     $passwordVerify = I('post.cpasswordVerify','','trim');                 }else{                     $data['password'] = I('post.emailpassword','','trim');                     $passwordVerify = I('post.emailpasswordVerify','','trim');                 }                 $data['username'] = I('post.username','','trim,badword');                 $data['email'] = I('post.email','','trim,badword');                 $data['utype']==1 && $data['mobile'] = I('post.telephone','','trim,badword');                 C('qscms_check_reg_email') && $data['status'] = 0;             }             !$data['password'] && $this->ajaxReturn(0,'请输入密码!');             $data['password'] != $passwordVerify && $this->ajaxReturn(0,'两次密码输入不一致!');             if($data['utype']==1){                 $com_setarr['audit'] = 0;                 $com_setarr['email']=$data['email'];                    $com_setarr['companyname']=I('post.companyname','','trim,badword');                 $com_setarr['contact']=I('post.contact','','trim,badword');                 $com_setarr['telephone']=I('post.telephone','','trim,badword');                 $com_setarr['landline_tel']=I('post.landline_tel','','trim,badword');                 $company_mod = D('CompanyProfile');                 if(false === $company_mod->create($com_setarr)) $this->ajaxReturn(0,$company_mod->getError());             }             $passport = $this->_user_server();             if(false === $data = $passport->register($data)){

通过一串的代码发现

$com_setarr['companyname']=I('post.companyname','','trim,badword');

写入进去companyname是可控的,然后跟入最后的insert发现期间并没有二次转义
本地复现:
74cms二次注入以及利用
Companyname填入sql语句
74cms二次注入以及利用
数据库中的内容可以清楚看见并无任何变化
74cms二次注入以及利用
在保存时请求发现直接爆出管理表中的username信息
官网演示:
74cms二次注入以及利用
这里有必要提出的一点是:
74cms二次注入以及利用
数据库中是对该字段进行了长度限制的,也就是sql语句最长只能为60长度。怼了半天没弄出更短的sql语句,于是看了下登录的逻辑。找到密码的加密方式

$admin = M('Admin')->field($field)->where(array('username'=>$username))->find(); if(!$err && !$admin) $err='管理员帐号不存在'; if(!$err && $admin['password'] != md5(md5($password).$admin['pwd_hash'].C('PWDHASH'))) $err='用户名或密码错误!';

看看`C("PWDHASH")`
74cms/Application/Common/Conf/pwdhash.php

<?php return array ( 'PWDHASH'=> '[email protected]' );

再看一下生成pwdhash的逻辑
/74cms/install/Home/Controller/IndexController.class.php

$QS_pwdhash=$this->randstr(16); $content = '<?'."php/n"; $content .= "return array (/n"; $content .= "'PWDHASH'=> '{$QS_pwdhash}'/n"; $content .= ");";   ...   protected function randstr($length=6) {     $hash='';     $chars= '[email protected]#!~?:-=';        $max=strlen($chars)-1;        mt_srand((double)microtime()*1000000);        for($i=0;$i<$length;$i++)   {            $hash.=$chars[mt_rand(0,$max)];        }        return $hash;    }

pwdhash是16位随机生成的。所以就算能得到很多组管理密码的hash,解出密码的概率也微乎其微。

如果是mt_srand(time())的话 可以参考ph牛的文章,复现出pwdhash的值。

发表评论

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: