ekucms2.5 local file contains vulnerability - code audit

Posted by backie on Wed, 16 Feb 2022 17:44:52 +0100

ekucms2.5 local file contains vulnerability - code audit

1, Foreword

In order to learn the operation principle of Thinkphp framework and strengthen its own code audit ability, we specially looked for a CMS vulnerability article written by php on the Internet for reproduction and reverse code audit. Vulnerability reference articles are as follows:

Easy cool cms2 5 local file contains vulnerability getshell - Oran9e - blog Garden (cnblogs.com)

  • Debugging tools:

phpstudy2018+phpstorm+xdebug; Firefox; xdebug helper

  • Tool instructions:

(1) phpstudy2018 has built-in xdebug module, which can be adjusted freely according to needs, which is more convenient.

(2) location of Xdebug module in phpstudy 2018: phpstudy installation path \ phpstudy\PHPTutorial\php\php - {version} - nts\ext\php_xdebug.dll

(3) environment configuration reference connection:

(32 messages) thinkphp learning notes (7) – PHPstorm+PHPstudy+Xdebug breakpoint debugging_ limingliang_ Blog - CSDN blog_ PHP debug think breakpoint

2, Ideas of code audit

(1) Use the high-risk functions contained in the files in PHP code to quickly locate those files, which may have local file inclusion vulnerabilities. (mainly through the global search function)

(2) In the process of analyzing whether there are local vulnerabilities in the file, analyze the file relationship and confirm the main function points of the file. (static trace functions and variables)

(3) Locate the controllable parameter transmission point, and use dynamic debugging to analyze the transmission relationship between data. (dynamic debugging)

3, Code audit process

1. Use the key function (include) to locate PHP files that may contain vulnerabilities, and then exclude the files with fixed file names from the search results. The final results are as follows:

core/Lib/View.class.php

core/ThinkPHP/Lib/Think/Core/View.class.php

core/ThinkPHP/Lib/Think/Template/ThinkTemplate.class.php

core/ThinkPHP/Common/functions.php

temp/~runtime.php

2. Locate the function positions in the above three files in turn and analyze the logic. The results are as follows:

  • Lib\View.class.php

By analyzing the above code, it is found that this function is used during debugging. At the same time, since the containing file name ($traceFile = CONFIG_PATH. 'trace.php';) has been defined at the beginning, Therefore, there are no inputable points, which can be skipped directly.

Note:

(1) Tracked variable CONFIG_PATH. It is found that this variable has been defined in (core/ThinkPHP/Common/paths.php).

(3) Variable APP_PATH location: core / conf / define php

(3) Variable conf_ Location of dir definition: core / ThinkPHP / common / paths php

To sum up, in the same way, look for other functions including in the file. Finally, it is found that the function that does not use the fixed file path is: fetch()

The code is as follows:

public function fetch($templateFile='',$charset='',$contentType='text/html',$display=false)
    {
        $GLOBALS['_viewStartTime'] = microtime(TRUE);
        if(null===$templateFile)
            // Use null parameter as template name and return directly without any output
            return ;
        if(empty($charset))  $charset = C('DEFAULT_CHARSET');
        // Web page character coding
        header("Content-Type:".$contentType."; charset=".$charset);
        header("Cache-control: private");  //Support page bounce
        //Page cache
        ob_start();
        ob_implicit_flush(0);

        if(!file_exists_case($templateFile))
            // Auto locate template file
            $templateFile   = $this->parseTemplateFile($templateFile);

        $engine  = strtolower(C('TMPL_ENGINE_TYPE'));
        if('php'==$engine) {
            // Template array variables are decomposed into independent variables
            extract($this->tVar, EXTR_OVERWRITE);
            // Load PHP templates directly
            include $templateFile;
        }elseif('think'==$engine && $this->checkCache($templateFile)) {
            // If it is a Think template engine and the cache is valid, decompose the variables and load the template cache
            extract($this->tVar, EXTR_OVERWRITE);
            //Load template cache file
            include C('CACHE_PATH').md5($templateFile).C('TMPL_CACHFILE_SUFFIX');
        }else{
            // The template file needs to be recompiled to support the third-party template engine
            // Call the template engine to parse and output
            $className   = 'Template'.ucwords($engine);
            require_cache(THINK_PATH.'/Lib/Think/Util/Template/'.$className.'.class.php');
            $tpl   =  new $className;
            $tpl->fetch($templateFile,$this->tVar,$charset);
        }
        $this->templateFile   =  $templateFile;
        // Get and empty cache
        $content = ob_get_clean();
        // Template content replacement
        $content = $this->templateContentReplace($content);
        // Layout template analysis
        $content = $this->layout($content,$charset,$contentType);
        // Output template file
        return $this->output($content,$display);
    }

By analyzing the above code logic, the following conclusions are drawn:

(1)The function has received variables: $templateFile

(2)This function does not have strict filtering behavior when receiving parameter values of variables

(3)Exists in this function include $templateFile operation

Therefore, it can be judged that the function has a vulnerability contained in this file. It is necessary to further track the location where the function is called and confirm whether the $templateFile variable can be obtained from the URL.

After searching, it is found that the $templateFile variable is defined in the file, but no way to call the View class under the file is found. (subsequent analysis found that the source code had been changed, so it could not be used.)

3. Through the above conclusion and searching all the codes in the project, it is found that the developer has multiple definitions of the same function. Therefore, directly use the function fetch() to find the code, and finally find the same function definition and call in the following files:

(1)temp/~runtime.php

(2)core/ThinkPHP/Lib/Think/Core/Action.class.php

(1) After analysis, it is found that core / ThinkPHP / lib / think / core / action class. The fetch() function call does not exist in the PHP file, so the file is excluded directly.

(2) Analyze temp / ~ runtime The fecth() function in the source code of PHP.

 public function fetch($templateFile='',$charset='',$contentType='text/html',$display=false) {
        $GLOBALS['_viewStartTime'] = microtime(TRUE);
        if(null===$templateFile) return ;
        if(empty($charset)) $charset = C('DEFAULT_CHARSET');
        header("Content-Type:".$contentType."; charset=".$charset);
        header("Cache-control: private");
        ob_start();
        ob_implicit_flush(0);
        if(!file_exists_case($templateFile)) $templateFile = $this->parseTemplateFile($templateFile);
        $engine = strtolower(C('TMPL_ENGINE_TYPE'));
        if('php'==$engine) {
            extract($this->tVar, EXTR_OVERWRITE);
            include $templateFile;
        } elseif('think'==$engine && $this->checkCache($templateFile)) {
            extract($this->tVar, EXTR_OVERWRITE);
            include C('CACHE_PATH').md5($templateFile).C('TMPL_CACHFILE_SUFFIX');
        } else {
            $className = 'Template'.ucwords($engine);
            require_cache(THINK_PATH.'/Lib/Think/Util/Template/'.$className.'.class.php');
            $tpl = new $className;
            $tpl->fetch($templateFile,$this->tVar,$charset);
        }
        $this->templateFile = $templateFile;
        $content = ob_get_clean();
        $content = $this->templateContentReplace($content);
        $content = $this->layout($content,$charset,$contentType);
        return $this->output($content,$display);
    }

By analyzing the code, it can be found that there are two vulnerability locations contained in local files;

        if('php'==$engine) {
            extract($this->tVar, EXTR_OVERWRITE);
            include $templateFile;
        } elseif('think'==$engine && $this->checkCache($templateFile)) {
            extract($this->tVar, EXTR_OVERWRITE);
            include C('CACHE_PATH').md5($templateFile).C('TMPL_CACHFILE_SUFFIX');
        } else {
            $className = 'Template'.ucwords($engine);
            require_cache(THINK_PATH.'/Lib/Think/Util/Template/'.$className.'.class.php');
            $tpl = new $className;
            $tpl->fetch($templateFile,$this->tVar,$charset);
        }
  • include $templateFile;

After analyzing the code logic, it is found that the user-defined variable TMPL_ENGINE_TYPE=think, which does not meet the if('php '= = $engine) judgment condition, so it cannot be called.

  • include C('CACHE_PATH').md5($templateFile).C('TMPL_CACHFILE_SUFFIX')

1) by continuing to trace the code logic, you need to meet the conditions for calling checkCache($templateFile) before you can use include. Next, continue to trace and analyze checkCache($templateFile)

    protected function checkCache($tmplTemplateFile) {
        if (!C('TMPL_CACHE_ON')) return false;
        $tmplCacheFile = C('CACHE_PATH').md5($tmplTemplateFile).C('TMPL_CACHFILE_SUFFIX');
        if(!is_file($tmplCacheFile)) {
            return false;
        } elseif (filemtime($tmplTemplateFile) > filemtime($tmplCacheFile)) {
            return false;
        } elseif (C('TMPL_CACHE_TIME') != -1 && time() > filemtime($tmplCacheFile)+C('TMPL_CACHE_TIME')) {
            return false;
        }
        return true;
    }

Through the final tracking analysis of C('CACHE_PATH ') = / temp/Cache and C('TMPL_CACHFILE_SUFFIX') = html, the variable $tmplCacheFile=/temp/Cache / template name is obtained html, and judge whether the cache file exists; If the cache file does not exist, put it back to True; If there is a cache file and the modification time of the cache file is less than the saving time, FALSE is returned. (during further dynamic debugging test and analysis, it is found that the TMPL_CACHE_ON variable is always OFF after processing, so it will be skipped directly.)

2) Return the fetch() function, continue the analysis, and finally locate the available code block as follows:

a. there is no cache file and tmpl_ CACHE_ The incoming status is ON:

...
    extract($this->tVar, EXTR_OVERWRITE);
    include C('CACHE_PATH').md5($templateFile).C('TMPL_CACHFILE_SUFFIX');
...

This value can be used directly. (PS: this time is consistent with the principle of local Inclusion Vulnerability in ThinkPHP3.X~5.X)

b. There are cache files:

...
	$className = 'Template'.ucwords($engine);
	require_cache(THINK_PATH.'/Lib/Think/Util/Template/'.$className.'.class.php');
	$tpl = new $className;
	$tpl->fetch($templateFile,$this->tVar,$charset);
...

Further trace the code and find that it is the process of calling and loading the template file. At the same time, it is found that core / ThinkPHP / lib / think / util / template / templatethink. Is called class. The load () function in the PHP file. Through further follow-up of the function, it is found that there is a file containing vulnerability code in the load() function: include $templateCacheFile. So far, the source code of the file containing vulnerability has been located successfully.

4. Position the parameter transfer

Premise statement:
	Because it is easy to find by observing the source code project structure, ekucms2.5 It was used thinkphp of MVC(Namely:Moudle,View,Controller)Mode, its transmission URL There are three parameter formats:
	(1)http://{IP:port}/index.php?a=test1&b=test2
	(2)http://{IP:port}/index.php/{moudle}/{Controller} / variable / variable value  
	(3)http://{IP:port}/index.php/?s={moudle}/{Controller} / variable / variable value 
Through observation, it is found that the transmission parameter is the third type, so you only need to find the call TemplateThink.class.php File module and controller

(1) First, according to the directory structure of thinkphp, look for the configuration files and see which modules can obtain parameter values from HTTP messages. Through the search, we finally found config There are two places in PHP-

They are: / home/info/detail and / home/my/show respectively. Visit the web page of the corresponding path in turn to check whether the breakpoint is triggered normally. Finally, confirm that the / home/my/show module can pass in the value to the parameter id.

The file location for processing id value is: core / lib / action / home / myaction class. php

4, Summary

​ ekucms2.5 the bottom layer is written in the framework of thinkphp5. This local file contains vulnerabilities, which is essentially the same as thinkphp5 The causes of X are basically the same. During vulnerability exploitation, the flow chart of program operation is as follows:

Topics: PHP security Web Security