码迷,mamicode.com
首页 > 数据库 > 详细

20.Ecshop 2.x/3.x SQL注入/任意代码执行漏洞(附实战exp)

时间:2018-11-13 18:06:04      阅读:747      评论:0      收藏:0      [点我收藏+]

标签:ice   ash   erro   table   compile   漏洞   err   一个   login   

Ecshop 2.x/3.x SQL注入/任意代码执行漏洞

影响版本:

    Ecshop 2.x

    Ecshop 3.x-3.6.0

漏洞分析:

该漏洞影响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注入漏洞分析:

首先我们看一下漏洞的起源点 user.php ,在用户login这里有一段代码:

/* 用户登录界面 */
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);
}

Ecshop使用了php模版引擎smarty,该引擎有两个基本的函数assign()、display()。assign()函数用于在模版执行时为模版变量赋值,display()函数用于显示模版。

smarty运行时,会读取模版文件,将模版文件中的占位符替换成assign()函数传递过来的参数值,并输出一个编译处理后的php文件,交由服务器运行。

可以看到 $back_act 是从 $GLOBALS[‘_SERVER‘][‘HTTP_REFERER‘] 获取到的,HTTP_REFERER 是外部可控的参数,这也是我们注入payload的源头。

在模版执行时,assign()把 $back_act 赋值给模版变量 back_act ,下面我们跟进这个模版变量到 /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;
            }
        }
    }

从上面的assign()函数,我们看出 $tpl_var 接受我们传过来的参数 $back_act ,但不是个数组,所以  $this ->_var[$back_act] = $back_act ,而后调用display()函数来显示模板,在includes/init.php文件中创建了Smarty对象cls_template来处理模版文件,对应的文件是 includes/cla_template.php

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()函数,传递进来的$filename是user_passport.dwt,从函数来看,首先会调用 $this->fetch 来处理 user_passport.dwt 模板文件,fetch函数中会用 $this->make_compiled 来编译模板。user_passport.dwt其中一段如下:


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

make_compiled 会将模板中的变量解析,也就是在这个时候将上面assign中注册到的变量 $back_act 传递进去了,解析完变量之后返回到display()函数中。此时$out是解析变量后的html内容,后面的代码是判断 $this->_echash 是否在 $out 中,如果存在的话,使用 $this->_echash 来分割内容,得到$k然后交给insert_mod处理,我们来查找 _echash 的值:

技术分享图片

发现 _echash 是个默认的值,不是随机生成的,所以 $val 内容可随意控制。跟进$this->insert_mod:

  function insert_mod($name) // 处理动态内容
    {
        list($fun, $para) = explode(|, $name);
        $para = unserialize($para);
        $fun = insert_ . $fun;

        return $fun($para);
    }

$val传递进来,先用 | 分割,得到 $fun $para$para进行反序列操作,$fun insert_ 拼接,最后动态调用 $fun($para),函数名部分可控,参数完全可控。接下来就是寻找以 insert_ 开头的可利用的函数了,在 /includes/lib_insert.php 有一个 insert_ads() 函数,正好满足要求。看下 insert_ads() 函数:

function insert_ads($arr)
{
    static $static_res = NULL;

    $time = gmtime();
    if (!empty($arr[‘num‘]) && $arr[‘num‘] != 1)
    {
        $sql  = SELECT a.ad_id, a.position_id, a.media_type, a.ad_link, a.ad_code, a.ad_name, p.ad_width,  .
                    p.ad_height, p.position_style, RAND() AS rnd  .
                FROM  . $GLOBALS[ecs]->table(ad) .  AS a .
                LEFT JOIN  . $GLOBALS[ecs]->table(ad_position) .  AS p ON a.position_id = p.position_id  .
                "WHERE enabled = 1 AND start_time <= ‘" . $time . "‘ AND end_time >= ‘" . $time . "".
                    "AND a.position_id = ‘" . $arr[‘id‘] . "" .
                ORDER BY rnd LIMIT  . $arr[‘num‘];
        $res = $GLOBALS[db]->GetAll($sql);
    }
    else
    {
        if ($static_res[$arr[‘id‘]] === NULL)
        {
            $sql  = SELECT a.ad_id, a.position_id, a.media_type, a.ad_link, a.ad_code, a.ad_name, p.ad_width, .
                        p.ad_height, p.position_style, RAND() AS rnd  .
                    FROM  . $GLOBALS[ecs]->table(ad) .  AS a .
                    LEFT JOIN  . $GLOBALS[ecs]->table(ad_position) .  AS p ON a.position_id = p.position_id  .
                    "WHERE enabled = 1 AND a.position_id = ‘" . $arr[‘id‘] .
                        "‘ AND start_time <= ‘" . $time . "‘ AND end_time >= ‘" . $time . "" .
                    ORDER BY rnd LIMIT 1;
            $static_res[$arr[id]] = $GLOBALS[db]->GetAll($sql);
        }
        $res = $static_res[$arr[id]];
    }
    $ads = array();
    $position_style = ‘‘;

 

20.Ecshop 2.x/3.x SQL注入/任意代码执行漏洞(附实战exp)

标签:ice   ash   erro   table   compile   漏洞   err   一个   login   

原文地址:https://www.cnblogs.com/bmjoker/p/9953451.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!