Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1198771
  • 博文数量: 272
  • 博客积分: 3899
  • 博客等级: 中校
  • 技术积分: 4734
  • 用 户 组: 普通用户
  • 注册时间: 2012-06-15 14:53
文章分类

全部博文(272)

文章存档

2012年(272)

分类: 网络与安全

2012-06-27 16:16:09

最近一年来很少写blog,主要原因是三个,一是一直以来在blog里说的,稍微带点技术的东西,因为可能未来在公司里用得上,所以不方便在blog上讲;二是最近转战微博了,也算是顺应潮流;三是最近一年多来,博文视点约我写了一本书,仅有的那么一点时间也要投入到写书大计中,实在没什么时间再写博客这种奢侈的东西。好在这本关于Web Security的书终于是快要写完了,在最近应该能够交稿,一共写了18章,第一次写书,没什么经验,还有很多不尽如人意的地方,但丑媳妇总得见公婆。

 

    未来可能时间会稍微多一点,其实最主要的原因是我的工作重心会从纷乱的项目中抽出来,往技术研究方面做些偏移,所以这个blog会慢慢的恢复更新。今天就先丢个小玩意上来吧,shopex的这个漏洞有点意思,虽然shopex的代码安全质量很有问题,但我们在此只关注这个有意思的地方,出于和谐的考虑,原文的POC代码也和谐掉了,见谅。

 

    我们知道弱伪随机数算法往往会带来很多安全问题,而程序员最容易犯的一个错误就是使用时间函数代替伪随机数算法,无形中使得本来容易犯错的随机数问题直接变成可预测的漏洞。

    shopex 4.8.5中,密码取回没有使用发送激活链接到邮箱的方式,而是直接生成一个新的密码发送到用户邮箱中。这个设计本身就不够安全,而新生成的密码算法是这样的:

/core/shop/controller/ctl.passport.php中:

    function sendPSW(){

        $this->begin($this->system->mkUrl('passport','lost'));

        $member=&$this->system->loadModel('member/member');

        $data=$member->getMemberByUser($_POST['uname']);

        if(($data['pw_answer']!=$_POST['pw_answer']) || ($data['email']!=$_POST['email'])){

            $this->end(false,__('问题回答错误或当前账户的邮箱填写错误'),$this->system->mkUrl('passport','lost'));

        }

 

        if( $data['member_id'] < 1 ){

            $this->end(false,__('会员信息错误'),$this->system->mkUrl('passport','lost'));

        }

 

        $messenger = &$this->system->loadModel('system/messenger');echo microtime()."
";

        $passwd = substr(md5(print_r(microtime(),true)),0,6);

 

        $pObj=$this->system->loadModel('member/passport');

        if ($obj=$pObj->function_judge('edituser')){

            $res = $obj->edituser($data['uname'],'',$passwd,$data['email'], '1');

            if ($res>0){

                $member->update(array('password'=>md5($passwd)),array('member_id'=>intval($data['member_id'])));

            }

            else{

                trigger_error('输入的旧密码与原密码不符!', E_USER_ERROR);

                return false;

            }

        }else{

            $member->update(array('password'=>md5($passwd)),array('member_id'=>intval($data['member_id'])));

        }

 

        $data['passwd'] = $passwd;

        $memberObj = &$this->system->loadModel('member/account');

        $memberObj->fireEvent('lostPw',$data,$data['member_id']);

 

        $this->end(true,__('邮件已经发送'),$this->system->mkUrl('passport','index'));

    }

 

    注意加粗的部分,新生成的用户密码实际上就是取了当前时间 microtime() md5值的前6位。

 

    PHP microtime()的值除了当前服务器的秒数外,还有微秒数,比如:

 0.55452100 1315562338

    微妙数的变化范围在0.000000 -- 0.999999 之间,一般来说,服务器的时间可以通过HTTP返回头的DATE字段来获取,因此我们只需要遍历这1000000可能值即可。

    但我们要使用暴力破解的方式发起1000000次网络请求的话,网络请求数也会非常之大。可是shopex非常贴心的在生成密码前再次将microtime() 输出了一次:

        $messenger = &$this->system->loadModel('system/messenger');echo microtime()."
";

    两次microtime()的调用间隔非常之短,使得我们破解的成本实际上非常低廉。至今仍然没有想明白shopex为什么要写这段代码,难道是开发留的后门?

 

    要成功利用这个漏洞需要满足以下条件:

 触发密码取回流程,这需要回答安全问题或者是用户注册邮箱填写正确;而shopex默认用户注册是只需要邮箱而不需要填写安全问题的

 

    在自动化验证新密码是否正确时,需要自动登录网站,而shopex的登录默认便是有验证码的。可是这个验证码的实现存在缺陷:

                if($_COOKIE["S_RANDOM_CODE"]!=md5($_POST['signupverifycode'])){

                    $this->splash('failed','back',__('验证码录入错误,请重新输入'),'','',$_POST['from_minipassport']);

                } 

    验证码的验证,实际上是比对两个值,一个是Cookie S_RANDOM_CODE的值,另一个是验证码的md5,因此通过一个小trick就能够绕过shopex的验证码机制:提交验证码为1234,并自定义cookie值为md5("1234"),这个验证码的校验将永远有效。

 

    结合上面这些,最终给出了我们的exploit

[]。。。。。。

 

测试如下:

D:\research\vulndb\shopex>python getpass.py target username email

(测试网站略)

 

 

在实际利用中,很多shopex小版本不返回微秒数,但能抓取到秒数。暴力破解微妙数的话,请求会过于频繁。在此不赘述了。

 

PS:此漏洞一月前已提交wooyun

 

阅读(1953) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~