压在透明的玻璃上c-国产精品国产一级A片精品免费-国产精品视频网-成人黄网站18秘 免费看|www.tcsft.com

zzzphpV1.6.1 遠(yuǎn)程代碼執(zhí)行漏洞簡單分析

0x1 前言

?在先知偶然看到了一篇文章zzzphp V1.6.1 遠(yuǎn)程代碼執(zhí)行漏洞分析,關(guān)于模版getshell其實(shí)很普遍,這種漏洞分析的樂趣在于跟蹤惡意代碼的全過程,很可惜先知上的作者可能對(duì)這方面不是很感興趣,直接丟出了payload,正好自己最近很想看下一些cms具體是如何解析模版的,比如之前那個(gè)seacms,很值得我去學(xué)習(xí)。

0x2 漏洞利用回溯分析

漏洞URL:http://127.0.0.1:8888/zzzphp/search

入口是:index.php->zzzclient.php

/Users/xq17/www/zzzphp/inc/zzz_client.php

 require 'zzz_template.php';
 if (conf('webmode')==0) error(conf('closeinfo'));
 $location=getlocation();
 ParseGlobal(G('sid'),G('cid'));
 //echop($location);die;
 switch ($location) { //$location=search
    case 'about':
         $tplfile= TPL_DIR . G('stpl');
        break; 
    case 'brand':        
        $stpl=splits(db_select('brand','b_template',"bid=".G('bid') or "b_name='".G('bname')."'"),',');
        if (defined('ISWAP')){
          $tplfile=isset($stpl[1]) ? $stpl[1] : $stpl[0];
        }else{
          $tplfile=$stpl[0];    
        }
         $tplfile=empty($tplfile) ? TPL_DIR .'brand.html' : TPL_DIR . $tplfile ;
        break;    
    case 'brandlist':
        $tplfile=isset($stpl) ? TPL_DIR .  $stpl: TPL_DIR . 'brandlist.html'; 
        $GLOBALS['tid']='-1';
        break;
    case 'content':        
         $tplfile= TPL_DIR . G('ctpl');
        break;    
    case 'list':
         $tplfile= TPL_DIR . G('stpl');
        break;
    case 'taglist':
        $tplfile=TPL_DIR . 'taglist.html'; 
        $GLOBALS['tid']='-1';
        break;
    case 'user':
         $tplfile= TPL_DIR . 'user.html'; 
        break;
    case 'search':
         $tplfile= TPL_DIR . 'search.html'; //從這步開始
        break;

先記著$tplfile=/Users/xq17/www/zzzphp/template/pc/cn2016/html/search.html?$location=search

然后繼續(xù)跟蹤$tplfile?繼續(xù)讀下去

就會(huì)發(fā)現(xiàn)在137~140 行進(jìn)行了解析模版操作

    $zcontent = load_file($tplfile,$location);    
    $parser = new ParserTemplate(); //2l行  require 'zzz_template.php';
    $zcontent = $parser->parserCommom($zcontent); // 解析模板
    echo $zcontent;

跟進(jìn)load_file函數(shù)了解下作用

zzz_file.php

function load_file( $path, $location = NULL ) {
    $path = str_replace( '//', '/', $path );//規(guī)范路徑
    if ( is_file( $path ) ) { //判斷是不是文件 
        return file_get_contents( $path ); //直接返回內(nèi)容
    } elseif ( !is_null( $location ) ) { 
        $locationpath = PLUG_DIR . 'template/' . $location . '.tpl';
        if ( is_file( $locationpath ) ) {
            return file_get_contents( $locationpath );
        } else {
            $url = $_SERVER[ 'REQUEST_URI' ];
            $url = sub_left( $url, '?' );
            phpgo( $url );
            return false;
        }
    } elseif ( is_file( SITE_DIR . $path ) ) {
        return file_get_contents( SITE_DIR . $path );
    } else {
        error( "載入文件失敗,請檢查文件路徑!," . str_replace( DOC_PATH, '', $path ) );
        return false;
    }
}

所以函數(shù)作用是: 獲取文件或者模版的內(nèi)容

這個(gè)時(shí)候$zcontent的內(nèi)容就是/Users/xq17/www/zzzphp/template/pc/cn2016/html/search.html

我們可以看下:

<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>關(guān)鍵詞【{zzz:keys}】搜索結(jié)果-{zzz:sitetitle}</title>
<meta name="Keywords" content="{zzz:pagekeys}" >
<meta name="Description" content="{zzz:pagedesc}">
<meta name="author" content="http://www.zzzcms.com" />   
<script src="{zzz:tempath}js/jquery-1.8.3.min.js" type="text/javascript"></script>
<link rel="stylesheet" type="text/css" href="{zzz:tempath}css/styles.css" />
<script src="{zzz:tempath}js/img.js" type="text/javascript"></script>
</head>

<body>
<!--head--> {zzz:top}
<div class="s_banner2"></div>
<div class="path_box">
  <div class="path_con">
    <div class="pc_title"><img src="{zzz:tempath}images/2_08.png" /><span>站內(nèi)搜索</span><i>search</i></div>
    <div class="sub_title">關(guān)鍵詞:{zzz:pagekeys}</div>
    <div class="pc_text"> 位置{zzz:location}</div>
    <div class="clear"></div>
  </div>
</div>
<div class="contact_box">
  <div class="contact_inf">
    <div class="sub_list">
      <dl>
        {zzz:navlist sid=5,6}
        <dd {if:[navlist:sid]= {zzz:sid}}class="sub_on"{end if}> <a href="[navlist:link]">[navlist:name]</a></dd>
        {/zzz:navlist}
      </dl>
    </div>
    <div class="sub_right">
      <div class="news">
        <div class="news_list"> {zzz:search size=5 order=order}
          <dl>
            <dt>[search:date]</dt>
            <dd><a href="[search:link]" title="[search:title]">[search:title]</a></dd>
            <div class="clear"></div>
          </dl>
          {/zzz:search} </div>
        {list:page len=3 style=1} </div>
    </div>
    <div class="clear"></div>
  </div>
</div>

<!--foot--> {zzz:foot}
</body>
</html>

里面包含了cms定義的各種標(biāo)簽。

接下來就是我想重點(diǎn)分析的部分了

0x3 解析模版過程

? 從上面然后執(zhí)行到了這一句:$zcontent = $parser->parserCommom($zcontent);

跟進(jìn)代碼:

/Users/xq17/www/zzzphp/inc/zzz_template.php

    function parserCommom( $zcontent ) {
        $zcontent = $this->parserSiteLabel( $zcontent ); // 站點(diǎn)標(biāo)簽
        $zcontent = $this->ParseInTemplate( $zcontent ); // 模板標(biāo)簽
        $zcontent = $this->parserConfigLabel( $zcontent ); //配置表情
        $zcontent = $this->parserSiteLabel( $zcontent ); // 站點(diǎn)標(biāo)簽
        $zcontent = $this->parserCompanyLabel( $zcontent ); // 公司標(biāo)簽
        $zcontent = $this->parserlocation( $zcontent ); // 站點(diǎn)標(biāo)簽
        $zcontent = $this->parserLoopLabel( $zcontent ); // 循環(huán)標(biāo)簽
        $zcontent = $this->parserContentLoop( $zcontent ); // 指定內(nèi)容
        $zcontent = $this->parserbrandloop( $zcontent );
        $zcontent = $this->parserGbookList( $zcontent );
        $zcontent = $this->parserUser( $zcontent ); //會(huì)員信息
        $zcontent = $this->parserLabel( $zcontent ); // 指定內(nèi)容
        $zcontent = $this->parserPicsLoop( $zcontent ); // 內(nèi)容多圖
        $zcontent = $this->parserad( $zcontent );
        $zcontent = parserPlugLoop( $zcontent );
        $zcontent = $this->parserOtherLabel( $zcontent );
        $zcontent = $this->parserIfLabel( $zcontent ); // IF語句
        $zcontent = $this->parserNoLabel( $zcontent );
        return $zcontent;
    }

可以很清楚看到寫了不同的函數(shù)去解析對(duì)應(yīng)的標(biāo)簽,這里我們不妨直接跟進(jìn)第一個(gè)來了解代碼流程。

$zcontent = $this->parserSiteLabel( $zcontent );

    function parserSiteLabel( $zcontent ) {
        $pattern = '/{zzz:([w]+)?}/'; 
        if ( preg_match_all( $pattern, $zcontent, $matches ) ) {
            $count = count( $matches[ 0 ] );//看下有多少個(gè)成功匹配的 二維數(shù)組的總長度
            for ( $i = 0; $i < $count; $i++ ) {
                switch ( $matches[ 1 ][ $i ] ) { //結(jié)果
                    case 'qqkf1':
                        $zcontent = str_replace( $matches[ 0 ][ $i ], load_file( SITE_DIR . "plugins/qqkf/qqkf1.html" ), $zcontent );
                        break;
                    case 'qqkf2':
                        $zcontent = str_replace( $matches[ 0 ][ $i ], load_file( SITE_DIR . "plugins/qqkf/qqkf2.html" ), $zcontent );
                        break;
                    case 'qqkf3':
                        $zcontent = str_replace( $matches[ 0 ][ $i ], load_file( SITE_DIR . "plugins/qqkf/qqkf3.html" ), $zcontent );
                        break;
                    case 'wapkf':
                        $zcontent = str_replace( $matches[ 0 ][ $i ], load_file( SITE_DIR . "plugins/qqkf/wapkf.html" ), $zcontent );
                        break;
                    case 'baidumap':
                        $zcontent = str_replace( $matches[ 0 ][ $i ], load_file( SITE_DIR . 'plugins/baidumap.html' ), $zcontent );
                        break;
                    case 'sitepath':
                        $zcontent = str_replace( $matches[ 0 ][ $i ], SITE_PATH, $zcontent );
                        break;
                    case 'wappath':
                        $zcontent = str_replace( $matches[ 0 ][ $i ], SITE_PATH.'wap/', $zcontent );
                        break;    
                    case 'plugpath':
                        $zcontent = str_replace( $matches[ 0 ][ $i ], SITE_PATH . 'plugins/', $zcontent );
                        break;
                    case 'version':
                        $zcontent = str_replace( $matches[ 0 ][ $i ], VERSION, $zcontent );
                        break;
                    case 'tempath':
                        $zcontent = str_replace( $matches[ 0 ][ $i ], TPL_PATH, $zcontent );
                        break;
                    case 'nowtime':
                    case 'time':
                        $zcontent = str_replace( $matches[ 0 ][ $i ], date( 'Y-m-d H:i:s' ), $zcontent );
                        break;
                    case 'Y': case 'm': case 'd': case 'H': case 'i': case 's':
                        $zcontent = str_replace( $matches[ 0 ][ $i ], date( ''.$matches[ 1 ][ $i ].'' ), $zcontent );
                        break;    
                    case 'sitename':
                        $zcontent = str_replace( $matches[ 0 ][ $i ], '{zzz:sitetitle}', $zcontent );
                        break;
                    case 'sitetitle2':
                        $zcontent = str_replace( $matches[ 0 ][ $i ], '{zzz:additiontitle}', $zcontent );
                        break;
                    case 'logo':
                    case 'pclogo':
                        $zcontent = str_replace( $matches[ 0 ][ $i ], '{zzz:sitepclogo}', $zcontent );
                        break;
                    case 'waplogo':
                        $zcontent = str_replace( $matches[ 0 ][ $i ], '{zzz:sitewaplogo}', $zcontent );
                        break;
                    case 'company':
                        $zcontent = str_replace( $matches[ 0 ][ $i ], '{zzz:companyname}', $zcontent );
                        break;
                    case 'address':
                        $zcontent = str_replace( $matches[ 0 ][ $i ], '{zzz:companyaddress}', $zcontent );
                        break;
                    case 'postcode':
                        $zcontent = str_replace( $matches[ 0 ][ $i ], '{zzz:companypostcode}', $zcontent );
                        break;
                    case 'contact':
                        $zcontent = str_replace( $matches[ 0 ][ $i ], '{zzz:companycontact}', $zcontent );
                        break;
                    case 'tel':
                        $zcontent = str_replace( $matches[ 0 ][ $i ], '{zzz:companytel}', $zcontent );
                        break;
                    case 'mobile':
                        $zcontent = str_replace( $matches[ 0 ][ $i ], '{zzz:companymobile}', $zcontent );
                        break;
                    case 'fax':
                        $zcontent = str_replace( $matches[ 0 ][ $i ], '{zzz:companyfax}', $zcontent );
                        break;
                    case 'siteicp':
                        $zcontent = str_replace( $matches[ 0 ][ $i ], '{zzz:companyicp}', $zcontent );
                        break;
                    case 'desc':
                    case 'sitedesc':
                    case 'pagedesc':
                        $zcontent = str_replace( $matches[ 0 ][ $i ], '{zzz:sitedesc}', $zcontent );
                        break;
                    case 'top':
                    case 'head':
                        $zcontent = str_replace( $matches[ 0 ][ $i ], '{zzz:template src=head.html}', $zcontent );
                        break;
                    case 'foot':
                    case 'end':
                        $zcontent = str_replace( $matches[ 0 ][ $i ], '{zzz:template src=foot.html}', $zcontent );
                        break;
                    case 'left':
                        $zcontent = str_replace( $matches[ 0 ][ $i ], '{zzz:template src=left.html}', $zcontent );
                        break;
                    case 'right':
                        $zcontent = str_replace( $matches[ 0 ][ $i ], '{zzz:template src=right.html}', $zcontent );
                        break;
                    case 'userlogin':
                        $zcontent = str_replace( $matches[ 0 ][ $i ], "<script language='javascript' src='" . PLUG_PATH . "template/login.php?backurl=".G( 'backurl' )."''></script>", $zcontent );
                        break;
                    case 'gbookform':
                        $zcontent = str_replace( $matches[ 0 ][ $i ], "<iframe width='100%' height='100%' frameborder='0'  style='min-height:500px;' src='" . PLUG_PATH . "template/gbook.php'></iframe>", $zcontent );
                        break;
                }
            }
        }
        return $zcontent;
    }

$pattern = '/{zzz:([w]+)?}/';?這個(gè)正則其實(shí)就是匹配{zzz:(匹配內(nèi)容)}這樣的格式

image-20190324163835399

這里就從{zzz:keys}?為例子,沒有,直接返回了內(nèi)容(這里是解析站點(diǎn)標(biāo)簽的,可能在其他函數(shù)會(huì)處理keys)

這里比如

                    case 'version':
                        $zcontent = str_replace( $matches[ 0 ][ $i ], VERSION, $zcontent ); //進(jìn)行了個(gè)替換操作
                        break;

0x4 重點(diǎn)分析漏洞形成點(diǎn)

? 在眾多解析標(biāo)簽的函數(shù)中,$zcontent = $this->parserIfLabel( $zcontent ); // IF語句

我們選擇跟進(jìn)解析if語句的函數(shù)。

    function parserIfLabel( $zcontent ) {
        $pattern = '/{if:([sS]+?)}([sS]*?){ends+if}/';
        if ( preg_match_all( $pattern, $zcontent, $matches ) ) {
            $count = count( $matches[ 0 ] );
            for ( $i = 0; $i < $count; $i++ ) {
                $flag = '';
                $out_html = '';
                $ifstr = $matches[ 1 ][ $i ];
                $ifstr = str_replace( '<>', '!=', $ifstr );
                $ifstr = str_replace( 'mod', '%', $ifstr );
                $ifstr1 = cleft( $ifstr, 0, 1 );
                switch ( $ifstr1 ) {
                    case '=':
                        $ifstr = '0' . $ifstr;
                        break;
                    case '{':
                    case '[':
                        $ifstr = "'" . str_replace( "=", "'=", $ifstr );
                        break;
                }
                $ifstr = str_replace( '=', '==', $ifstr );
                $ifstr = str_replace( '===', '==', $ifstr );
                @eval( 'if(' . $ifstr . '){$flag="if";}else{$flag="else";}' );
                if ( preg_match( '/([sS]*)?{else}([sS]*)?/', $matches[ 2 ][ $i ], $matches2 ) ) { // 判斷是否存在else                
                    switch ( $flag ) {
                        case 'if': // 條件為真
                            if ( isset( $matches2[ 1 ] ) ) {
                                $out_html .= $matches2[ 1 ];
                            }
                            break;
                        case 'else': // 條件為假
                            if ( isset( $matches2[ 2 ] ) ) {
                                $out_html .= $matches2[ 2 ];

                            }
                            break;
                    }
                } elseif ( $flag == 'if' ) {
                    $out_html .= $matches[ 2 ][ $i ];
                }

                // 無限極嵌套解析
                $pattern2 = '/{if([0-9]):/';
                if ( preg_match( $pattern2, $out_html, $matches3 ) ) {
                    $out_html = str_replace( '{if' . $matches3[ 1 ], '{if', $out_html );
                    $out_html = str_replace( '{else' . $matches3[ 1 ] . '}', '{else}', $out_html );
                    $out_html = str_replace( '{end if' . $matches3[ 1 ] . '}', '{end if}', $out_html );
                    $out_html = $this->parserIfLabel( $out_html );
                }

                // 執(zhí)行替換
                $zcontent = str_replace( $matches[ 0 ][ $i ], $out_html, $zcontent );
            }
        }
        return $zcontent;
    }

里面有句直接eval了變量,那么我們從頭開始分析,eval里面的內(nèi)容是否可控。

$pattern = '/{if:([sS]+?)}([sS]*?){ends+if}/';

分析下這個(gè)正則是怎么匹配的

(括號(hào)代表的是匹配的組)

sS?是任意匹配內(nèi)容(比.還要強(qiáng)可以匹配換行等等)

ends+if?代表 end 和 if之間至少要有個(gè)空白符(空白 換行等)

也就是說這個(gè)正則匹配的格式:

{if:(匹配內(nèi)容)}(匹配內(nèi)容){end if}

繼續(xù)分析下他的代碼。

提取重要部分出來如下:

                $flag = '';
                $out_html = '';
                $ifstr = $matches[ 1 ][ $i ]; //匹配的內(nèi)容 {if:phpinfo()};{end if}->$ifstr=phpinfo()
                $ifstr = str_replace( '<>', '!=', $ifstr );//<>替換為!=
                $ifstr = str_replace( 'mod', '%', $ifstr ); //mod 替換為%
                $ifstr1 = cleft( $ifstr, 0, 1 );//提取第一個(gè)值 p
    //function cleft( $str, $start = 0, $num = 1 ) {
    //$var = trim( $str );
    //$result = substr( $var, $start, $num );
    //return $result;
                switch ( $ifstr1 ) { //這個(gè)選擇結(jié)構(gòu)可以直接跳過了
                    case '=':
                        $ifstr = '0' . $ifstr;
                        break;
                    case '{':
                    case '[':
                        $ifstr = "'" . str_replace( "=", "'=", $ifstr );
                        break;
                }
                $ifstr = str_replace( '=', '==', $ifstr );  //= 替換為==
                $ifstr = str_replace( '===', '==', $ifstr ); // ===替換為==
                @eval( 'if(' . $ifstr . '){$flag="if";}else{$flag="else";}' );
    //最后拼接結(jié)果就是 if(phpinfo()){$flag="if";}else{$flag="else";}

image-20190324170353464

0x5 關(guān)于漏洞利用

? 先知那片文章的payload已經(jīng)給的很詳細(xì)了,根據(jù)我的分析你也能很簡單寫出來了。

image-20190324170547107

直接加入個(gè)if格式的標(biāo)簽就可以直接rce了。

0x6 總結(jié)

? 雖然這個(gè)漏洞簡單,但是換做我以前的話估計(jì)就是粗略掃一下,大概明白漏洞思路,而不會(huì)去仔細(xì)分析,從而丟失了學(xué)習(xí)機(jī)會(huì),但是現(xiàn)在我覺得自己對(duì)待學(xué)習(xí)代碼審計(jì)的過程中,認(rèn)真嚴(yán)謹(jǐn)?shù)膶W(xué)習(xí)模式才能讓自己有所提高。

原文鏈接:https://www.anquanke.com/post/id/173991

上一篇:IDC:2022年全球安全支出增至1,338億美元

下一篇:ShaHmer行動(dòng): 疑似華X電腦公司遭受有針對(duì)性的供應(yīng)鏈攻擊