本文参考自TimeLine Sec团队成员microworld的复现记录。
http://www.74cms.com/news/show-2497.html
骑士 CMS 官方发布安全更新,修复了一处远程代码执行漏洞。由于骑士 CMS 某些函数存在过滤不严格,攻击者通过构造恶意请求,配合文件包含漏洞可在无需登录的情况下执行任意代码,控制服务器。
骑士 CMS < 6.0.48
从官网下载6.0.20版本源代码
http://www.74cms.com/download/index.html
新建数据库,用MAMP Pro搭建站点
发送如下请求:
1 | http://[IP]/index.php?m=home&a=assign_resume_tpl |
/data/Runtime/Logs/home/20_12_15.log
在正式分析漏洞之前,先看一看74CMS的路由和日志记录。
由于74CMS是基于ThinkPHP 3.2.3,其标准的URL路径为:
1 | http://[IP]/index.php/模块/控制器/操作 |
但是74CMS采用的是普通模式,使用传统的GET传参来指定当前访问的模块、控制器和方法,例如:
1 | http://localhost/?m=模块&c=控制器&a=方法&var=参数 |
当然这些参数也是可以自定义的,配置文件位于ThinkPHP/Conf/convention.php
其次,ThinkPHP定义了日志记录的方式,在ThinkPHP/Library/Think/Log.class.php
中的write方法:
ERR代表一般性错误,$destination
是日志的存储位置,日志文件名是有年_月_日
组成。
根据官方通告,漏洞代码位于Application/Common/Controller/BaseController.class.php
的assign_resume_tpl
方法,用于渲染简历模板:
继续跟进fetch函数,该函数位于Controllor.class.php
文件中:
函数内部又调用了ThinkPHP/Lirary/Think/View.class.php
类中的fetch
方法:
content
为空进入第一个if判断,模板文件不存在的话直接返回,下一个if语句判断TMPL_ENGINE_TYPE
是否是php
,我们可以全局搜索这个常量,在ThinkPHP/Conf/convention.php
中定义为Think
,也就是说使用Think模板,那么就进入到else语句中。
首先构造一个参数数组$params
,然后将调用静态方法Hook::listen
,继续跟进,位于ThinkPHP/Library/Hook.class.php
文件中:
此时tag=view_parse
,该方法会查找$tags
中有没有绑定view_parse
事件的方法,然后用foreach遍历$tags
属性,并执行Hook:exec
方法。
此方法会检查行为名称中是否存在Behavior
,若存在此关键字,行为扩展必须使用run入口方法,关于Hook的配置在/ThinkPHP/Mode/common.php
中
继续跟进到ThinkPHP/Behavior/ParseTemplateBehavior.class.php
,找到文件中的run方法:
已知74CMS采用的是Think模板引擎,当首次运行时不存在缓存文件,会进入到else语句中,新建一个Template类,在调用类中的fetch方法,位于ThinkPHP/Library/Think/Template.class.php
文件:
调用loadTemplate()
,将其存入templateCacheFile
中,我们跟入loadTemplate
:
首先读取templateFile的文件内容存到tmplContent中,然后再调用compiler函数编译模板内容,继续跟进:
传入的模板内容未经过过滤就直接被拼接到$tmplContent
变量,然后返回loadTemplate
方法,调用put
方法写入缓存文件,并返回缓存文件名,于是我们再回归到fetch()
方法,调用了Storage::load
,位于ThinkPHP/Library/Think/Storage/Driver/File.class.php
:
这里就直接包含文件,最终造成了模板注入。