代码审计:PbootCMS2.07内核处理缺陷导致的一个前台任意文件包含漏洞分析

挖出来之后看了下官网发现不到半个月之前更新了最新版,下下来之后发现这洞修了..我吐了
干脆直接发出来 分享下思路吧

0x00漏洞分析

漏洞发生在PbootCMS内核的模板解析函数中
为了方便看直接上一个完整的Parser代码吧

public function parser($file)
    {
        // 设置主题
        $theme = isset($this->vars['theme']) ? $this->vars['theme'] : 'default';
        //file形式:xxxxx/../../../可以双写穿越
        $theme = preg_replace('/\.\.(\/|\\\)/', '', $theme); // 过滤掉相对路径
        $file = preg_replace('/\.\.(\/|\\\)/', '', $file); // 过滤掉相对路径

        if (strpos($file, '/') === 0) { // 绝对路径模板
            $tpl_file = ROOT_PATH . $file;
        } elseif (! ! $pos = strpos($file, '@')) { // 跨模块调用
            $path = APP_PATH . '/' . substr($file, 0, $pos) . '/view/' . $theme;
            define('APP_THEME_DIR', str_replace(DOC_PATH, '', $path));
            if (! is_dir($path)) { // 检查主题是否存在
                error('模板主题目录不存在!主题路径:' . $path);
            } else {
                $this->tplPath = $path;
            }
            $tpl_file = $path . '/' . substr($file, $pos + 1);
        } else {
            // 定义当前应用主题目录
            define('APP_THEME_DIR', str_replace(DOC_PATH, '', APP_VIEW_PATH) . '/' . $theme);
            if (! is_dir($this->tplPath .= '/' . $theme)) { // 检查主题是否存在
                error('模板主题目录不存在!主题路径:' . APP_THEME_DIR);
            }
            $tpl_file = $this->tplPath . '/' . $file; // 模板文件
        }
        $note = Config::get('tpl_html_dir') ? '<br>同时检测到您系统中启用了模板子目录' . Config::get('tpl_html_dir') . ',请核对是否是此原因导致!' : '';
        file_exists($tpl_file) ?: error('模板文件' . APP_THEME_DIR . '/' . $file . '不存在!' . $note);
        $tpl_c_file = $this->tplcPath . '/' . md5($tpl_file) . '.php'; // 编译文件

        // 当编译文件不存在,或者模板文件修改过,则重新生成编译文件
        if (! file_exists($tpl_c_file) || filemtime($tpl_c_file) < filemtime($tpl_file) || ! Config::get('tpl_parser_cache')) {
            $content = Parser::compile($this->tplPath, $tpl_file); // 解析模板
            file_put_contents($tpl_c_file, $content) ?: error('编译文件' . $tpl_c_file . '生成出错!请检查目录是否有可写权限!'); // 写入编译文件
            $compile = true;
        }
        //tplPath:PbootCMS/template
        ob_start(); // 开启缓冲区,引入编译文件
        $rs = include $tpl_c_file;
        if (! isset($compile)) {
            foreach ($rs as $value) { // 检查包含文件是否更新,其中一个包含文件不存在或修改则重新解析模板
                if (! file_exists($value) || filemtime($tpl_c_file) < filemtime($value) || ! Config::get('tpl_parser_cache')) {
                    $content = Parser::compile($this->tplPath, $tpl_file); // 解析模板
                    file_put_contents($tpl_c_file, $content) ?: error('编译文件' . $tpl_c_file . '生成出错!请检查目录是否有可写权限!'); // 写入编译文件
                    ob_clean();
                    include $tpl_c_file;
                    break;
                }
            }
        }
        $content = ob_get_contents();
        ob_end_clean();
        return $content;
    }

简单讲一下重点

这里对传入路径的过滤并不严格,可以双写绕过
再往下跟一下

当模板文件不在缓存中的时候,会读取$tpl_file中的内容,然后写入缓存文件中并且包含。
也就是说,当parser函数的参数可以被控制的时候,就会造成一个任意文件包含。
所以,要找一个可控参数的parser调用
经过简单寻找,就可以发现前台控制器TagController中的index方法,完美符合我们的要求
上代码:

public function index()
    {
        // 在非兼容模式接受地址第二参数值
        if (defined('RVAR')) {
            $_GET['tag'] = RVAR;
        }

        if (! get('tag')) {
            _404('您访问的页面不存在,请核对后重试!');
        }
        $a=get('tag');
        $tagstpl = request('tagstpl');
        if (! preg_match('/^[\w\-\.\/]+$/', $tagstpl)) {
            $tagstpl = 'tags.html';
        }
        $content = parent::parser($this->htmldir . $tagstpl); // 框架标签解析
        $content = $this->parser->parserBefore($content); // CMS公共标签前置解析
        $content = $this->parser->parserPositionLabel($content, 0, '相关内容', homeurl('tag/' . get('tag'))); // CMS当前位置标签解析
        $content = $this->parser->parserSpecialPageSortLabel($content, - 2, '相关内容', homeurl('tag/' . get('tag'))); // 解析分类标签
        $content = $this->parser->parserAfter($content); // CMS公共标签后置解析
        $this->cache($content, true);
    }

传入parser的参数,是通过request接收的参数$tagstpl和$this->htmldir拼接的,因为已经知道在函数内部可以出现目录穿越,所以前面的路径不管怎么拼接都无所谓啦。
这样就完成了整个攻击链,TagController->parser->双写绕过->文件读取->文件写入->文件包含

00x1 漏洞验证

因为是windows搭的环境,就不读/etc/passwd了,读一下D盘根目录的文件吧


成功
再包含个phpinfo试试

也是可以的
漏洞验证完成
本来是想再看看有没有什么组合利用的姿势,毕竟文件包含这种洞本身利用的灵活度还是蛮高的
不过既然最新版已经修了 就不多看了

00x2 修复方案

官方的修复非常简单粗暴,没有对内核中导致漏洞的根本原因过滤不充分进行修改,而是直接对TagController的正则进行了修改,强制限制了后缀名为html。
看一下前后对比
2.07:

2.08:

本文来源于: https://xz.aliyun.com/t/7744

相关推荐

CSZ CMS 1.2.7 xss分析与复现

CSZ CMS 1.2.7 xss分析与复现 简介 CSZ CMS是一个开源Web应用程序,允许管理网站上的所有内容和设置。CSZ CMS是在Codeigniter3的基础上构建的,并设计了Bootstrap3的结构,这应该使您的网站轻松响

蚁剑改造过WAF系列(一)

Author: lz520520@深蓝攻防实验室 0x00 前言 为什么会有修改蚁剑这个念头呢,之前有朋友对冰蝎做了改造,本来冰蝎就是加密传输,可对抗大多数WAF,但因为有一些弱特征、强特征等,通过流量分析转换成自动化检测,也可以做到冰蝎流

Java 反序列化回显的多种姿势

写在文前 在研究weblogic、fastjson、shiro反序列化漏洞时,多次遇到了回显问题,本文将从以下几种角度出发来分别探讨反序列化回显的问题,也感谢各位师傅们的反序列化回显研究。 defineClass RMI绑定实例 URLCl

无字母数字webshell进阶收藏版

收藏即为会了!!!!!! 还没有看过P神两篇文章的走这里 一些不包含数字和字母的webshell 无字母数字webshell之提高篇 Unicode码运用 1.原理 P神在他文章中指出: 我们可以使用[].Φ来得到字符串Array. 我们

蚁剑改造过WAF系列(二)

Author: lz520520@深蓝攻防实验室 0x00 前言 上一篇讲过了编码器和解码器,本篇会讲解蚁剑源码部分,添加asp/aspx解码模块,来实现asp/aspx的加密回显。 0x01 蚁剑源码介绍 大家蚁剑可能用到最多的还是php

SaltStack CVE-2020-11651/11652 分析

SaltStack是一种基于C/S架构的服务器基础架构集中管理平台,最近披露出存在两个安全漏洞 CVE-2020-11651 权限缺陷、CVE-2020-11652 任意文件读写漏洞,官方公告SALT 3000.2 RELEASE NOTE

SSTI模板注入(Python+Jinja2)

cl4y@星盟 [toc] SSTI模板注入(Python+Jinja2) 之前有做过一些SSTI的ctf,但是没有系统的学习,今天来总结一下。 前提知识 python、flask、jinja2 SSTI介绍 ssti主要为python的一

蚁剑改造过WAF系列(三)

Author: lz520520@深蓝攻防实验室 0x00 前言 前两篇从蚁剑的介绍,编解码器的功能讲解,到源码分析和解码器的改造,其实已经实现了大部分流量的加密传输,只要在设计一个简单的加密算法即可,比如异或算法,用字符串和key做异或即

一次敏感信息泄露引发的逻辑漏洞挖掘

根据手头上的信息,最大化的利用,一次简单的漏洞挖掘,感觉过程很有意思分享一下~ 0x01初始 收集子域,也是渗透的初始。这里我只是简单用了fofa发现了该公司用来管理合作的一些子域名然后发现是登录管理页面,深入然后发现很多的敏感信息。也是从

YCCMS代码审计(新手教学方向)

前言 在逛CNVD时发现这款CMS存在不少常见的漏洞,并且看样子漏洞没有修复,一时好奇就下载下来看了看。经过简单的分析发现该CMS触发漏洞的方式挺常见的,正好可以从代码方面 分析一下这些漏洞的成因,加深对一些常见漏洞的理解 CMS下载地址:

记一次excel XXE漏洞

0x00 概述 Microsoft Office从2007版本引入了新的开放的XML文件格式,基于压缩的ZIP文件格式规范,改后缀名为zip再解压缩可以发现其中多数是描述工作簿数据、元数据、文档信息的XML文件。 许多网站允许上传/导入文