影响版本

V1.0.0.20200506_beta(最新版)

利用限制

/application/config.php 文件中:

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

即需要开启会员中心功能

漏洞分析

/application/index/User.php文件

第58-67行:

 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_empty为开发者预留的钩子可以忽视不看,主要看 return $this->view->fetch($name);

此方法中的$name参数可控,并且将$name的值传入到了fecth()函数中。

fetch()为thinkphp的解析模板函数,其返回模板文件渲染后的内容

fetch()函数的关键内容如下:

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

继续调用栈可以看下其实这个fetch()函数调用的是内置模板引擎的fetch方法, 这个方法实际上就是将要输出的页面内容赋值给一个变量,为了方便,thinkphp在对模板渲染的过程中,添加了php标签功能,使得其可以解析php代码。

总之一句话,这个漏洞其实就是由于对传入变量过滤不严导致的模板引擎注入漏洞,只要控制了传入模板的文件,就可以利用模板本身的渲染功能,实现包含漏洞getshell

另外需要注意的是,当验证传入的模板是否是文件时,使用的is_file()函数,这个函数在Linux下和windows下的判断会有所不同,具体如下:

1、在linux下利用is_file()来判断类似于/****/../../../../etc/passwd文件时,如果****是不存在的目录,则会返回false,在windows下 ,这个目录存在与否,均返回true,如下图所示:

1.png

2.png

2、在linux下,is_file()函数判可用于判断符号链接

3、在linux下,is_file函数会受到权限的影响,当前用户权限不足或父目录没有设置+x权限时,is_file()会返回false

4、windows系统里面/\ 都可以使用,但是在linux下只能使用/ 来分隔路径,因此这会导致is_file()在不同系统下的返回结果不一致

3.png

4.png

5、is_file()判断文件时,如果文件大小超过2^32时,会判断失败

漏洞验证

通过前文可知,这个漏洞的利用点在_empty()函数,需要注意的是,在官方文档中通常_empty()方法是用来判断一个方法是否存在,如果不存在,则进入该函数。而这里是开发者自定义的方法,因此直接传入_empty方法,调用name参数即可。

利用过程如下:

在前台的会员中心,个人资料处,上传修改头像:

5.png

抓包后修改图片数据(满足图片头格式即可):

6.png

记录下路径后,成功getshell

7.png

在Linux下,通过这种方法会失效,因为在/public路径下不存在user目录,由前文中的知识点可以知道,当不存在这个目录的时候,无论怎么跳转目录,is_file()函数返回的结果始终未false,因此无法利用该漏洞,如下图所示:

8.png

当我们在/public目录下创建文件夹/user,在利用,即可成功:

9.png

最后感谢joseph师傅提供的漏洞点,又学习了一波

使用微信扫描二维码完成支付

版权声明:本文为作者原创,如需转载须联系作者本人同意,未经作者本人同意不得擅自转载。