ThinkCMF 任意文件包含/代码执行漏洞
2019-12-17 16:52:24

前言

ByteCTF的opensns 到前段时间斗象科技发的ThinkCMF 框架上的任意内容包含漏洞 再到Li4n0师傅发的TinkcmfX 前台任意代码执行分析 都是由于thinkphp模版设计缺陷再加上开发者不恰到的使用导致的。因此在这里拿ThinkCMF做为例子简单分析学习一下。 影响版本: ThinkCMF X1.6.0 ThinkCMF X2.1.0 ThinkCMF X2.2.0 ThinkCMF X2.2.1 ThinkCMF X2.2.2

任意文件包含

http://127.0.0.1/?a=display&templateFile=README.md  image.png ThinkPHP框架约定可以通过 a 参数来指定对应的 action ,配置文件中的默认模块为 Portal 控制器为 Index  image.png 因此可知调用的控制器为 Portal/Controller/IndexController.class.php  image.png 由于没有名为 display 的 action ,因此会调用其父类 HomebaseController 的 display 方法 image.png 然后会调用 $this->parseTemplate 处理 $template 参数,当其是一个存在当文件时,直接返回,然后调用父类的 display 方法,也就是 ThinkPHP3 的 display 方法,接着会调用View类的display方法 image.png 最终 View 类的 fetch 方法。 image.png 可以明显的看到if条件中有危险操作,不过我们进不去,ThinkPHP3 中TMPL_ENGINE_TYPE 默认为 Think 。 image.png不过else条件中有一个钩子操作  image.png ThinkCMFX/simplewind/Core/Library/Behavior/ParseTemplateBehavior.class.php image.png 跟进ThinkPHP内置模板引擎类 Template 的 fetch 方法 image.png$this->loadTemplate 方法读取了 $templateFile 文件内容后,将其编译为tpl缓存文件,返回缓存文件名 image.png 最后调用load方法将其include出来。 image.png 而在ByteCTF opensns 那道题中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?php
public function search()
{
$keywords=I('post.keywords','','text');

$modules = D('Common/Module')->getAll();
foreach ($modules as $m) {
if ($m['is_setup'] == 1 && $m['entry'] != '') {
if (file_exists(APP_PATH . $m['name'] . '/Widget/SearchWidget.class.php')) {
$mod[] = $m['name'];
}
}
}
$show_search = get_kanban_config('SEARCH', 'enable', $mod, 'Home');

$this->assign($keywords);
$this->assign('showBlocks', $show_search);
$this->display();
}

assign 方法的 $keywords 参数可控 image.png 通过传入数组 keywords[_filename]=/flag 令 $this->tVar[_filename]=/flag  display()渲染时 $params = array('var'=>$this->tVar,'file'=>$templateFile,'content'=>$content,'prefix'=>$prefix);  然后继续跟进ThinkPHP内置模板引擎类 Templatefetch 方法 image.png 上面的$this->tVar数组也就是 this->tVar 传入了 Storage::load  image.png 接着变量覆盖掉了 $_filename ,从而include了出来。 image.png

代码执行

http://127.0.0.1/index.php?a=display&templateFile=README.md&content=%3C?php%20phpinfo();die(); image.png 还是跟上面一样跟入 fetch 方法, image.png 继续跟进 $this->loadTemplate  image.png 跟进编译模板文件内容的 $this->compiler 方法 image.png 可以看到直接进行了拼接,虽然过滤了 ?><?php ,在没有定义 THINK_PATH 的情况下退出程序,但是文件包含是通过 index.php 进行访问的,不会进入该if条件,从而可以执行代码,或者也可以 <?=phpinfo();exit();?> 绕过过滤。 image.png 由于漏洞的关键点在thinkphp View 类的 fetch 方法,我们也可以直接调用 fetch 操作 http://127.0.0.1/?a=fetch&content=%3C?=phpinfo();exit();  同样的也可以 http://127.0.0.1/?a=fetch&templateFile=README.md 只不过无回显 image.png

修复

原因其实就是 HomebaseController 类中的 fetch、display 方法是public的,可以被用户任意调用 image.png 而thinkphp中为 protected  image.png 在thinkphp5.1中对此进行了修复 bytectf opensns那个题是由于用户可控assign的参数或者说是display的参数,在正常环境下基本不可能出现。

Referer

TinkcmfX 前台任意代码执行分析 ThinkCMF 框架上的任意内容包含漏洞 ByteCTF opensns