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"
$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"
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