Analyzing a Front-End Shell Upload Vulnerability in the Latest FastAdmin

SummaryAffected version: V1.0.0.20200506 beta, the latest release. Requirement: the member center must be enabled in /application/config.php. Vulnerability analysis: lines 58–67 of /application/index/User.php. user_request_empty is a developer hook and can be ignored; the important part is return $thi…

Code Auditingphp0dayfastsadmin

Affected Version

V1.0.0.20200506 beta (latest release)

Exploitation Requirements

In /application/config.php:

PHP
    //是否开启前台会员中心
    'usercenter'            => true,

The member center feature must be enabled.

Vulnerability Analysis

/application/index/User.phpfile

Lines 58–67:

PHP
 public function _empty($name)
    {
		$data = Hook::listen("user_request_empty", $name);
   		 foreach ($data as $index => $datum) {
        	$this->view->assign($datum);
   		 }
    return $this->view->fetch($name);
}

user_request_emptyis a developer hook and can be ignored. Focus on return $this->view->fetch($name);

In this method,$nameThe parameter is controllable, and$namepasses the value intofecth()function.

fetch()is ThinkPHP's template parsing function and returns the rendered contents of the template file.

fetch()The important part of the function is:

PHP
  public function fetch($template, $data = [], $config = [])
    {
        if ('' == pathinfo($template, PATHINFO_EXTENSION)) {
            // 获取模板文件名
            $template = $this->parseTemplate($template);
        }
        // 模板不存在 抛出异常
        if (!is_file($template)) {
            throw new TemplateNotFoundException('template not exists:' . $template, $template);
        }
        // 记录视图信息
        App::$debug && Log::record('[ VIEW ] ' . $template . ' [ ' . var_export(array_keys($data), true) . ' ]', 'info');
        $this->template->fetch($template, $data, $config);
    }

Following the call stack shows that fetch() invokes the built-in template engine's fetch method. It assigns the rendered page content to a variable. For convenience, ThinkPHP supports PHP tags during template rendering, allowing the template to parse PHP code.

In one sentence, this is a template injection vulnerability caused by insufficient filtering of an input variable. If an attacker controls the template file, the template renderer can be abused as a file-inclusion primitive to obtain a shell.

Also note that when verifying whether the supplied template is a file, the code usesis_file()function. It behaves differently on Linux and Windows, as follows:

1. Exploiting on Linuxis_file()to test paths such as/****/../../../../etc/passwdWhen checking the file, if****is a nonexistent directory and therefore returns false. On Windows it returns true whether or not the directory exists, as shown below:

1.png
2.png

2. On Linux,is_file()can be used to test symbolic links.

3. On Linux,is_fileis affected by permissions. If the current user lacks permission or the parent directory does not have +x permission,is_file()returns false.

4. On Windows/and\ both forms work, but on Linux only/ as the path separator, which causesis_file()returns different results on different operating systems.

3.png
4.png

5、is_file()fails when checking files larger than 2^32 bytes.

Verification

As shown above, the exploitation point is in_empty()function. Note that the official documentation usually says_empty()checks whether a method exists and enters this function if it does not. Because this is a developer-defined method, directly supply_emptymethod by supplying the name parameter.

The exploitation process is:

In the member center, open the profile page and upload a new avatar:

5.png

Capture the request and modify the image data, retaining a valid image header:

6.png

After recording the path, the shell is obtained successfully.

7.png

This method fails on Linux because in/publicthe path does not containuserdirectory. As explained above, when this directory does not exist, no amount of directory traversal will makeis_file()The function always returns false, so this approach cannot exploit the vulnerability, as shown below:

8.png

When we are in/publicCreate a directory under/user, then exploit it to succeed:

9.png

Finally, thanks to Joseph. I learned something new again.