前言:最近爆出来的漏洞,ThinkCmfX版本应该是通杀的,基于3.X Thinkphp开发的

代码下载地址:https://gitee.com/thinkcmf/ThinkCMFX/releases

我们就拿这个payload来进行分析

http://127.0.0.1/index.php?a=fetch&content=<?php system(‘ping xxxxxx’);?>

我们要知道tp框架的特性,可以通过这种形式的路由方式进行访问相应的功能点,例如通过g\m\a参数指定分组\控制器\方法

我们可以跟进父类的fetch的方法中查看

发现还调用了父类的fetch方法return parent::fetch($templateFile,$content,$prefix);,那我们可以继续来到AppframeController,但是发现里面没有fetch那么就肯定是继承来自Controller的控制器了,继续跟进

发现调用了view类的fetch方法继续更,这里的view类是Controller构造函数中$this->view = Think::instance('Think\View');

view类中的fetch方法如下:

    public function fetch($templateFile='',$content='',$prefix='') {
if(empty($content)) { //首先判断content有无内容
$templateFile = $this->parseTemplate($templateFile);
// 模板文件不存在直接返回
if(!is_file($templateFile)) E(L('_TEMPLATE_NOT_EXIST_').':'.$templateFile);
}else{
defined('THEME_PATH') or define('THEME_PATH', $this->getThemePath());
}
// 页面缓存
ob_start();
ob_implicit_flush(0);
if('php' == strtolower(C('TMPL_ENGINE_TYPE'))) { // 使用PHP原生模板
$_content = $content;
// 模板阵列变量分解成为独立变量
extract($this->tVar, EXTR_OVERWRITE);
// 直接载入PHP模板
empty($_content)?include $templateFile:eval('?>'.$_content);
}else{
// 视图解析标签
// 走的是这里
$params = array('var'=>$this->tVar,'file'=>$templateFile,'content'=>$content,'prefix'=>$prefix); // 生成一个数组赋值给$params
Hook::listen('view_parse',$params); //这个是关键
}
// 获取并清空缓存
$content = ob_get_clean();
// 内容过滤标签
Hook::listen('view_filter',$content);
// 输出模板文件
return $content; //这里进行模板文件的输出
}

可以看注释,我们的content肯定是有内容的,那么进行的就是第二个if判断,经过调式走的是第二个判断

来到这里Hook::listen('view_parse',$params); //这个是关键,第一个参数传的是view_parse,我们先看下listen这个函数的说明

    /**
* 监听标签的插件
* @param string $tag 标签名称
* @param mixed $params 传入参数
* @return void
*/
static public function listen($tag, &$params=NULL) { // 此时$tag = view_parse
if(isset(self::$tags[$tag])) {
if(APP_DEBUG) {
G($tag.'Start');
trace('[ '.$tag.' ] --START--','','INFO');
}
foreach (self::$tags[$tag] as $name) { //循环遍历
APP_DEBUG && G($name.'_start');
$result = self::exec($name, $tag,$params); // 这里进行执行
if(APP_DEBUG){
G($name.'_end');
trace('Run '.$name.' [ RunTime:'.G($name.'_start',$name.'_end',6).'s ]','','INFO');
}
if(false === $result) {
// 如果返回false 则中断插件执行
return ;
}
}
if(APP_DEBUG) { // 记录行为的执行日志
trace('[ '.$tag.' ] --END-- [ RunTime:'.G($tag.'Start',$tag.'End',6).'s ]','','INFO');
}
}
return;
}

我们全局搜索,view_parse或者echo $name输出调试发现调用的类为ParseTemplateBehavior

'view_parse'    =>  array(
'Behavior\ParseTemplateBehavior', // 模板解析 支持PHP、内置模板引擎和第三方模板引擎
),

在exe函数中 如果类名存在的话会进行实例化 并且调用run方法

        if('Behavior' == substr($name,-8) ){
// 行为扩展必须用run入口方法
$class = $name;
$tag = 'run'; //此时的$tag = run 所以下方调用的是run方法
}else{
$class = "plugins\\{$name}\\{$name}Plugin";
}
if(class_exists($class)){ //ThinkCMF NOTE 插件或者行为存在时才执行
$addon = new $class();
return $addon->$tag($params); //相当于 return $addon->run($params);
}

我们再看下ParseTemplateBehavior的类中的run方法

    // 行为扩展的执行入口必须是run
public function run(&$_data){
$engine = strtolower(C('TMPL_ENGINE_TYPE'));
$_content = empty($_data['content'])?$_data['file']:$_data['content'];
$_data['prefix'] = !empty($_data['prefix'])?$_data['prefix']:C('TMPL_CACHE_PREFIX');
if('think'==$engine){ // 采用Think模板引擎
if((!empty($_data['content']) && $this->checkContentCache($_data['content'],$_data['prefix']))
|| $this->checkCache($_data['file'],$_data['prefix'])) { // 缓存有效
//载入模版缓存文件
Storage::load(C('CACHE_PATH').$_data['prefix'].md5($_content).C('TMPL_CACHFILE_SUFFIX'),$_data['var']);
//C('CACHE_PATH').$_data['prefix'].md5($_content).C('TMPL_CACHFILE_SUFFIX'),$_data['var']地址为
//D:\QMDownload\PHPTutorial\www\thinkcmfx2\data\runtime\Cache\Portal
}else{
$tpl = Think::instance('Think\\Template'); //走的是这里
// 编译并加载模板文件
$tpl->fetch($_content,$_data['var'],$_data['prefix']);
}
}else{
// 调用第三方模板引擎解析和输出
if(strpos($engine,'\\')){
$class = $engine;
}else{
$class = 'Think\\Template\\Driver\\'.ucwords($engine);
}
if(class_exists($class)) {
$tpl = new $class;
$tpl->fetch($_content,$_data['var']);
}else { // 类没有定义
E(L('_NOT_SUPPORT_').': ' . $class);
}
}
}

当payload为上面的时候走的是else语句中的,我们可以去看下Think\Template的类

    public function fetch($templateFile,$templateVar,$prefix='') {
$this->tVar = $templateVar; // tvar = $_data['var']
$templateCacheFile = $this->loadTemplate($templateFile,$prefix);
//echo $templateCacheFile;
Storage::load($templateCacheFile,$this->tVar,null,'tpl');
}

它的fetch方法如下

    public function fetch($templateFile,$templateVar,$prefix='') {
$this->tVar = $templateVar; // tvar = $_data['var']
$templateCacheFile = $this->loadTemplate($templateFile,$prefix); //$templateFile为$_content
//echo $templateCacheFile;
Storage::load($templateCacheFile,$this->tVar,null,'tpl');
}

其中loadTemplate方法如下传入的参数为$templateFile,其实也就是$_content

    public function loadTemplate ($templateFile,$prefix='') {
if(is_file($templateFile)) {
$this->templateFile = $templateFile;
// 读取模板文件内容
$tmplContent = file_get_contents($templateFile);
}else{
$tmplContent = $templateFile;
}
// 根据模版文件名定位缓存文件
$tmplCacheFile = $this->config['cache_path'].$prefix.md5($templateFile).$this->config['cache_suffix']; // 判断是否启用布局
if(C('LAYOUT_ON')) {
if(false !== strpos($tmplContent,'{__NOLAYOUT__}')) { // 可以单独定义不使用布局
$tmplContent = str_replace('{__NOLAYOUT__}','',$tmplContent);
}else{ // 替换布局的主体内容
$layoutFile = THEME_PATH.C('LAYOUT_NAME').$this->config['template_suffix'];
// 检查布局文件
if(!is_file($layoutFile)) {
E(L('_TEMPLATE_NOT_EXIST_').':'.$layoutFile);
}
$tmplContent = str_replace($this->config['layout_item'],$tmplContent,file_get_contents($layoutFile));
}
}
// 编译模板内容
$tmplContent = $this->compiler($tmplContent);
Storage::put($tmplCacheFile,trim($tmplContent),'tpl');
return $tmplCacheFile;
}

倒数第二句调用了put方法,Storage::put($tmplCacheFile,trim($tmplContent),'tpl');,其中put和load方法同存的有File.class.php类文件,走到File.class.php类文件

    public function put($filename,$content,$type=''){
$dir = dirname($filename);
if(!is_dir($dir)){
mkdir($dir,0777,true);
}
if(false === file_put_contents($filename,$content)){ //这里的内容可控
E(L('_STORAGE_WRITE_ERROR_').':'.$filename);
}else{
$this->contents[$filename]=$content;
return true;
}
}

if(false === file_put_contents($filename,$content)) //这里的内容可控,然后写入缓存文件,最后调用Storage::load加载cache文件包含文件,最终导致代码执行

    public function load($_filename,$vars=null){
if(!is_null($vars)){
extract($vars, EXTR_OVERWRITE);
}
include $_filename; //进行包含文件的操作
}

转自freebuf的流程图如下:

参考文章:https://www.freebuf.com/vuls/218105.html

PHP:ThinkCMFX任意文件包含漏洞的更多相关文章

  1. phpmyadmin任意文件包含漏洞分析(含演示)

    0x01 漏洞描述 phpmyadmin是一款应用非常广泛的mysql数据库管理软件,基于PHP开发. 最新的CVE-2014-8959公告中,提到该程序多个版本存在任意文件包含漏洞,影响版本如下: ...

  2. [CVE-2014-8959] phpmyadmin任意文件包含漏洞分析

    0x01 漏洞描述 phpmyadmin是一款应用非常广泛的mysql数据库管理软件,基于PHP开发. 最新的CVE-2014-8959公告中,提到该程序多个版本存在任意文件包含漏洞,影响版本如下: ...

  3. [WEB安全]phpMyadmin后台任意文件包含漏洞分析(CVE-2018-12613)

    0x00 简介 影响版本:4.8.0--4.8.1 本次实验采用版本:4.8.1 0x01 效果展示 payload: http://your-ip:8080/index.php?target=db_ ...

  4. phpMyadmin(CVE-2018-12613)后台任意文件包含漏洞分析

    前言 影响版本:4.8.0--4.8.1 本次复现使用4.8.1     点击下载 复现平台为vulhub.此漏洞复现平台如何安装使用不在赘述.请自行百度. 漏洞复现 漏洞环境启动成功. 访问该漏洞地 ...

  5. phpMyAdmin Transformation 任意文件包含/远程代码执行漏洞

    漏洞参考 https://yq.aliyun.com/articles/679633 国外提供了一个在线测试的靶场     默认密码  root  toor https://www.vsplate.c ...

  6. phpMyAdmin本地文件包含漏洞

    4 phpMyAdmin本地文件包含漏洞 4.1 摘要 4.1.1 漏洞简介 phpMyAdmin是一个web端通用MySQL管理工具,上述版本在/libraries/gis/pma_gis_fact ...

  7. 代码审计-凡诺CMS 2.1文件包含漏洞

    0x01代码审计 后台账号密码: admin admin 安装好了是这样的 漏洞文件:/channel.php if (ism()) { include($dir.$t_mpath.$c_mcmode ...

  8. phpmyadmin 4.8.1 远程文件包含漏洞(CVE-2018-12613)

    漏洞详情 范围 phpMyAdmin 4.8.0和4.8.1 原理 首先在index.php 50-63行代码 $target_blacklist = array ( 'import.php', 'e ...

  9. thinkphp 5.x~3.x 文件包含漏洞分析

    漏洞描述: ThinkPHP在加载模版解析变量时存在变量覆盖的问题,且没有对 $cacheFile 进行相应的消毒处理,导致模板文件的路径可以被覆盖,从而导致任意文件包含漏洞的发生. 主要还是变量覆盖 ...

随机推荐

  1. OI之路

    由于各种因素 学习.初赛.时间... 我的oi可能会结束, 我也不甘于放弃, 所以 综合今后的表现, 我再决定 以后我尽量写博客.

  2. 【转】潜说js对象和数组

    /* 数组和对象 [JavaScript 权威指南 第五版] */ /* 对象: 是一个无序属性集合, 每个属性都有自己的名字和值 */ /* 创建对象简单方法, 对象直接量 */ var obj = ...

  3. Linux学习笔记之vim操作指令大全

    0x00 关于Vim Vim是款强大的文本编辑器,但是众多指令需要学习,这次记录了指令大全方便以后翻阅. Vim的几种模式 正常模式:可以使用快捷键命令,或按:输入命令行. 插入模式:可以输入文本,在 ...

  4. C# ThreadStart中如何带参数

    1.ThreadStart 线程执行带参数的方法,new Thread(new ThreadStart(delegate { ThreadTask(firstPage, lastPage); })); ...

  5. 我的探究:为什么.h头文件中不要写using namespace std

  6. 换个语言学一下 Golang (12)——Web基础

    一.web工作方式 我们平时浏览网页的时候,会打开浏览器,输入网址后按下回车键,然后就会显示出你想要浏览的内容.在这个看似简单的用户行为背后,到底隐藏了些什么呢?对于普通的上网过程,系统其实是这样做的 ...

  7. 去除vue项目地址栏中的#

    在router文件夹下的index.js中的router实例中添加mode属性,值设置为history export default new Router({ mode:"history&q ...

  8. 搞不懂JS中赋值·浅拷贝·深拷贝的请看这里

    前言 百科定义:拷贝就是拷贝指向对象的指针,意思就是说:拷贝出来的目标对象的指针和源对象的指针指向的内存空间是同一块空间,浅拷贝只是一种简单的拷贝,让几个对象公用一个内存,然而当内存销毁的时候,指向这 ...

  9. XML简述

    XML简述 本文主要内容都是在中国大学MOOC上学习的,这里做个记录. 课程:Java核心技术(进阶),华东师范大学 陈良育老师 感谢陈良育老师,在他的慕课上受益匪浅. XML基本概念 XML(eXt ...

  10. 使用SAP CRM中间件XIF(External Interface)一步步创建服务订单

    tcode WE19, choose an existing IDOC in the system: Just change the existing IDOC Service Order ID to ...