CmsEasy会员编辑资料安全性分析

0x00前言

最近一段时间尝试研究CmsEasy的安全性,分析了一下“会员编辑资料”功能的小问题。

0x01会员编辑资料安全分析

ps:front::$post进行了一些XSS、SQL等过滤,另外还有360Web防火墙,所以此处不关注这些漏洞,关注逻辑问题
会员中心->编辑资料

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
//D:\wamp64\www\cmseasy_5.6\lib\default\user_act.php
function edit_action()
{
if (front::post('submit')) {
unset(front::$post['username']);
unset(front::$post['groupid']);
unset(front::$post['powerlist']);
unset(front::$post['password']);
if (!is_email(front::$post['e_mail'])) {
alerterror(lang('mailbox_format_is_not'));
}

foreach (front::$post as $k => $v) {
if (is_array($v) && !empty($v)) {
front::$post[$k] = implode(',', $v);
}
front::check_type(front::post($k), 'safe');
}

$this->_user->rec_update(front::$post, "username='".session::get('username')."'");
//var_dump(front::$post);
front::flash(lang('modify_data_successfully'));
front::redirect(url::create('user/index'));
}
$this->view->data = $this->view->user;
//var_dump($this->view->data);
}

edit_action函数删除POST数据包中的username、groupid、powerlist、password字段,接着遍历POST数据,检测安全性,赋值后,调用$this->_user->rec_update更新数据库(这里考虑到PHP unset区分大小写,而Mysql不区分大小写,起初认为通过大小写绕过groupid的unset,直接提权为管理员,然而事与愿违……)

1
2
3
4
5
6
function rec_update($row,$where) {
$tbname=$this->name;
$sql=$this->sql_update($tbname,$row,$where);
//echo $sql."<br>";exit;
return $this->query_unbuffered($sql);
}

rec_update函数调用sql_update函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
//D:\wamp64\www\cmseasy_5.6\lib\inc\table.php
function sql_update($tbname,$row,$where) {
//var_dump($row);
$sqlud='';
if (is_string($row))
$sqlud = $row.' ';
else
foreach ($row as $key=>$value) {
if (in_array($key,explode(',',$this->getcolslist()))) {
$value=$value;
/*if (preg_match('/^\[(.*)\]$/',$value,$match))
$sqlud .= "`$key`"."= '".$match[1]."',";
else*/if ($value === "")
$sqlud .= "`$key`= NULL, ";
else
$sqlud .= "`$key`"."= '".$value."',";
}
}
$sqlud=rtrim($sqlud);
$sqlud=rtrim($sqlud,',');
$this->condition($where);
$sql="UPDATE `".$tbname."` SET ".$sqlud." WHERE ".$where;
//echo $sql;
return $sql;
}

sql_update函数调用$this->getcolslist()返回数据库中表的列名,接着用in_array判断字段名$key是否包含于数据库表的列名数组之内,如果不在数组之内直接忽略该字段。(in_array区分大小写,所以即使绕过了unset函数,也不能成功update数据库的username、groupid、powerlist、password字段)
不能提权利用,但是由于遍历POST数据后update操作,存在其他的安全问题,可以修改数据库中原本没有权限直接修改的数据,比如:isblock、isdelete、checked、userip等
例如:管理员在后台“冻结”并”清退”了test用户,该用户登录后修改资料,POST添加isblock=0&isdelete=0数据包即可解除“冻结”和”清退”状态,payload如下:

1
username=test&nickname=123&question=&isblock=0&isdelete=0&answer=&qq=1111111111&e_mail=test%40test.com&tel=18702111&address=123123&intro=123123&submit=%E6%8F%90%E4%BA%A4