最新版。两个注入点。顺带一个绕过waf的小技巧。 第一处:/member/model/index.class.php 39行
function index_action() { $this->public_action(); $this->member_satic(); $this->com_cache(); $resume = $this->obj->DB_select_once("resume","`uid`='".$this->uid."'"); $expect=$this->obj->DB_select_once("resume_expect","`id`='".$resume['def_job']."'"); if($_GET['type']=="job") { $where="`job_post` in (".$expect['job_classid'].") and `status`<>'1' and `state`='1' and `sdate`<'".mktime()."' and `r_status`<>'2' and `edate`>'".mktime()."'"; }elseif($_GET['type']=="city"){ $where="`cityid`='".$expect['cityid']."' and `status`<>'1' and `state`='1' and `sdate`<'".mktime()."' and `r_status`<>'2' and `edate`>'".mktime()."'"; }else{ $where="`state`='1' and status<>'1' and `sdate`<'".mktime()."' and `r_status`<>'2' and `edate`>'".mktime()."'"; } $rows=$this->obj->DB_select_all("company_job",$where." order by id desc limit 12","`name`,`id`,`salary`,`edu`,`edate`");
where 语句拼接了$expect['job_classid'].变量。而这个变量来自于$expect=$this->obj->DB_select_once("resume_expect","`id`='".$resume['def_job']."'"); resume_expect 表。 member/model/index.class.php 587行
function saveexpect_action() { if($_POST['submit']) { $eid=(int)$_POST['eid']; unset($_POST['submit']); unset($_POST['eid']); unset($_POST['urlid']); $_POST['name'] = iconv("utf-8", "gbk", $_POST['name']); $where['id']=$eid; $where['uid']=$this->uid; $_POST['lastupdate']=time(); if($eid=="") { $num=$this->obj->DB_select_num("resume_expect","`uid`='".$this->uid."'"); var_dump($num); if($num>=$this->config['user_number']) { echo 1;die; } $_POST['uid']=$this->uid; $nid=$this->obj->insert_into("resume_expect",$_POST); if ($nid) {
可以看到整个$_POST插入到了expect表中。完全是可控的。 测试过程。因为phpyun有两个waf,360就不说了。phpyun自带的依然可以白名单绕过。这里说一下自带的waf。
function gpc2sql($str,$str2) { if(preg_match("/select|insert|update|delete|union|into|load_file|outfile/is", $str)) { exit(safe_pape()); } if(preg_match("/select|insert|update|delete|union|into|load_file|outfile/is", $str2)) { exit(safe_pape()); } $arr=array(" and "=>" an d "," or "=>" Or ","%20"=>" ","select"=>"Select","update"=>"Update","count"=>"Count","chr"=>"Chr","truncate"=>"Truncate","union"=>"Union","delete"=>"Delete","insert"=>"Insert","<"=>"<",">"=>">","\""=>""","'"=>"´","--"=>"- -"); foreach($arr as $key=>$v){ $str = preg_replace('/'.$key.'/isU',$v,$str); } return $str; }
这个函数直接过滤了select|insert|update|delete|union|into|load_file|outfile 关键字,$arr=array(" and "=>" an d "," or "=>" Or ","%20"=>" ","select"=>"Select","update"=>"Update","count"=>"Count","chr"=>"Chr","truncate"=>"Truncate","union"=>"Union","delete"=>"Delete","insert"=>"Insert","<"=>"<",">"=>">","\""=>""","'"=>"´","--"=>"- -"); 这里之前把%20替换为空。之前有人提的利用这里引入or关键字的方式就不可以了。这个过滤还是很变态的。所以这里尝试进行延时注入。 其中过滤sleep的正则是这样的:
sleep\s*?\\([\d\.]+?\\)
看了以前原来已经前辈发过了。 WooYun: 360webscan正则不当可被绕过进行SQL注入 sleep里面进行算数运算就可以了。这里说一下别的方法: 比如sleep((11)),多加一层括号,sleep((select 2)),等等。所以 WooYun: 360webscan正则不当可被绕过进行SQL注入 这里分析的改成benchmark这样也是不安全的。
benchmark\s*?\\(\d+?|sleep\s*?\\([\d\.]+?\\)
最彻底的就是sleep\s*?\(.*\)进行过滤。 最后说一下这里的利用过程。 首先登陆,post数据到数据库。
访问一下url触发。
所以这里实现的绕过也就是前辈说过的
如果我们想让服务器挂掉或者查询当前的数据库环境,很容易,我们可以很轻松的sleep或者获取当前运行的数据库信息了,这些不依赖select字符的事情我们都可以做了。
第二个点存在招聘会的地方,也是二次注入,写数据的时候需要后台审核。 企业预订的时候,抓包。
修改GET /phpyun3.1/index.php?m=ajax&c=zphcom&uid=5&pid=1&jobid=2 HTTP/1.1 jobid为注入payload。 然后查看参会企业触发。只是默认情况下,企业申请需要管理员审核。
相关代码:model/zph.class.php 23行:
function com_action() { $this->job_cache(); $row=$this->obj->DB_select_once("zhaopinhui","`id`='".(int)$_GET['id']."'"); $this->yunset("row",$row); $where="`zid`='".(int)$_GET['id']."' and status='1'"; var_dump($where); $urlarr["c"]=$_GET['c']; $urlarr["id"]=$_GET['id']; $urlarr["page"]="{{page}}"; $pageurl=$this->url("index",$_GET['m'],$urlarr,"1"); $rows=$this->get_page("zhaopinhui_com",$where." order by id desc",$pageurl,"13"); if(is_array($rows)){ foreach($rows as $key=>$v){ $rows[$key]['comname']=$this->obj->get_comname($v['uid']); $rows[$key]['job']=$this->obj->DB_select_all("company_job","id in (".$v['jobid'].") and `status`<>'1' and `r_status`<>'2'","name,id"); } }
写数据的代码位于model/ajax.class.php894 line
function zphcom_action() { if(!$this->uid || !$this->username || $_COOKIE['usertype']!=2 || $this->uid!=$_GET['uid']) { $arr['status']=0; $arr['content']=iconv("gbk","utf-8","您不是企业用户或者还没有登录,<a href='index.php?m=login&usertype=2'>请先登录</a>"); }elseif(!$_GET['pid']){ $arr['status']=0; $arr['content']=iconv("gbk","utf-8","你没有选择招聘会"); }elseif(!$_GET['jobid']){ $arr['status']=0; $arr['content']=iconv("gbk","utf-8","你还没有选择职位"); }elseif(is_array($this->obj->DB_select_once("zhaopinhui_com","uid='".(int)$_GET['uid']."' and zid='".(int)$_GET['pid']."'"))){ $arr['status']=0; $arr['content']=iconv("gbk","utf-8","您已经参与该招聘会"); }else{ $jobidarr=@explode(",",$_GET['jobid']); $array=array(); foreach($jobidarr as $v){ if(!in_array($v,$array)){ $array[]=$v; } } $sql['uid']=$_GET['uid']; $sql['zid']=$_GET['pid']; $sql['jobid']=@implode(",",$array); $sql['ctime']=mktime(); $sql['status']=0; $id=$this->obj->insert_into("zhaopinhui_com",$sql);