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();
}
首先当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()方法的过滤 造成远程命令执行
通过正则找到受影响且无需身份认证的文件,来进行命令执行
/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
- …
0x03 漏洞复现
这里我发现没有禁止反引号,就直接拿反引号执行命令
0x04 参考链接
https://srcincite.io/blog/2021/09/30/chasing-a-dream-pwning-the-biggest-cms-in-china.html