骑士CMS 文件包含RCE漏洞分析

0x00 前言

之前在逛p牛知识星球的时候看到了panda师傅发的这个74cms文件包含RCE,然后又是最近的所以就跟进一波,从中学习学习

复现环境以及源码下载链接:

win10 、phpstudy

源码下载链接:

0x01 前置知识

74cms是基于ThinkPHP 3.2.3框架的基础上二次开发的,所以我们在审计74cms之前我们需要先对ThinkPHP框架做一个熟悉,ThinkPHP的开发手册写的非常好,我放到我的服务器上了下载链接如下

下载链接:http://data.wjlshare.xyz/project-source-code/ThinkPHP3.2.3dev.pdf

ThinkPHP是一个单入口文件进行项目的部署和访问,所有功能的实现都是通过这个唯一的入口文件。

骑士CMS 文件包含RCE漏洞分析 - 图1

大致项目结构如下:

骑士CMS 文件包含RCE漏洞分析 - 图2

ThinkPHP中主要采用的是多层MVC架构:Views(视图)、Model(模块)、Controller(控制器)

Model: 主要负责封装和实现应用的具体功能等

Views:主要负责视图的展现

Controller:主要负责请求拦截妆发,加载配置文件,调配对应的模块来处理对应的请求等

ThinkPHP框架自带了一个应用目录结构,并且带了一个默认的应用入口文件,方便部署和测试,默认的应
用目录是Application

骑士CMS 文件包含RCE漏洞分析 - 图3

ThinkPHP项目在初始化的时候会自动的生成几个默认的模块

骑士CMS 文件包含RCE漏洞分析 - 图4

Common 公共模块是不能直接访问的

其中每个模块之间都是独立的,所以每个模块都可以自由的设置和添加 (也就是之前说的多层MVC架构)

骑士CMS 文件包含RCE漏洞分析 - 图5

ThinkPHP下访问是通过利用入口文件访问对应模块下对应控制器的对应操作方法

ThinkPHP下访问默认页面就会通过如下路由进行访问

http://localhost/index.php?m=Home&c=Index&a=index

m : Home模块

c :Index控制器类

a :index操作

骑士CMS 文件包含RCE漏洞分析 - 图6

效果如下:

骑士CMS 文件包含RCE漏洞分析 - 图7

我们这里直接来看74cms这样更加形象一些

骑士CMS 文件包含RCE漏洞分析 - 图8

上面这个路由对应访问的就是

Admin模块下的Index控制类中的login操作

骑士CMS 文件包含RCE漏洞分析 - 图9

这边只能简单的介绍一下ThinkPHP的一些信息,具体的话师傅们可以去看开发者手册

0x02 漏洞复现

文件包含日志实现RCE

利用报错将shell写入日志

骑士CMS 文件包含RCE漏洞分析 - 图10

由于基于thinkPHP 二次开发,所以日志目录就是 ThinkPHP的日志目录

/data/Runtime/Logs/Home/xx_xx_xx.log

骑士CMS 文件包含RCE漏洞分析 - 图11

这里如果直接是 <?php phpinfo();?> 是不会有内容回显的,为什么呢?我们在下面的代码分析中会提到

0x03 代码分析

我们来看74cms官网修复的地方

骑士CMS 文件包含RCE漏洞分析 - 图12

漏洞代码如下:

骑士CMS 文件包含RCE漏洞分析 - 图13

可以发现官方修复手法在之前的基础上增加了一个判断

我们从漏洞点开始分析:

文件位置:/Application/Common/Controller/BaseController.class.php 大约 168行左右

发现assign_resume_tpl 函数需要传入两个参数,文件包含存在的位置在 $tpl 参数,所以我们直接跟进 fetch方法

骑士CMS 文件包含RCE漏洞分析 - 图14

文件位置 : /ThinkPHP/Library/Think/Controller.class.php , 之前传入的 $tpl 传到了这里的第一个参数的位置,$content $prefix 都为空,继续跟进

骑士CMS 文件包含RCE漏洞分析 - 图15

文件位置: /ThinkPHP/Library/Think/View.class.php

由于$content 参数为空,所以进入if判断语句,如果文件存在就进入 parseTemplate ,此时的 $templateFile 的数值为我们之前传入的文件地址 例 :./data/upload/resume_img/2101/08/5ff814074d7af.png,由于文件调用 parseTemplate 进行解析

  1. $templateFile = $this->parseTemplate($templateFile);

骑士CMS 文件包含RCE漏洞分析 - 图16

跟进到我们的 parseTemplate 方法,在同一个文件

这个函数会进行一个判断传入目标路径是否是文件,如果是的话则直接进行一个返回,如果不是文件的话就会利用ThinkPHP的C函数读取配置文件中的TMPL_TEMPLATE_SUFFIX 进行一个拼接

默认拼接后缀为 .html

骑士CMS 文件包含RCE漏洞分析 - 图17

这里我们传入的文件路径是文件所以直接返回路径,并不会进行拼接

骑士CMS 文件包含RCE漏洞分析 - 图18

继续返回到之前的fetch函数

继续往下看发现有一个if判断语句会将phpTMPL_ENGINE_TYPE 进行一个比较 ,通过查看发现74cms默认的值为THINK

骑士CMS 文件包含RCE漏洞分析 - 图19

所以我们进入到else语句中,将数值传入了数组,然后再带入到了 Hook::listen 方法中

即监听 view_parse 标签位,利用数组传入多个参数

骑士CMS 文件包含RCE漏洞分析 - 图20

ps: 这里来解释一下上面的那个问题 为什么 普通的 <?php phpinfo();?> 没有回显,而是需要在后面添加 ob_flush();

我们这里看到第118行,ob_start(); 这个代码的意思是打开缓冲区,所以我们phpinfo信息会存储在缓冲区,所以才没有回显,那么我们想要有回显的话,只需要执行ob_flush冲刷出(送出)输出缓冲区中的内容,打印到浏览器页面上即可

参考链接:https://xz.aliyun.com/t/8596#toc-5

这里的标签涉及ThinkPHP框架的Behavior行为,我们可以把标签理解为钩子函数,即程序运行到这个标签的时候就会被拦截下来执行相关的行为

骑士CMS 文件包含RCE漏洞分析 - 图21

继续跟进文件

文件位置:/ThinkPHP/Library/Think/Hook.class.php 大约80行左右的位置

当系统执行到了 view_parse 标签位的时候,ThinkPHP会找到Hook::listen() 方法,查找 $tags 中绑定 view_parse 标签的方法,然后利用foreach进行遍历并且执行 Hook:exec 方法

骑士CMS 文件包含RCE漏洞分析 - 图22

跟进 exec 方法 约119行左右

发现会做一个判断检查行为名称,如果包含 Behavior 字符串,那么入口就必须是run方法

骑士CMS 文件包含RCE漏洞分析 - 图23

全局搜索 view_parse 找到配置文件

文件位置:/ThinkPHP/Mode/common.php 大约61行左右

骑士CMS 文件包含RCE漏洞分析 - 图24

从上面的配置文件中可以发现,view_parse 执行了 Behavior\ParseTemplateBehavior 类,所以入口就是run 方法,我们跟踪过去进行查看

文件位置 : /ThinkPHP/Library/Behavior/ParseTemplateBehavior.class.php 大约17行左右

上文我们已经知道我们的模板引擎就是Think所以我们进入第一个if 判断,如果我们是第一次进行模板解析的话会进入else语句,然后执行了fetch() 方法

骑士CMS 文件包含RCE漏洞分析 - 图25

继续跟踪

文件位置:/ThinkPHP/Library/Think/Template.class.php 大约75行左右

发现在77行左右调用了 loadTemplate 方法

骑士CMS 文件包含RCE漏洞分析 - 图26

继续跟进我们的方法在同文件下大约89行左右

会进行一个if判断如果我们是文件的话就会读取我们的内容 存放到 $tmplContent 中,由于默认 LAYOUT_ON 是false 所以我们直接进入else语句

骑士CMS 文件包含RCE漏洞分析 - 图27

继续跟踪我们的compiler 函数

同名文件下 大约129行左右

发现在解析过程中,并没有对文件内容进行过滤,而是直接进行了拼接,然后返回了我们的内容

骑士CMS 文件包含RCE漏洞分析 - 图28

回到我们之前的 loadTemplate函数

将我们之前返回的内容进行一个缓存处理

骑士CMS 文件包含RCE漏洞分析 - 图29

全局搜索 cache_suffix

骑士CMS 文件包含RCE漏洞分析 - 图30

骑士CMS 文件包含RCE漏洞分析 - 图31

即如下位置

骑士CMS 文件包含RCE漏洞分析 - 图32

最后返回我们的缓存文件名给fetch函数

骑士CMS 文件包含RCE漏洞分析 - 图33

如下图:

骑士CMS 文件包含RCE漏洞分析 - 图34

进入了 Storage::load($templateCacheFile,$this->tVar,null,'tpl'); 方法

我们跟进该方法

文件位置:/ThinkPHP/Library/Think/Storage/Driver/File.class.php 大约76行左右

发现包含了这个文件,从而导致RCE

骑士CMS 文件包含RCE漏洞分析 - 图35

0x04 参考链接

https://xz.aliyun.com/t/8596#toc-5

https://xz.aliyun.com/t/8520