漏洞概况

这个漏洞的成因是没有考虑到php是弱类型语言,不合理使用intval()函数而造成的任意用户登录。

环境搭建

  • espcms 6.4.15.08.25
  • php 5.6.40
  • Ubuntu 18.04 + Mysql 5.7 + Apache2

漏洞利用

搭建好环境后,我们先注册几个会员账号。

1

登录后台管理员账号后可以在会员管理这里看到注册的会员账号。

2

可以看到我们注册了5个用户,为了登录ID为3的用户,我们可以先注册一个以”数字3+字符串”的用户3test

3

刷新当前页面,用burpsuite抓包,将ecisp_member_info的值修改得和ecisp_member_info一样。

1
ecisp_member_username=57liGUuVT2EkJ0mZuAZgYCl%2B%2Fi3PJuRjagZ21ZMaR7I%3D; ecisp_member_info=57liGUuVT2EkJ0mZuAZgYCl%2B%2Fi3PJuRjagZ21ZMaR7I%3D;

4

发送修改后的数据包,发现成功登录了ID为3的用户lxx。

漏洞分析

interface/membermain.php:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
function in_center() {
if ($this->CON['mem_isucenter']) {
include_once admin_ROOT . 'public/uc_client/client.php';
}
parent::start_pagetemplate();
parent::member_purview();
$lng = (admin_LNG == 'big5') ? $this->CON['is_lancode'] : admin_LNG;
$db_where = "userid=$this->ec_member_username_id AND username='$this->ec_member_username' ";
$db_table1 = db_prefix . 'member AS a';
$db_table2 = db_prefix . 'member_value AS b';
$db_sql = "SELECT * FROM $db_table1 LEFT JOIN $db_table2 ON a.userid = b.userid WHERE a.userid = $this->ec_member_username_id ";
$rsMember = $this->db->fetch_first($db_sql);
$rsMember['userid'] = $this->ec_member_username_id;

$rsMember['rankname'] = $this->get_member_purview($rsMember['mcid'], 'rankname');
$userid = intval($rsMember['userid']);
if (empty($userid)) {
exit('user err!');
}
...

我们跟踪一下espcms验证用户信息的流程,in_center()函数会调用member_purview(),这是处理uid的函数。

public/class_connector.php :

6

先通过$this->fun->accept('ecisp_member_info', 'C')获取cookie中的ecisp_member_info值。

7

这里没有传入$htmlcode,所以$htmlcode默认为true,会执行

1
($rehtml ? $this->preg_htmldecode($putvalue) : $this->htmldecode($putvalue))

又因为没有传入$rehtml,所以$rehtml默认为false,会执行

1
$this->preg_htmldecode($putvalue)

返回的结果就是57liGUuVT2EkJ0mZuAZgYCl+/i3PJuRjagZ21ZMaR7I=

10

接着返回的字符串就会传入$this->fun->eccode($this->fun->accept('ecisp_member_info', 'C'), 'DECODE', db_pscode)。跟进eccode()函数,发现会对传入的字符串进行解密操作。

8

经过decryptCookie()函数后就可以获得解密后的ecisp_member_info值。

9

比如我们新注册的用户是3test,那么返回的$user_info值就是3test,然后将这个值赋给$ec_member_username_id

漏洞的主要成因就是对$ec_member_username_id进行了intval操作。因为php是弱类型语言,所以3test就会被intval理解为3

1
$this->ec_member_username_id = intval($ec_member_username_id);

执行完member_purview()函数,$this->ec_member_username_id就是3,此时$db_where就是

1
userid=3 AND username='3test'
1
2
3
4
5
6
7
8
9
10
function in_center(){
...
...
$db_where = "userid=$this->ec_member_username_id AND username='$this->ec_member_username' ";
$db_table1 = db_prefix . 'member AS a';
$db_table2 = db_prefix . 'member_value AS b';
$db_sql = "SELECT * FROM $db_table1 LEFT JOIN $db_table2 ON a.userid = b.userid WHERE a.userid = $this->ec_member_username_id ";
...
...
}

此时sql查询语句为:

1
SELECT * FROM espcms_member AS a LEFT JOIN espcms_member_value AS b ON a.userid = b.userid  WHERE a.userid = 3

可以看到这时返回的结果就是ID为3的用户lxx。

11

官方补丁

这个补丁完全可以绕过啊,官方没有没有成功修复这个漏洞orz…

patch