DedeCMS命令执行复现&研究|CVE-2025-6335

admin 2026-01-04 22:20:43 网络安全文章 来源:ZONE.CI 全球网 0 阅读模式

文章总结: 本文分析DedeCMS5.7.2以下版本命令执行漏洞CVE-2025-6335。漏洞源于dedetag.class.php对参数过滤缺失,导致恶意标签名拼接到缓存文件执行代码。文章复现了RCE过程,提供POC脚本及流量分析。建议升级版本或实施标签名白名单机制以修复漏洞。 综合评分: 90 文章分类: 漏洞分析,WEB安全,漏洞POC,代码审计,渗透测试


cover_image

DedeCMS命令执行复现&研究 | CVE-2025-6335

原创

404号浪漫

404号浪漫

2025年9月27日 22:29 北京

0x0 背景介绍

在 DеdеCMS 5.7.2及更早版本中发现了一个漏洞,并被归类为严重。此问题影响文件 /inсludе/dеdеtаɡ.class.phр 中的某个未知处理过程,该组件为模板处理器。对参数 nоtеѕ 的操作导致命令注入。

漏洞详情

| 漏洞类型 | 影响版本 | 利用复杂度 | CVE编号 | | — | — | — | — | | 命令执行 | DedeCMS≤ 5.7.2 | 低(需权限) | CVE-2025-6335 |

攻击效果:

  • 执行任意命令,实现RCE。

#


0x1 环境搭建

1.搭建步骤(Win10+PHPsutudy+PHP5.6x)

  • 项目直接下载地址:
https://updatenew.dedecms.com/base-v57/package/DedeCMS-V5.7.118-UTF8.zip
  • 搭建手册:
https://help.dedecms.com/chujie/huanjing/windows.html
  • 安装:访问后直接默认安装就可以,无其他要求


0x2 漏洞复现

1)手动测试tag_test_action.php,向Temp文件夹写入文件

  • 写入成功

2)手动测试co_get_corule.php,弹出计算器 * 弹出计算器

#

3)python脚本

  • 粗略的做了一个目标是win系统的,只是简单的验证
https://github.com/Kai-One001/cve-/blob/main/dede_cms_RCE_CVE-2025-6335.py
  • 示例脚本是向目标C:\Windows\Temp写入,也可以写入到web服务器`中,脚本也是可以直接修改cmd命令比较方便一些

  • 题外,因为时间不赶趟了,所以写入目标服务器就没融入脚本

4)复现流量特征 (PACP)

  • co_get_corule.php接口

  • tag_test_action.php接口


0x3 漏洞原理分析

1、/dede/co_get_corule.php接口分析

PS:通过漏洞信息得知通过向 /dede/co_get_corule.php 接口发送特制参数,利用对 notes 参数的不当过滤,触发命令注入,实现远程命令执行。

1) 优先查看 /dede/co_get_corule.php文件

  • 文件位置:uploads\dede\co_get_corule.php
  • 关键代码段:
require(dirname(__FILE__)."/config.php");
$cuserLogin = new userLogin();if($cuserLogin->getUserID()==-1){    header("location:login.php?gotopage=".urlencode($dedeNowurl));    exit();}
  • 检查是否检查用户是否登录,未登录则跳转到登录页面
CheckPurview('co_AddNote');#使用config.php内容+检查用户使用有权限(CheckPurview() 函数)

2) 跟踪定位notes参数

  • co_get_corule.php发现多处使用,进行过滤发现大抵流程是 当 $job 不为空时,进入notes
  • 再往下面逻辑是base64解码去除下反斜杠,接着往下看
$notes=trim($notes);

3) 模板解析和标签提取

  • 关键代码:
$dtp = new DedeTagParse();$dtp->LoadString($notes);
  • 调用路径追踪:
LoadString($str)
 └──> LoadSource($str)
       └──> LoadTemplate($filename)
             └──> ParseTemplet()
                   └──> 提取 {dede:xxx} 标签 → 构建 CTags 数组
  • 将 $notes 作为 DedeCMS 模板字符串进行解析
  • 这里会用到dedetag.class.php因为LoadString()
  • 会调用提取所有 {dede:xxx}标签 后续LoadTemplate()读取该文件并触发 ParseTemplet()

4) 查看LoadString() 方法的实现

  • 文件位置:uploads/include/dedetag.class.php
function LoadString($str){    $this->LoadSource($str);}
  • 调用了LoadSource($str),查看具体方法
    function LoadSource($str)    {        /*        $this->SetDefault();        $this->SourceString = $str;        $this->IsCache = FALSE;        $this->ParseTemplet();        */        //优化模板字符串存取读取方式        $this->taghashfile = $filename = DEDEDATA.'/tplcache/'.md5($str).'.inc';        if( !is_file($filename) )        {            file_put_contents($filename, $str);        }        $this->LoadTemplate($filename);    }
  • 调用了 LoadTemplate($filename),再进去套娃
function LoadTemplate($filename){    $this->SetDefault();    if(!file_exists($filename))    {        $this->SourceString = " $filename Not Found! ";        $this->ParseTemplet(); //  直接调用 ParseTemplet    }    else    {        $fp = @fopen($filename, "r");        while($line = fgets($fp,1024))        {            $this->SourceString .= $line;        }        fclose($fp);        if($this->LoadCache($filename))        {            return '';        }        else        {            $this->ParseTemplet(); //  正常流程也调用 ParseTemplet        }    }}
  • 到这里就看到所以无论文件是否存在,只要没有缓存命中,最终都会调用 ParseTemplet()

5)那就查看ParseTemplet方法

  • 这个方法的作用是:

遍历 $this->SourceString 找出所有{dede:xxx}开头的标签 提取标签名、属性、内嵌文本(InnerText) 构建DedeTag对象并存入 $this->CTags数组

$FullTagStartWord =  $TagStartWord.$this->NameSpace.":";...$sPos = strpos($this->SourceString, $FullTagStartWord, $ss);
  • 然后提取tTagName,解析属性,设置 InnerText,最后加入CTags数组。

6)跟踪CTags函数

  • 是在 dedetag.class.php中,SaveCache() 方法将标签信息写入缓存文件(.inc)
&nbsp; &nbsp;&nbsp;function&nbsp;SaveCache()&nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;$fp&nbsp;=&nbsp;fopen($this->CacheFile.'.txt',"w");&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;fwrite($fp,$this->TempMkTime."\n");&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;fclose($fp);&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;$fp&nbsp;=&nbsp;fopen($this->CacheFile,"w");&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;flock($fp,3);&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;fwrite($fp,'<'.'?php'."\r\n");&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;$errmsg&nbsp;=&nbsp;'';&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;if(is_array($this->CTags))&nbsp; &nbsp; &nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;foreach($this->CTags&nbsp;as&nbsp;$tid=>$ctag)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;$arrayValue&nbsp;=&nbsp;'Array("'.$ctag->TagName.'",';&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;if&nbsp;(!$this->CheckDisabledFunctions($ctag->InnerText,&nbsp;$errmsg)) {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;fclose($fp);&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; @unlink($this->taghashfile);&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; @unlink($this->CacheFile);&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; @unlink($this->CacheFile.'.txt');&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;die($errmsg);&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;$arrayValue&nbsp;.=&nbsp;'"'.str_replace('$','\$',str_replace("\r","\\r",str_replace("\n","\\n",str_replace('"','\"',str_replace("\\","\\\\",$ctag->InnerText))))).'"';&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;$arrayValue&nbsp;.=&nbsp;",{$ctag->StartPos},{$ctag->EndPos});";&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;fwrite($fp,"\$z[$tid]={$arrayValue}\n");&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;if(is_array($ctag->CAttribute->Items))&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;fwrite($fp,"\$z[$tid][4]=array();\n");&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;foreach($ctag->CAttribute->Items&nbsp;as&nbsp;$k=>$v)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;$v&nbsp;=&nbsp;str_replace("\\","\\\\",$v);&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;$v&nbsp;=&nbsp;str_replace('"',"\\".'"',$v);&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;$v&nbsp;=&nbsp;str_replace('$','\$',$v);&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;$k&nbsp;=&nbsp;trim(str_replace("'","",$k));&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;if($k=="")&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;continue;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;if($k!='tagname')&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;fwrite($fp,"\$z[$tid][4]['$k']=\"$v\";\n");&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;fwrite($fp,"\n".'?'.'>');&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;fclose($fp);&nbsp; &nbsp; }
  • $ctag->TagName

    是从用户输入中提取的标签名(如 {dede:xxx} 中的 xxx)

  • 直接拼接到PHP代码中,未进行转义或过滤

  • 直接拼接到 fwrite输出的 PHP 代码中

  • 构造恶意“标签名”即可在缓存inc中打断字符串并插入任意 PHP,例如:

{dede:ewoji");system('calc');///}&nbsp;→ 下次读缓存时被包含执行
  • 缓存文件的生成位置也就是 /data/tplcache/md5($notes).inc同一个恶意Payload每次都会生成相同的缓存文件名
  • 攻击载荷示例:
{dede:ewoji");system('calc');//}
  • ParseTemplet()

    解析出标签名:ewoji");system('calc');//

  • 在 SaveCache()中生成缓存文件 .inc 内容如下:

<?php
$z[0]=Array("ewoji");system('calc');//", "...", ...);
?>
  • 当该缓存文件被后续 include 或 require 加载时,system('calc') 将被执行

2、/dede/tag_test_action.php接口分析

1) 同上接口,可控参数导致直接拼接注入

<?phprequire_once(dirname(__FILE__)."/config.php");CheckPurview('temp_Test'); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;require_once(DEDEINC."/arc.partview.class.php");csrf_check();
  • co_get_corule.php接口相同,对权限进行检查,不同的是会有csrf-token检查
  • 在测试时,我根据参考进行直接POST,果然会提示DedeCMS:CSRF Token Check Failed!
  • 于是找到web页面进行抓包,看到有一个token,可以正常使用(这个token就是在当前接口的HTML中)

2) 转义绕过

$partcode=stripslashes($partcode);// 去除斜杠
  • 作用:去除GPC自动添加的反斜杠(如 ' → \'),但是即使输入中包含引号、括号等特殊字符,也能被还原

3) SetTemplet() 将用户输入的 $partcode 作为“模板字符串”传入

$pv->SetTemplet($partcode,&nbsp;"string");// 显示源码(可选)if(&nbsp;$showsource&nbsp;==&nbsp;""&nbsp;||&nbsp;$showsource&nbsp;==&nbsp;"yes"&nbsp;) {&nbsp; &nbsp;&nbsp;echo&nbsp;"模板代码:";&nbsp; &nbsp;&nbsp;echo&nbsp;"<span style='color:red;'><pre>".dede_htmlspecialchars($partcode)."</pre></span>";&nbsp; &nbsp;&nbsp;echo&nbsp;"结果:<hr size='1' width='100%'>";}// 执行显示$pv->Display();
  • 当标签闭合不当或拼接到PHP 代码中时,可能触发代码注入
调用追踪如下
$pv->Display()
&nbsp;└──&nbsp;$this->MakeHtml()
&nbsp; &nbsp; &nbsp; └──&nbsp;$this->dtp->ParseTemplate($this->SourceString)
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;└── 调用 DedeTagParse 类解析标签
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; └── 对&nbsp;{dede:...}&nbsp;进行&nbsp;eval&nbsp;或 create_function 执行
  • DedeCMS&nbsp;在处理&nbsp;{dede:xxx}&nbsp;标签时,会将其转换&nbsp;PHP代码片段。例如:
传入:{dede:test /}
被转换为:
<?php&nbsp;echo&nbsp;GetTagData('test');&nbsp;?>
如果标签内容异常,如:
{dede:test"});&nbsp;system('calc');&nbsp;//}
在拼接进 PHP 缓冲区时,可能导致语法闭合 + 代码注入

#


0x4 修复建议

修复方案:

1、升级到最新版本?:目前厂商说已发布升级补丁以修复漏洞,但是我下载也是这个版本的V5.7.118

![](https://mmbiz.qpic.cn/mmbiz_png/wEkiap6hicP8x4t0iaXUicYFsaVIiciammUBquJ0Rm2UZrpCsJpFPib0VtNPHbowjq19LMUcppRuEib8icRzIrPI2hBXAwA/640?wx_fmt=png&from=appmsg&watermark=1#imgIndex=10)

![](https://mmbiz.qpic.cn/mmbiz_png/wEkiap6hicP8x4t0iaXUicYFsaVIiciammUBquwfeh6ep15RQ8QZxF7HusjMbP7QRwfwR2OAeJNyCmzJWI7zD3t42Y4g/640?wx_fmt=png&from=appmsg&watermark=1#imgIndex=11)

2、临时缓解措施:

  • 禁用危险字符拼接 :SaveCache()方法,对 $ctag->TagName进行转义;
  • 白名单机制:只允许预定义的合法标签名(如field, list, arc 等),其余一律拒绝;
  • 输入过滤:增加对notes内容的合法性校验;
  • 强化口令:账户最小化原则。

免责声明:

本文仅用于安全研究目的,请勿用于非法渗透测试活动。实际操作需获得目标系统所有者明确授权。因使用本文提供的方法造成的任何法律后果,由使用者自行承担。

/**好忙呀,最近。得亏是领导安排工作可以稍微晚点发文**/


免责声明:

本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。

任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。

本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我

本文转载自:404号浪漫 404号浪漫《DedeCMS命令执行复现&研究 | CVE-2025-6335》

阿里云AI安全全球挑战赛 网络安全文章

阿里云AI安全全球挑战赛

文章总结: 作者分享了首次参加阿里云AI安全全球挑战赛的经历,在赛道一未进复赛但在赛道三获得第四名。文中重点提及了对提示词注入和越狱技术的体验,认为其具有趣味性
评论:0   参与:  0