0x01 前言
MetInfo采用PHP+MySQL架构,目前最新版本为6.1.3。2018年12月27日,Metinfo被爆出存在存储型跨站脚本漏洞,远程攻击者无需登录可插入恶意代码,管理员在后台管理时即可触发。该XSS漏洞引起的原因是变量覆盖,这种情况还是比较少见的,因此打算对这个漏洞进行分析并学习一下思路。
0x02 影响范围及利用条件
Metinfo 6.x (6.0.0-6.1.3)
无需登录,知晓后台登陆地址。
0x03 漏洞分析
我们以官方最新版6.1.3为例进行分析。漏洞点位于/admin/login/login_check.php 12行,$url_array数组中某个元素被赋值给$turefile,经过条件判断后赋值给$met_adminfile,随后经过authcode方法加密后再赋值给$truefile。最后执行update SQL操作,将之前加密后的字符串插入数据库。
再跟踪$url_array如何输入的,代码第7行包含了/admin/include/common.inc.php,跟进common.inc.php。在代码第10行发现变量$url_array被赋值,这里利用Xdebug打下断点,发现$url_array是以后台绝对路径分割而成的数组,而后台路径是安装时就确定的。
那漏洞是如何产生的呢?继续看commin.inc.php,在代码77-82行,这是一个典型的对输入参数名进行变量注册的循环,并且是在$url_array赋值之后,存在变量覆盖漏洞。这里继续打下断点,访问下面的path
/admin/login/login_check.php?url_array[]=inject here&url_array[]=a
可以看到$url_array的值已经被覆盖并且可任意控制。
再返回login_check.php,$url_array的值可被插入到$met_config表中,$met_config可由commin.inc.php的90-93行进行变量注册,这里$met_config被赋值为met_config。
接下来看XSS的触发点,位于/app/system/safe/admin/index.clss.php
的doindex方法中,此方法对应着后台的【安全-安全与效率】操作。管理员在进行后台操作时,Metinfo会提前将met_config表中的数据取出,并放到全局变量$_M中;我们可以跟踪下这个过程,在/app/system/safe/admin/index.clss.php
的行首调用了/app/system/include/class/load.class.php中的sys_class方法加载系统类文件,这里加载了/app/system/include/class/admin.class.php,index类继承了admin类,并在构造函数__construct
中执行了父类admin的构造函数。
继续跟进到/app/system/include/class/admin.class.php中,发现admin类继承了common类,同样在构造函数中执行了父类的构造函数。
跟进到/app/system/include/class/common.class.php,common类的构造函数执行了load_confg_global方法加载全站配置数据。
load_config_global方法在128行,调用load_config方法,执行SQL语句从met_config表查询出所有数据,并将其放在全局变量$_M[‘config’]中。
因此可知,我们之前插入的XSS代码被放在$_M['config']['met_adminfile']
中,此时还处于加密的状态;load_config_global方法中代码120行,调用了authcode方法解密了$_M['config']['met_adminfile']
。
此时返回/app/system/safe/admin/index.clss.php
,解密后的XSS payload进入$localurl_admin。
index.class.php中代码39行,包含了模板/app/system/safe/admin/templates/index.php,跟进到index.php,在66行未过滤直接输出了$localurl_admin,最终导致了XSS。
测试漏洞的POC为
http://127.0.0.1/MetInfo6_1_3/admin/login/login_check.php?url_array[]=<script>alert(1)</script>&url_array[]=a
管理员打开后台安全-安全与效率,直接触发
0x04 修复方案
- 2019年1月2日,官方在论坛中确认了该漏洞,并准备在下一个小版本中修复
- 暂时缓解措施,修改/admin/include/common.inc.php,在82行变量注册之后插入
$url_array=explode($DS,ROOTPATH_ADMIN);
防止$url_array变量被覆盖。