cmseasy 全站用户cookie枚举伪造登录

2015-02-02T00:00:00
ID SSV:94046
Type seebug
Reporter Root
Modified 2015-02-02T00:00:00

Description

简要描述:

cmseasy 全站用户cookie枚举伪造登录

详细说明:

此问题 比较棘手,原因出在openid上面 我们之前有分析过一个案例: WooYun: cmseasy 逻辑缺陷可升级普通用户为管理员(shell还会难吗) 这里不赘述了,怎样去修改这个openid呢 我们发送url: http://localhost:8080/cmseasy/uploads/index.php?case=user&act=respond&ologin_code=alipaylogin&user_id=2&real_name=test&token=xxxx 此时session里面openid已经为2 我们打印一下: user_act.php:

``` include_once ROOT.'/lib/plugins/ologin/'.$classname.'.php'; $ologinobj = new $classname(); $status = $ologinobj->respond();

    $where[$classname] = session::get('openid');
    if(!$where[$classname]) front::redirect(url::create('user'));
    $user = new user();
    $data = $user->getrow($where);
    var_dump(session::get('openid'));exit;

```

如图所示:

<img src="https://images.seebug.org/upload/201502/0115504709831dbca055d60c6ed344164a7fbddd.png" alt="1.png" width="600" onerror="javascript:errimg(this);">

此时的 session 确实是我们设置的 分析一下这一段代码的逻辑:

``` include_once ROOT.'/lib/plugins/ologin/'.$classname.'.php'; $ologinobj = new $classname(); $status = $ologinobj->respond();

    $where[$classname] = session::get('openid');
    if(!$where[$classname]) front::redirect(url::create('user'));
    $user = new user();
    $data = $user-&gt;getrow($where);
    //var_dump(session::get('openid'));exit;
    if(!$data){
        $this-&gt;view-&gt;data = $status;
    }else{
        cookie::set('login_username',$data['username']);
        cookie::set('login_password',front::cookie_encode($data['password']));
        session::set('username',$data['username']);
        front::redirect(url::create('user'));
    }

```

如果classname为alipaylogin,然后做一下sql查询 2015-02-01 15:40 SELECT * FROM cmseasy_user WHERE alipaylogin='2' ORDER BY 1 desc limit 1 这里的这个alipaylogin 结果是唯一的 下来查询到了,就把用户名和密码暴露到cookie里面,此时我们就可以拿到这个cookie伪造登录了 那么问题来了,我们看看数据结构

<img src="https://images.seebug.org/upload/201502/01155419d79c347be1a0969d495f771d78f62ade.png" alt="2.png" width="600" onerror="javascript:errimg(this);">

发现这里是没有记录用户唯一性的第三方登录标志的id的 我们大胆猜想一下,如果用户采用第三方登录 或者第三方注册,那么这个alipaylogin 值会不会生成,而且永远不变呢 代码向上推 首先看注册的地方:

if(front::post('username') &&front::post('password')) { $username=front::post('username'); $username=str_replace('\\', '', $username); $password=md5(front::post('password')); $data=array( 'username'=&gt;$username, 'password'=&gt;$password, 'groupid'=&gt;101, 'userip'=&gt;front::ip(), $classname=&gt;session::get('openid'), ); if($this-&gt;_user-&gt;getrow(array('username'=&gt;$username))) { front::flash(lang('该用户名已被注册!')); return; }

发现了 这个位置当你采用第三方注册时候 这里就会生成alipaylogin 或者qqlogin的数值 而且经过我们的排查,这个值永远不变随着用户的注册之后,绑定了 我们在往下看第三方登录这里:

if (front::post('submit')) { if (front::post('username') && front::post('password')) { $username = front::post('username'); $password = md5(front::post('password')); $data = array( 'username' =&gt; $username, 'password' =&gt; $password, ); $user = new user(); $row = $user-&gt;getrow(array('username' =&gt; $data['username'], 'password' =&gt; $data['password'])); if (!is_array($row)) { $this-&gt;login_false(); return; } $post[$classname] = session::get('openid');

发现了 正式用户刚才注册的然后在填写用户名密码进行登录 分析到这我们清楚了一个道理,如果用用户采用第三方去注册,那么我们是可以获取到这个用户的用户名和密码的cookie的 我们在看看正常的用户登录这里需要的cookie是否和我们刚才通过第三方拿到的cookie结构是否一样

function login_action() { if(!$this-&gt;loginfalsemaxtimes()) if(front::post('submit')) { if(config::get('verifycode')) { if(!session::get('verify') ||front::post('verify')&lt;&gt;session::get('verify')) { front::flash(lang('验证码错误!')."&lt;a href=''&gt;".lang('backuppage')."&lt;/a&gt;"); return; } } if(front::post('username') &&front::post('password')) { $username=front::post('username'); $password=md5(front::post('password')); $data=array( 'username'=&gt;$username, 'password'=&gt;$password, ); $user=new user(); $user=$user-&gt;getrow(array('username'=&gt;$data['username'],'password'=&gt;$data['password'])); if(!is_array($user)) { $this-&gt;login_false(); return; } $user=$data; cookie::set('login_username',$user['username']); cookie::set('login_password',front::cookie_encode($user['password']));

发现填写的cookie 和第三方要构造的 一模一样 下载 剩下的问题就是 ,如果所有权限的入口只是查了一下cookie里面的name和passwd,我们的分析就正确无误:

function init() { $user=''; if(cookie::get('login_username') &&cookie::get('login_password')) { $user=new user(); $user=$user-&gt;getrow(array('username'=&gt;cookie::get('login_username'))); if(cookie::get('login_password')!=front::cookie_encode($user['password'])){ unset($user); } }

user_act里面的 符合我们刚才分析的,其他的我们就不看了,我们看一下管理员: 管理员这里比较bug,站点安装完成之后,管理员的 比如说alipaylogin 是空的 那么我们只要把openid设置为空就直接可以查看管理员cookie了 分析到此为止 我们来实际操作一下 第三方注册一个用户: url: http://localhost:8080/cmseasy/uploads/index.php?case=user&act=respond&ologin_code=alipaylogin postdata: regsubmit=xxx&username=test2&password=111111 我们怎么去获取test2用户cookie,并且伪造登录: 打印一下相关的数据:

$where[$classname] = session::get('openid'); if(!$where[$classname]) front::redirect(url::create('user')); $user = new user(); $data = $user-&gt;getrow($where); var_dump($data); if(!$data){ $this-&gt;view-&gt;data = $status; }else{ cookie::set('login_username',$data['username']); cookie::set('login_password',front::cookie_encode($data['password'])); session::set('username',$data['username']); var_dump($_SESSION); var_dump($_COOKIE); exit; front::redirect(url::create('user')); }

访问url: http://localhost:8080/cmseasy/uploads/index.php?case=user&act=respond&ologin_code=alipaylogin&user_id=1&real_name=test&token=xxxx 我没有登录,获取到了这个的cookie:

<img src="https://images.seebug.org/upload/201502/011621351038df59956a093a4b703d7c65801990.png" alt="3.png" width="600" onerror="javascript:errimg(this);">

注释,由于我们alipaylogin.php,上下文查看没有可以影响到user_id的传递,本地没有搭建第三方 我们模拟了一下

//var_dump($alipayNotify); $verify_result = $alipayNotify-&gt;verifyReturn(); //var_dump($verify_result); if(true || $verify_result) {//验证成功 $user_id = front::$get['user_id']; $token = front::$get['token']; session::set('access_token',$token); session::set("openid",$user_id);

第三方这里的恒等true 进入到设置session里面来 ,这个在之前的一个漏洞已经说明原因 分析到此为止

漏洞证明: