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 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>');}
}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_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 valueelse 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 );
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();
}
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);
}
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