0x01 安装dedecms

dedecms安装链接:

https://github.com/dedecms/DedeCMS/releases/tag/v5.8.1

0x02 漏洞分析

1.plus/flink.php脚本内部

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

这里调用了ShowMsg(),跟进这个函数

2.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;
​        }
​    }
    ...............................................................
        
​    $tpl = new DedeTemplate();
​    $tpl->LoadString($msg);
​    $tpl->Display();
}

首先当gourl=1HTTPREFERERLoadString(gourl=-1时,HTTP_REFERER时可控的,后续会在LoadString(msg)加载,在Display()调用解析。

3.include/dedetemplate.class.php

public function LoadString($str = '')
{
    $this->sourceString = $str;
    $hashcode = md5($this->sourceString);
    $this->cacheFile = $this->cacheDir . "/string_" . $hashcode . ".inc";
    $this->configFile = $this->cacheDir . "/string_" . $hashcode . "_config.inc";
    $this->ParseTemplate();
}

public function Display()
{
    global $gtmpfile;
    extract($GLOBALS, EXTR_SKIP);
    $this->WriteCache();
    include $this->cacheFile;
}

sourceString设置与攻击者控制的$msg会在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) {
​                $this->ParseTemplate();
​            }
​            $fp = fopen($this->cacheFile, 'w') or dir("Write Cache File Error! ");
​            flock($fp, 3);
​            $result = trim($this->GetResult());
​            $errmsg = '';
​            //var_dump($result);exit();
​            if (!$this->CheckDisabledFunctions($result, $errmsg)) {
​                fclose($fp);
​                @unlink($this->cacheFile);
​                die($errmsg);
​         }
​     fwrite($fp, $result);
​     fclose($fp);
    .........
}
public function GetResult()
{
​    if (!$this->isParse) {
​        $this->ParseTemplate();
​    }
​    $addset = '';
​    $addset .= '<' . '?php' . "\r\n" . 'if(!isset($GLOBALS[\'_vars\'])) $GLOBALS[\'_vars\'] = array(); ' . "\r\n" . '$fields = array();' . "\r\n" . '?' . '>';
​    return preg_replace("/\?" . ">[ \r\n\t]{0,}<" . "\?php/", "", $addset . $this->sourceString);
}

调用GetResult返回值sourceString来设置$result变量,该变量现在包含攻击者控制的输入。然后在CheckDisabledFunctions函数在$result变量上被调用

public function CheckDisabledFunctions($str, &$errmsg = '')
{
    global $cfg_disable_funs;
    $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 href="http://help.dedecms.com/install-use/apply/2013/0711/2324.html" target="_blank">more...</a>';
                    return false;
                }
            }
        }
    }
    return true;
}

GetResult()方法执行后返回结果通过CheckDisabledFunctions过滤,然后经过include $this->cacheFile;

到这儿就可以通过控制 Referer请求头,来控制模版的渲染,绕过 CheckDisabledFunctions()方法的过滤 造成远程命令执行

通过正则找到受影响且无需身份认证的文件,来进行命令执行

  1. /plus/flink.php?dopost=save
  2. /plus/users_products.php?oid=1337
  3. /plus/download.php?aid=1337
  4. /plus/showphoto.php?aid=1337
  5. /plus/users-do.php?fmdo=sendMail
  6. /plus/posttocar.php?id=1337
  7. /plus/vote.php?dopost=view
  8. /plus/carbuyaction.php?do=clickout
  9. /plus/recommend.php

0x03 漏洞复现

这里我发现没有禁止反引号,就直接拿反引号执行命令

1633766116159

0x04 参考链接

https://srcincite.io/blog/2021/09/30/chasing-a-dream-pwning-the-biggest-cms-in-china.html