首页 > 其他分享 >关于CVE-2024-9047的分析

关于CVE-2024-9047的分析

时间:2025-01-14 20:00:10浏览次数:1  
标签:code filepath downloader 2024 wfu file CVE 9047 WFU

1 漏洞成因

  本文的分析基于 wp-file-upload.4.24.11。在wfu_file_downloader.php中存在可控变量$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);
}
// 省略部分代码

/**
 * wfu_fopen_for_downloader
 * 当$filepath前7个字符不为"sftp://"时,打开文件
 */
function wfu_fopen_for_downloader($filepath, $mode) {
	if ( substr($filepath, 0, 7) != "sftp://" ) return @fopen($filepath, $mode);
	// 省略
}

  下文所描述的代码行号均为4.24.11版本中 wfu_file_downloader.php的行号。为方便浏览,下方已将此版本源码折叠。

wfu_file_downloader.php源码
 <?php
if ( !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();
}

function wfu_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 die
	if ( !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 again
	WFU_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 operation
	if ( 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.txt
	elseif ( 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 needed
	WFU_USVAR_unset_downloader('wfu_storage_'.$file_code);
	//check that file exists
	if ( !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>');
	}
}

function wfu_update_download_status($ticket, $new_status) {
	require_once WFU_USVAR_downloader('wfu_ABSPATH').'wp-load.php';
	WFU_USVAR_store('wfu_download_status_'.$ticket, $new_status);
}

function WFU_USVAR_exists_downloader($var) {
	global $wfu_user_state_handler;
	if ( $wfu_user_state_handler == "dboption" && WFU_VAR("WFU_US_DBOPTION_BASE") == "cookies" ) return isset($_COOKIE[$var]);
	else return WFU_USVAR_exists_session($var);
}

function WFU_USVAR_downloader($var) {
	global $wfu_user_state_handler;
	if ( $wfu_user_state_handler == "dboption" && WFU_VAR("WFU_US_DBOPTION_BASE") == "cookies" ) return $_COOKIE[$var];
	else return WFU_USVAR_session($var);
}

function WFU_USVAR_unset_downloader($var) {
	global $wfu_user_state_handler;
	if ( $wfu_user_state_handler == "session" || $wfu_user_state_handler == "" ) WFU_USVAR_unset_session($var);
}

function wfu_file_exists_for_downloader($filepath) {
	if ( substr($filepath, 0, 7) != "sftp://" ) return file_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;
}

function wfu_filesize_for_downloader($filepath) {
	if ( substr($filepath, 0, 7) != "sftp://" ) return filesize($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;
}

function wfu_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;
}

function wfu_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;
}

2 代码逻辑

2.1 分析思路

  本节描述漏洞的分析思路,从漏洞点开始,直至分析参数部分。

2.1.1 Part 1

  在漏洞点位置,文件路径变量 $filepath 首先会在第 77-80 行被检查。

if ( !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>');
}

   由于存在 die(),因此 if 的判断条件必须为 false。查看 wfu_file_exists_for_downloader():

function wfu_file_exists_for_downloader($filepath) {
	if ( substr($filepath, 0, 7) != "sftp://" ) return file_exists($filepath);
	// 省略
}

  由代码可知,此处仅为检查 $filepath 是否存在。无需过多注意。继续推理逻辑。

2.1.2 Part 2

  在第44-73行有一个 if 判断。代码如下所示:

if ( 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.txt
elseif ( 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;
}

  该处 if 语句用于判断 $file_code的前几个字符是否为"exportdata"或者"debuglog",但是我们不想让可操控的文件路径受限,因此重点关注最后的 else 。

  首先,关注$filepath = WFU_USVAR_downloader('wfu_storage_'.$file_code);。查看WFU_USVAR_downloader()的代码:

function WFU_USVAR_downloader($var) {
	global $wfu_user_state_handler;
	if ( $wfu_user_state_handler == "dboption" && WFU_VAR("WFU_US_DBOPTION_BASE") == "cookies" ) return $_COOKIE[$var];
	else return WFU_USVAR_session($var);
}
  WFU_USVAR_downloader()判断全局变量 $wfu_user_state_handler 是否为 "dboption",并且 WFU_VAR("WFU_US_DBOPTION_BASE") 是否为 "cookies"。如果都为 true,则返回 cookie中的 $var的值。此处即返回 cookie中 'wfu_storage_'.$file_code的值。如果能够使 if 条件为真,再加上cookie可控,那么WFU_USVAR_downloader()的返回值也可控。下面详细分析 if 判断条件。
  • $wfu_user_state_handler == "dboption"
  首先,查看前面的判断内容。查找全局变量 $wfu_user_state_handler,发现其在第18行被定义:
$GLOBALS["wfu_user_state_handler"] = wfu_sanitize_code($handler);
  wfu_sanitize_code的作用是删除所传参数中的非字母数字。继续查找 $handler,在第6行发现,且$handler可控:
$handler = (isset($_POST['handler']) ? $_POST['handler'] : (isset($_GET['handler']) ? $_GET['handler'] : '-1'));

  因此,$handler 可控 ==> $wfu_user_state_handler 可控 ==> $wfu_user_state_handler == "dboption" 为true。

  • WFU_VAR("WFU_US_DBOPTION_BASE") == "cookies"
  再来看后面的判断内容。WFU_VAR()代码如下:
function WFU_VAR($varname) {
	if ( !isset($GLOBALS["WFU_GLOBALS"][$varname]) ) return false;
	if ( $GLOBALS["WFU_GLOBALS"][$varname][5] ) return $GLOBALS["WFU_GLOBALS"][$varname][3];
	//in case the environment variable is hidden then return the default value
	else return $GLOBALS["WFU_GLOBALS"][$varname][2];
}

  WFU_VAR() 在此处的作用是:在 $GLOBALS["WFU_GLOBALS"]["WFU_US_DBOPTION_BASE"] 存在时,返回$GLOBALS["WFU_GLOBALS"]["WFU_US_DBOPTION_BASE"][3],否则返回$GLOBALS["WFU_GLOBALS"]["WFU_US_DBOPTION_BASE"][5]。追踪 "WFU_GLOBALS" 和 "WFU_US_DBOPTION_BASE",在第15行发现相关代码:

$GLOBALS["WFU_GLOBALS"]["WFU_US_DBOPTION_BASE"] = array( "", "", "", wfu_sanitize_code($dboption_base), "", true );

  继续追踪 $dboption_base,发现该变量在第8行被赋值且完全可控:

$dboption_base = (isset($_POST['dboption_base']) ? $_POST['dboption_base'] : (isset($_GET['dboption_base']) ? $_GET['dboption_base'] : '-1'));
     因此,$dboption_base 可控 ==> $GLOBALS["WFU_GLOBALS"]["WFU_US_DBOPTION_BASE"][3] 可控 ==> WFU_VAR("WFU_US_DBOPTION_BASE") == "cookies" 为true。   根据以上分析可知,$wfu_user_state_handler == "dboption" && WFU_VAR("WFU_US_DBOPTION_BASE") == "cookies"可以为true,并且$_COOKIE[$var]可控。
  再来看第70行代码:
$filepath = ( substr($filepath, 0, 6) == 'ftp://' || substr($filepath, 0, 7) == 'ftps://' || substr($filepath, 0, 7) == 'sftp://' ? $filepath : WFU_USVAR_downloader('wfu_ABSPATH').$filepath );
  此处代码从cookie中获取 "wfu_ABSPATH"的值,并与 $filepath 拼接。   $filepath 的值与 $file_code 有关,因此下面分析 $file_code。  

2.1.3 Part 3

   $file_code相关代码如下所示:

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 die
if ( !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();
}
  根据以上代码可知,$file_code 和 $ticket 可由 GET 或者 POST 方法赋值。   wfu_initialize_user_state(),与cookie和session相关,具体分析放到 Part 4。   WFU_USVAR_exists_downloader($var)的作用就是检查 cookie 中是否存在 $var 。
function WFU_USVAR_exists_downloader($var) {
	global $wfu_user_state_handler;
	if ( $wfu_user_state_handler == "dboption" && WFU_VAR("WFU_US_DBOPTION_BASE") == "cookies" ) return isset($_COOKIE[$var]);
	else return WFU_USVAR_exists_session($var);
}
  为了避免进入程序被 die(),cookie 中必须存在 'wfu_download_ticket_'.$ticket,且值必须大于当前的时间戳。

2.1.4 Part 4

  由于测试漏洞时,这里出现了一些bug,所以单开一个part分析wfu_initialize_user_state(),该函数的代码如下:

function wfu_initialize_user_state() {
	$a = func_get_args(); $a = WFU_FUNCTION_HOOK(__FUNCTION__, $a, $out); if (isset($out['vars'])) foreach($out['vars'] as $p => $v) $$p = $v; switch($a) { case 'R': return $out['output']; break; case 'D': die($out['output']); }
	global $wfu_user_state_handler;
	if ( $wfu_user_state_handler == "dboption" && WFU_VAR("WFU_US_DBOPTION_BASE") == "cookies" ) {
		if ( wfu_get_session_cookie() == "" ) wfu_set_session_cookie();
	}
	elseif ( WFU_VAR("WFU_US_SESSION_LEGACY") == "true" && !headers_sent() && ( function_exists("session_status") ? ( PHP_SESSION_ACTIVE !== session_status() ) : ( session_id() == "" ) ) ) { session_start(); }
}
  • Bug 由来

  要想触发漏洞, $wfu_user_state_handler == "dboption" && WFU_VAR("WFU_US_DBOPTION_BASE") == "cookies"一定成立,因此一定会触发判断:if ( wfu_get_session_cookie() == "" ) wfu_set_session_cookie();。这里再来看一下 wfu_get_session_cookie():

function wfu_get_session_cookie() {
	return isset($_COOKIE[WPFILEUPLOAD_COOKIE]) ? wfu_sanitize_code(substr($_COOKIE[WPFILEUPLOAD_COOKIE], 0, 32)) : "";
}

  wfu_get_session_cookie()会首先判断cookie中是否存在WPFILEUPLOAD_COOKIE,如果存在就取前32个字符,否则为空。

  一开始,我没有设置WPFILEUPLOAD_COOKIE,因为我以为就算为空, wfu_set_session_cookie() 会初始化有关内容,所以没有继续分析,结果在测试漏洞时报错了:

  只能分析报错的代码:

function wfu_set_session_cookie() {
	$a = func_get_args(); $a = WFU_FUNCTION_HOOK(__FUNCTION__, $a, $out); if (isset($out['vars'])) foreach($out['vars'] as $p => $v) $$p = $v; switch($a) { case 'R': return $out['output']; break; case 'D': die($out['output']); }
	if ( !headers_sent() ) {
		$cookie = wfu_create_random_string(32);
		setcookie(
			WPFILEUPLOAD_COOKIE,
			$cookie,
			time() + intval(WFU_VAR("WFU_US_COOKIE_LIFE")) * 3600,
			COOKIEPATH ? COOKIEPATH : '/',
			COOKIE_DOMAIN,
			false,
			false
		);
		$_COOKIE[WPFILEUPLOAD_COOKIE] = $cookie;
	}
}

  根据报错,COOKIEPATH未定义,而我也没找到COOKIEPATH到底在哪里定义的,所以只能尝试其他方法。

  • 另辟蹊径

  我的主要目的的分析漏洞,又不是写插件。既然wfu_set_session_cookie会触发漏洞,那么就尽可能不让它执行。所以,必须要使 wfu_get_session_cookie != "",而要使wfu_get_session_cookie != "",cookie中必须存在WPFILEUPLOAD_COOKIE。

  追踪 WPFILEUPLOAD_COOKIE,发现在 wfu_file_downloader.php 的第17行被定义:

if ( !defined("WPFILEUPLOAD_COOKIE") ) DEFINE("WPFILEUPLOAD_COOKIE", wfu_sanitize_tag($wfu_cookie));

  $wfu_cookie则在第10行:

$wfu_cookie = (isset($_POST['wfu_cookie']) ? $_POST['wfu_cookie'] : (isset($_GET['wfu_cookie']) ? $_GET['wfu_cookie'] : ''));

  因此,完全可以控制WPFILEUPLOAD_COOKIE,避免触发 wfu_set_session_cookie()

2.1.5 Part 5

   漏洞点的位置在wfu_download_file()中,因此分析wfu_download_file()的调用。代码如下所示:

<?php
if ( !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();
}

  $handler、$session_legacy、$dboption_base、$dboption_useold、$wfu_cookie均可通过GET或者POST传参。只要注意别被 die() 就行。

2.1.6 Part 6

  总结:

  • GET/POST 传递
    • handler = "dboption",固定值
    • session_legacy = 1,随意
    • dboption_base = "cookie",固定值
    • dboption_useold = 1,随意
    • wfu_cookie = abccc,与Cookie对应
    • file=abc123,与Cookie对应
    • ticket=bcd234,与Cookie对应
  • Cookie 传递
    • wfu_download_ticket_bcd234=很大的时间戳
    • wfu_storage_abc123=文件名,可以路径穿越
    • wfu_ABSPATH=目录
    • abccc=qwe,abccc与GET/POST对应,值随意

2.1.7 Part 7

  测试结果如下图所示:

 

3. 总结

  可以通过GET/POST传参控制 $file_code,再控制 Cookie,从而给$filepath赋值,最后输出 $filepath 的内容。过程中需要满足一些条件以免die()或者bug

 

标签:code,filepath,downloader,2024,wfu,file,CVE,9047,WFU
From: https://www.cnblogs.com/brilliantHongXi/p/18671359

相关文章

  • 【建议搜藏】最新版IDEA2024.3.1.1简介及使用
    IntelliJIDEA介绍IntelliJIDEA是一款用于Java语言开发的集成开发环境(IDE)。IntelliJIDEA被业界公认为最好的Java开发工具,尤其在智能代码助手、代码自动提示、重构、J2EE支持、Ant、JUnit、CVS整合、代码审查、创新的GUI设计等方面的功能可以说是超棒的。IntelliJIDEA由JetBra......
  • 2024秋季学期 理论力学期末复习笔记
    参考资料[1]秦敢,向守平.力学与理论力学(下册)[M].科学出版社,2017.8.[2]曹利明.理论力学课程讲义[Z].中国科学技术大学,2024.拉格朗日力学哈密顿力学刚体部分......
  • Xmind Pro 2024 24下载及安装破解教程
    Xmind应该是目前最好用的一款思维导图软件了。拥有优秀的用户体验,凭借简单易用,功能强大的特点,XMind在2013年被著名互联网媒体Lifehacker评选为全球最受欢迎的思维导图软件。Xmind具有如下优点①、用心打磨16年的思维导图软件②、评分高,多次获得推荐③、装机量超过1亿,深受全......
  • 国内汽车法规政策标准解读:GB/T 44464-2024《汽车数据通用要求》
    目录背景介绍概要General标准适用范围重要规定与要求汽车数据安全管理体系要求扩展:汽车数据安全管理体系(DSMS)个人信息保护要求个人信息处理通用要求个人同意个人信息收集个人信息存储个人信息使用个人信息传输个人信息删除个人信息出境重要数据保护要求重要......
  • 2024大模型实战指南:大模型学习,从小白到专家的详细步骤与进阶策略!
    前言随着人工智能技术的迅猛发展,大模型(LargeModels)已成为这一领域的新宠。从GPT系列到BERT,再到各类变体,大模型以其强大的能力吸引了无数开发者和研究者的目光。那么,作为一个零基础的学习者,如何快速入门并精通大模型技术呢?本文将为你提供一份详尽的学习指南。一、大模型基......
  • 2024年度总结:寻找平衡
    文章目录前言我的非工作日我的网络安全2025年我给大家送的礼物......
  • 个人学习笔记:2024年flatpak,snap,appimage信息收集
    Flatpak的重大改进支持Wayland安全上下文协议Flatpak在2024年引入了对Wayland安全上下文协议的支持,这一改进显著提升了沙盒环境的安全性和功能性。通过Wayland合成器的支持,Flatpak能够更精确地控制应用程序的行为,既保证了系统的安全性,又提高了应用程序的功能性......
  • VP Toyota Programming Contest 2024#12(AtCoder Beginner Contest 384)
    A-aaaadaa题意:给你一个字符串和两个字符\(c_1\),\(c_2\),把字符串里的所有不等于\(c_1\)的字符都换成\(c_2\)。模拟即可。点击查看代码voidsolve(){intn;chara,b;std::cin>>n>>a>>b;std::strings;std::cin>>s;for(auto&c:......
  • 2024ICPC(香港)游记
    转自MyBlog虽然2025了再写好像有点迟就是了。day-180?大一xdx太nb导致的,本来预估网络赛400有两场,600校内出线,结果被xdx搞成网络赛200有两场,400校内出线。632遗憾退场。day-100?老师给南京站争取了一个外卡,可惜20分罚时之差给了我们前一队。这么说去年邀请赛也是,虽然298分过了......
  • 痞子衡嵌入式:我评上了2024年度电子星球(eestar)最强大脑
    今天收到了「电源网旗下电子星球」颁发的2024年度最强大脑奖牌,这是电子星球第二年给痞子衡颁奖了。这个奖牌设计得非常用心,区别于去年奖牌只能捧在手上,今年痞子衡可以把奖牌挂脖子上出去拉风了。从23年8月开始,电子星球小编每个工作日会转发一篇痞子衡的技术原创文章,直到24年6......