PHPWind 7.5 多处包含漏洞
作者: 80vul
1.描叙
api/class_base.php文件里callback函数里$mode变量没有过滤导致任意包含本地文件,从而可以执行任意PHP命令.
2. 具体分析
api/class_base.php文件里:
function callback($mode, $method, $params) {
if (!isset($this->classdb[$mode])) {
if (!file_exists(R_P.'api/class_' . $mode . '.php')) {
return new ErrorMsg(API_MODE_NOT_EXISTS, "Class($mode) Not Exists");
}
require_once(R_P.'api/class_' . $mode . '.php'); //这里
$this->classdb[$mode] = new $mode($this);
}
if (!method_exists($this->classdb[$mode], $method)) {
return new ErrorMsg(API_METHOD_NOT_EXISTS, "Method($method of $mode) Not Exists");
}
!is_array($params) && $params = array();
return @call_user_func_array(array(&$this->classdb[$mode], $method), $params);
}
我们继续跟一下具体变量传递的过程. 上面的函数在run()里有调用:
function run($request) {
$request = $this->strips($request);
if (isset($request['type']) && $request['type'] == 'uc') {
$this->type = 'uc';
$this->apikey = $GLOBALS['uc_key'];//注意这个变量也是该漏洞的关键
} else {
$this->type = 'app';
$this->apikey = $GLOBALS['db_siteownerid'];
$this->siteappkey = $GLOBALS['db_siteappkey'];
}
/***
if ($this->type == 'app' && !$GLOBALS['o_appifopen']) {
return new ErrorMsg(API_CLOSED, 'App Closed');
}
***/
ksort($request);
reset($request);
$arg = '';
foreach ($request as $key => $value) {
if ($value && $key != 'sig') {
$arg .= "$key=$value&";
}
}
if (md5($arg . $this->apikey) != $request['sig']) { //注意这个判断,需要绕过它.上面的代码可以看的出来$this->apikey = $GLOBALS['uc_key'],和$request['sig']我们
//都可以控制,那么很容易绕过它
return new ErrorMsg(API_SIGN_ERROR, 'Error Sign');
}
$mode = $request['mode']; //取$mode 没有过滤直接进入下面的callback()
$method = $request['method'];
$params = isset($request['params']) ? unserialize($request['params']) : array();
if (isset($params['appthreads'])) {
if (PHP_VERSION < 5.2) {
require_once(R_P.'api/class_json.php');
$json = new Services_JSON(true);
$params['appthreads'] = $json->decode(@gzuncompress($params['appthreads']));
} else {
$params['appthreads'] = json_decode(@gzuncompress($params['appthreads']),true);
}
}
if ($params && isset($request['charset'])) {
$params = pwConvert($params, $this->charset, $request['charset']);
}
return $this->callback($mode, $method, $params); //调用callback ()
}
我们继续看看run()函数的调用:
在pw_api.php文件里:
$api = new api_client();
$response = $api->run($_POST + $_GET);//直接run了$_POST , $_GET提交的变量.
上面的分析是逆行分析了整个漏洞变量提交的过程,其实我们这个漏洞还包含一次编码与解码的问:require_once(R_P.'api/class_' . $mode . '.php');这个需要绕过魔术引号才可以
包含容易文件.我们注意看run()的第一句
$request = $this->strips($request);
strips()的代码:
function strips($param) {
if (is_array($param)) {
foreach ($param as $key => $value) {
$param[$key] = $this->strips($value);
}
} else {
$param = stripslashes($param); //变量直接使用了stripslashes,那么我们可以直接绕过魔术引号了 :)
}
return $param;
}
3.POC/EXP
缺
4.FIX
由于漏洞信息的外泄,官方针对这个漏洞已经做出了修补:
http://www.phpwind.net/read-htm-tid-914851.html
具体代码:
require_once Pcv(R_P.'api/class_' . $mode . '.php');
function Pcv($filename,$ifcheck=1){
$tmpname = strtolower($filename);
$tmparray = array(' http://',"