报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告
漏洞作者:skysheep
分析作者:Seay
博客:http://www.cnseay.com/
漏洞分析:
漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。
public function account_manage_info() {
if(isset($_POST['dosubmit'])) {
//更新用户昵称
$nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : '';
if($nickname) {
$this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid']));
if(!isset($cookietime)) {
$get_cookietime = param::get_cookie('cookietime');
}
$_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0);
$cookietime = $_cookietime ? TIME + $_cookietime : 0;
param::set_cookie('_nickname', $nickname, $cookietime);
}
require_once CACHE_MODEL_PATH.'member_input.class.php';
require_once CACHE_MODEL_PATH.'member_update.class.php';
$member_input = new member_input($this->memberinfo['modelid']);
$modelinfo = $member_input->get($_POST['info']);
$this->db->set_model($this->memberinfo['modelid']);
$membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));
if(!empty($membermodelinfo)) {
$this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));
} else {
$modelinfo['userid'] = $this->memberinfo['userid'];
$this->db->insert($modelinfo);
}
代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看,
在\caches\caches_model\caches_data\ member_input.class.php 文件中:
?[Copy to clipboard]View Code PHP
function get($data) {
$this->data = $data = trim_script($data);
$model_cache = getcache('member_model', 'commons');
$this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename'];
$info = array();
$debar_filed = array('catid','title','style','thumb','status','islink','description');
if(is_array($data)) {
foreach($data as $field=>$value) {
if($data['islink']==1 && !in_array($field,$debar_filed)) continue;
$name = $this->fields[$field]['name'];
$minlength = $this->fields[$field]['minlength'];
$maxlength = $this->fields[$field]['maxlength'];
$pattern = $this->fields[$field]['pattern'];
$errortips = $this->fields[$field]['errortips'];
if(empty($errortips)) $errortips = "$name 不符合要求!";
$length = empty($value) ? 0 : strlen($value);
if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!");
if($maxlength && $length > $maxlength && !$isimport) {
showmessage("$name 不得超过 $maxlength 个字符!");
} else {
str_cut($value, $maxlength);
}
if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips);
if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!");
$func = $this->fields[$field]['formtype'];
if(method_exists($this, $func)) $value = $this->$func($field, $value);
$info[$field] = $value;
}
}
return $info;
}
trim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组,
再到phpcms\modules\member\index.php 文件account_manage_info函数
过了get()函数之后。
?[Copy to clipboard]View Code PHP
$modelinfo = $member_input->get($_POST['info']);
$this->db->set_model($this->memberinfo['modelid']);
$membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));
if(!empty($membermodelinfo)) {
$this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));
} else {
直接带入数据库,update函数我们跟进看看
?[Copy to clipboard]View Code PHP
public function update($data, $table, $where = '') {
if($table == '' or $where == '') {
return false;
}
$where = ' WHERE '.$where;
$field = '';
if(is_string($data) && $data != '') {
$field = $data;
} elseif (is_array($data) && count($data) > 0) {
$fields = array();
foreach($data as $k=>$v) {
switch (substr($v, 0, 2)) {
case '+=':
$v = substr($v,2);
if (is_numeric($v)) {
$fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false);
} else {
continue;
}
break;
case '-=':
$v = substr($v,2);
if (is_numeric($v)) {
$fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false);
} else {
continue;
}
break;
default:
$fields[] = $this->add_special_char($k).'='.$this->escape_string($v);
}
}
$field = implode(',', $fields);
} else {
return false;
}
$sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where;
print_r($sql);
return $this->execute($sql);
}
从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。
攻击测试:
测试地址 http://localhost
注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句
www.2cto.com补充临时解决方案:
暂时先去掉该功能吧
修改如下:
public function account_manage_info() {
include template('member', 'account_manage_info');
exit();
if(isset($_POST['dosubmit'])) {
//更新用户昵称