WooYun-2013-24984:PHPCMS最新版(V9)SQL注入一枚
漏洞作者: blue
来源:http://www.wooyun.org/bugs/wooyun-2013-024984
简要描述
比较有意思的一个SQL注入点,代码分析是个体力活,唿~
详细说明
存在于在线充值功能,直接上代码分析,建议先看漏洞证明:
/phpcms/phpcms/modules/pay/deposit.php 96行起的pay_recharge方法
...
$trade_sn = param::get_cookie('trade_sn'); //约110行位置,如果可以控制$trade_sn,即可注入,事实自然是可以的
$usernote = $_POST['info']['usernote'] ? $_POST['info']['name'].'['.$trade_sn.']'.'-'.new_html_special_chars(trim($_POST['info']['usernote'])) : $_POST['info']['name'].'['.$trade_sn.']';
$surplus = array(
'userid' => $this->_userid,
'username' => $this->_username,
'money' => trim(floatval($_POST['info']['price'])),
'quantity' => $_POST['quantity'] ? trim(intval($_POST['quantity'])) : 1,
'telephone' => preg_match('/[0-9\-]+/', $_POST['info']['telephone']) ? trim($_POST['info']['telephone']) : '',
'contactname' => $_POST['info']['name'] ? trim($_POST['info']['name']).L('recharge') : $this->_username.L('recharge'),
'email' => is_email($_POST['info']['email']) ? trim($_POST['info']['email']) : '',
'addtime' => SYS_TIME,
'ip' => ip(),
'pay_type' => 'recharge',
'pay_id' => $payment['pay_id'],
'payment' => trim($payment['pay_name']),
'ispay' => '1',
'usernote' => $usernote,
'trade_sn' => $trade_sn,
);
$recordid = $this->handle->set_record($surplus); //直到这里,也没有对$trade_sn进行处理吧?接下来看set_record方法
/phpcms/phpcms/modules/pay/classes/pay_deposit.class.php 12行起
/**
* 生成流水记录
* @param unknown_type
*/
public function set_record($data){
$require_items = array('userid','username','email','contactname','telephone','trade_sn','money','quantity','addtime','paytime','usernote','usernote','pay_type','pay_id','payment','ip','status');
if(is_array($data)) {
foreach($data as $key=>$item) {
if(in_array($key,$require_items)) $info[$key] = $item;
}
} else {
return false;
}
$trade_exist = $this->account_db->get_one(array('trade_sn'=>$info['trade_sn'])); //这里
if($trade_exist) return $trade_exist['id'];
$this->account_db->insert($info); //还有这里
return $this->account_db->insert_id();
}
好了,关键是控制$trade_sn的值,看param::get_cookie和param::set_cookie方法
/phpcms/phpcms/libs/classes/param.class.php
public static function set_cookie($var, $value = '', $time = 0) {
$time = $time > 0 ? $time : ($value == '' ? SYS_TIME - 3600 : 0);
$s = $_SERVER['SERVER_PORT'] == '443' ? 1 : 0;
$var = pc_base::load_config('system','cookie_pre').$var;
$_COOKIE[$var] = $value;
if (is_array($value)) {
foreach($value as $k=>$v) {
setcookie($var.'['.$k.']', sys_auth($v, 'ENCODE'), $time, pc_base::load_config('system','cookie_path'), pc_base::load_config('system','cookie_domain'), $s);
}
} else {
setcookie($var, sys_auth($value, 'ENCODE'), $time, pc_base::load_config('system','cookie_path'), pc_base::load_config('system','cookie_domain'), $s); //cookie加密了,而且方法很给力,sys_auth是有auth_key的,基本上可以说破解这个值不容易
}
}
......
public static function get_cookie($var, $default = '') {
$var = pc_base::load_config('system','cookie_pre').$var;
return isset($_COOKIE[$var]) ? sys_auth($_COOKIE[$var], 'DECODE') : $default; //这里是解密方法,咱也用不上
}
看来想自己更改cookie值很难,不知道加密的auth_key值嘛,可是...如果利用一个能set_cookie($value)的点,并且咱们能控制$value呐?这个点自然是有的~
/phpcms/phpcms/modules/attachment/attachments.php 228行起
public function swfupload_json() {
$arr['aid'] = intval($_GET['aid']); //这个不行,intval了
$arr['src'] = trim($_GET['src']); //这个可以,虽然$_GET会addslashes,但下面的json_encode会帮上忙(此时' => \',这里是一个反斜线 )
$arr['filename'] = urlencode($_GET['filename']); //这个不行,urlencode了
$json_str = json_encode($arr); (此时 \' => \\' 这里是两个反斜线)所以单引号可以用了
$att_arr_exist = param::get_cookie('att_json');
$att_arr_exist_tmp = explode('||', $att_arr_exist);
if(is_array($att_arr_exist_tmp) && in_array($json_str, $att_arr_exist_tmp)) {
return true;
} else {
$json_str = $att_arr_exist ? $att_arr_exist.'||'.$json_str : $json_str;
param::set_cookie('att_json',$json_str); //这里
return true;
}
}
漏洞证明
1.在COOKIE att_json为空时(当然可以手动清空),访问以下链接生成att_json
http://localhost/test/phpcms/index.php?m=attachment&c=attachments&a=swfupload_json&src=1%27&filename=a%27 (当然,你可以干点别的)
2.提交在线充值时,更改COOKIE trade_sn为att_json的值
修复方案
对$trade_sn进行addslashes