ThinkCMF 任意文件包含/代码执行漏洞
source link: https://www.smi1e.top/thinkcmf-%e4%bb%bb%e6%84%8f%e6%96%87%e4%bb%b6%e5%8c%85%e5%90%ab-%e4%bb%a3%e7%a0%81%e6%89%a7%e8%a1%8c%e6%bc%8f%e6%b4%9e/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
从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
ThinkPHP框架约定可以通过 a
参数来指定对应的 action
,配置文件中的默认模块为 Portal
控制器为 Index
Portal/Controller/IndexController.class.php
由于没有名为 display
的 action
,因此会调用其父类 HomebaseController
的 display
方法
然后会调用 $this->parseTemplate
处理 $template
参数,当其是一个存在当文件时,直接返回,然后调用父类的 display
方法,也就是 ThinkPHP3
的 display
方法,接着会调用View
类的display方法
View
类的 fetch
方法。
可以明显的看到if条件中有危险操作,不过我们进不去,ThinkPHP3
中TMPL_ENGINE_TYPE
默认为 Think
。
ThinkCMFX/simplewind/Core/Library/Behavior/ParseTemplateBehavior.class.php
跟进ThinkPHP内置模板引擎类 Template
的 fetch
方法$this->loadTemplate
方法读取了 $templateFile
文件内容后,将其编译为tpl缓存文件,返回缓存文件名
最后调用load方法将其include出来。
而在ByteCTF opensns
那道题中
<?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
参数可控
keywords[_filename]=/flag
令 $this->tVar[_filename]=/flag
display()渲染时
$params = array('var'=>$this->tVar,'file'=>$templateFile,'content'=>$content,'prefix'=>$prefix);
然后继续跟进ThinkPHP内置模板引擎类 Template
的 fetch
方法
$this->tVar
数组也就是 this->tVar
传入了 Storage::load
接着变量覆盖掉了 $_filename
,从而include了出来。
http://127.0.0.1/index.php?a=display&templateFile=README.md&content=%3C?php%20phpinfo();die();
还是跟上面一样跟入 fetch
方法,
$this->loadTemplate
跟进编译模板文件内容的 $this->compiler
方法
可以看到直接进行了拼接,虽然过滤了 ?><?php
,在没有定义 THINK_PATH
的情况下退出程序,但是文件包含是通过 index.php
进行访问的,不会进入该if条件,从而可以执行代码,或者也可以 <?=phpinfo();exit();?>
绕过过滤。
由于漏洞的关键点在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
只不过无回显
原因其实就是 HomebaseController
类中的 fetch、display
方法是public的,可以被用户任意调用
protected
在thinkphp5.1中对此进行了修复
bytectf opensns那个题是由于用户可控assign的参数或者说是display的参数,在正常环境下基本不可能出现。
Referer
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK