最新消息:图 床

ECShop 0day 的墮落之路

COOL IAM 66浏览 0评论

Author: Badcode@知道創宇404實驗室
Date: 2018/09/04

背景

ECShop是一款B2C獨立網店系統,適合企業及個人快速構建個性化網上商店。系統是基於PHP語言及MYSQL數據庫構架開發的跨平台開源程序。2018年6月13日,知道創宇404積極防禦團隊通過知道創宇旗下雲防禦產品“創宇盾”防禦攔截並捕獲到一個針對某著名區塊鏈交易所網站的攻擊,通過分析,發現攻擊者利用的正式ECShop 2.x版本的0day漏洞攻擊。於2018年6月14日,提交到知道創宇Seebug漏洞平台並收錄

隨後於2018年8月31日,ID為“ringk3y”研究人員在其博客公開這個漏洞,並做了詳細分析,該分析收錄在Seebug Paper

知道創宇404積極防禦團隊於2018年9月2日正式對外發布《ECShop全系列版本的遠程代碼執行漏洞》預警。

從2018年的6月13日首次攔截后,知道創宇404實驗室多個團隊對這個利用ECShop 0day攻擊事件進行持續的監控分析,從下文的分析結果可以看出一個0day漏洞在實際攻擊中的各個階段的“墮落”過程。

漏洞分析

該漏洞影響ECShop 2.x和3.x版本,是一個典型的“二次漏洞”,通過user.php文件中display()函數的模板變量可控,從而造成SQL注入漏洞,而後又通過SQL注入漏洞將惡意代碼注入到危險函數eval中,從而實現了任意代碼執行。

值得一提的是攻擊者利用的payload只適用於ECShop 2.x版本導致有部分安全分析者認為該漏洞不影響ECShop 3.x,這個是因為在3.x的版本里有引入防注入攻擊的安全代碼,通過我們分析發現該防禦代碼完全可以繞過實現對ECShop 3.x的攻擊(詳見下文分析)。

註:以下代碼分析基於ECShop 2.7.3

SQL 注入漏洞

首先看到ecshop/user.php

elseif ($action == 'login')
{
    if (empty($back_act))
    {
        if (empty($back_act) && isset($GLOBALS['_SERVER']['HTTP_REFERER']))
        {
            $back_act = strpos($GLOBALS['_SERVER']['HTTP_REFERER'], 'user.php') ? './index.php' : $GLOBALS['_SERVER']['HTTP_REFERER'];
        }
        else
        {
            $back_act = 'user.php';
        }

    }


    $captcha = intval($_CFG['captcha']);
    if (($captcha & CAPTCHA_LOGIN) && (!($captcha & CAPTCHA_LOGIN_FAIL) || (($captcha & CAPTCHA_LOGIN_FAIL) && $_SESSION['login_fail'] > 2)) && gd_version() > 0)
    {
        $GLOBALS['smarty']->assign('enabled_captcha', 1);
        $GLOBALS['smarty']->assign('rand', mt_rand());
    }

    $smarty->assign('back_act', $back_act);
    $smarty->display('user_passport.dwt');
}

可以看到$back_act是從HTTP_REFERER獲取到的,HTTP_REFERER是外部可控的,這也是萬惡的根源。

接着將back_act變量傳遞給assign函數,跟進ecshop/includes/cls_template.php

   /**
     * 註冊變量
     *
     * @access  public
     * @param   mix      $tpl_var
     * @param   mix      $value
     *
     * @return  void
     */
    function assign($tpl_var, $value = '')
    {
        if (is_array($tpl_var))
        {
            foreach ($tpl_var AS $key => $val)
            {
                if ($key != '')
                {
                    $this->_var[$key] = $val;
                }
            }
        }
        else
        {
            if ($tpl_var != '')
            {
                $this->_var[$tpl_var] = $value;
            }
        }
    }

可以從註釋了解這個函數的功能,是註冊模板變量,也就是$back_act變成了$this->_var[$back_act]=$back_act,而後調用display函數

    function display($filename, $cache_id = '')
    {
        $this->_seterror++;
        error_reporting(E_ALL ^ E_NOTICE);

        $this->_checkfile = false;
        $out = $this->fetch($filename, $cache_id);

        if (strpos($out, $this->_echash) !== false)
        {
            $k = explode($this->_echash, $out);
            foreach ($k AS $key => $val)
            {
                if (($key % 2) == 1)
                {
                    $k[$key] = $this->insert_mod($val);
                }
            }
            $out = implode('', $k);
        }
        error_reporting($this->_errorlevel);
        $this->_seterror--;

        echo $out;
    }

user.php調用display函數,傳遞進來的$filenameuser_passport.dwt,從函數來看,首先會調用$this->fetch來處理user_passport.dwt模板文件,fetch函數中會調用$this->make_compiled來編譯模板。user_passport.dwt其中一段如下:

            <td>&nbsp;</td>
            <td align="left">
            <input type="hidden" name="act" value="act_login" />
            <input type="hidden" name="back_act" value="{$back_act}" />
            <input type="submit" name="submit" value="" class="us_Submit" />
            </td>

make_compiled會將模板中的變量解析,也就是在這個時候將上面assign中註冊到的變量$back_act傳遞進去了,解析完變量之後返回到display函數中。此時$out是解析變量后的html內容,判斷$this->_echash是否在$out中,若在,使用$this->_echash來分割內容,得到$k然後交給insert_mod處理。

转载请注明:IAMCOOL » ECShop 0day 的墮落之路

0 0 vote
Article Rating
Subscribe
Notify of
0 Comments
Inline Feedbacks
View all comments
0
Would love your thoughts, please comment.x
()
x