首页 > 技术文章 > PbootCMS审计记录_将CTF思维带入实战

Gcker 2020-10-20 22:14 原文

PbootCMS审计记录_将CTF思维带入实战

本文记录我个人的第三次审计记录,比较有趣,就记录下来分享一下。这次审出的都是后台漏洞(前台实在没什么功能...),大佬轻喷啊!

0x01 PbootCMS

PbootCMS是全新内核且永久开源免费的PHP企业网站开发建设管理系统,是一套高效、简洁、 强悍的可免费商用的PHP CMS源码,能够满足各类企业网站开发建设的需要。系统采用简单到想哭的模板标签,只要懂HTML就可快速开发企业网站。
本次审计的版本是v2.0.7

0x02 过滤函数分析

PbootCMS新建了get()、post()、request()、cookie()函数,用于代替$_GET、$_POST、$_REQUEST、$_COOKIE,且全局都通过前者来代替后者获取数据。由于这几个函数的数据处理逻辑基本一致,所以我们直接分析下get()函数的实现代码:

代码文件:/core/function/helper.php

get():
可以看到get()初始化了$condition后将$name和$condition传入filter,其中$conditio的d_source为指定获取get参数数据。我们跟进到filter中去看一下。

function get($name, $type = null, $require = false, $vartext = null, $default = null)
{
    $condition = array(
        'd_source' => 'get',
        'd_type' => $type,
        'd_require' => $require,
        $name => $vartext,
        'd_default' => $default
    
    );
    return filter($name, $condition);
}

filter():
这里可以看到filter()根据$condition中设置的d_source获取请求参数值赋值给$data,并根据$condition设置的其他内容对$data进行数据验证,具体操作可以看下代码中注释。在函数最末尾,将验证后数据$data传入escape_string()中,继续跟进此函数。

function filter($varname, $condition)
{
    // 变量名称文本
    if (array_key_exists($varname, $condition) && $condition[$varname]) {
        $vartext = $condition[$varname];
    } else {
        $vartext = $varname;
    }
    
    // 数据源
    if (array_key_exists('d_source', $condition)) {
        switch ($condition['d_source']) {
            case 'post':
                $data = @$_POST[$varname];
                break;
            case 'get':
                $data = @$_GET[$varname];
                break;
            case 'cookie':
                $data = @$_COOKIE[$varname];
                break;
            case 'session':
                $data = session($varname);
                break;
            case 'both':
                $data = @$_POST[$varname] ?: @$_GET[$varname];
                break;
            case 'string':
                $data = $varname;
            default:
                error($vartext . '数据获取方式设置错误!');
        }
        // 去空格
        if (is_string($data))
            $data = trim($data);
    } else {
        $data = $varname; // 没有数据源指定时直接按照字符串过滤处理
    }
    
    // 数据为空时,进行是否允许空检测
    if (! $data && array_key_exists('d_none', $condition) && $condition['d_none'] === false) {
        error($vartext . '不能为空!');
    }
    
    // 判断是否强制检测,为true时,意味着如果数据不满足要求直接报错,否则返回null
    if (array_key_exists('d_require', $condition) && $condition['d_require'] == true) {
        $require = true;
    } else {
        $require = false;
    }
    
    // 数据类型检测
    if (array_key_exists('d_type', $condition)) {
        switch ($condition['d_type']) {
            case 'int':
                if (! preg_match('/^[0-9]+$/', $data)) {
                    $err = '必须为整数!';
                }
                break;
            case 'float':
                if (! is_float($data)) {
                    $err = '必须为浮点数!';
                }
                break;
            case 'num':
                if (! is_numeric($data)) {
                    $err = '必须为数字!';
                }
                break;
            case 'letter':
                if (! preg_match('/^[a-zA-Z]+$/', $data)) {
                    $err = '只能包含字母!';
                }
                break;
            case 'var':
                if (! preg_match('/^[\w\-\.]+$/', $data)) {
                    $err = '只能包含字母、数字、划线、点!';
                }
                break;
            case 'bool':
                if (! is_bool($data)) {
                    $err = '必须为布尔类型!';
                }
                break;
            case 'date':
                if (! strtotime($data)) {
                    $err = '必须为日期类型!';
                }
                break;
            case 'array':
                if (! is_array($data)) {
                    $err = '必须为数组类型!';
                }
                break;
            case 'object':
                if (! is_object($data)) {
                    $err = '必须为对象类型!';
                }
                break;
            case 'vars':
                if (! preg_match('/^[\x{4e00}-\x{9fa5}\w\-\.,\s]+$/u', $data)) {
                    $err = '只能包含中文、字母、数字、横线、点、逗号、空格!';
                }
                break;
            default:
                if ($condition['d_type'])
                    error($vartext . '数据类型设置错误!');
        }
    }
    
    // 非必须或必须但无错误时执行
    if ((! $require || ($require && ! isset($err)))) {
        
        // 正则匹配
        if (array_key_exists('d_regular', $condition)) {
            if (! preg_match($condition['d_regular'], $data)) {
                $err = '不符合正则表达式规则!';
            }
        }
        // 最大值匹配
        if (array_key_exists('d_max', $condition)) {
            if (is_numeric($data)) {
                if ($data > $condition['d_max']) {
                    $err = '不能大于' . $condition['d_max'];
                }
            } else {
                if (mb_strlen($data) > $condition['d_max']) {
                    $err = '长度不能大于' . $condition['d_max'];
                }
            }
        }
        // 最小值匹配
        if (array_key_exists('d_min', $condition)) {
            if (is_numeric($data)) {
                if ($data < $condition['d_min']) {
                    $err = '不能小于' . $condition['d_min'];
                }
            } else {
                if (mb_strlen($data) < $condition['d_min']) {
                    $err = '长度不能小于' . $condition['d_min'];
                }
            }
        }
    }
    
    // 如果为必须且有错误,则显示错误,如果非必须,但有错误,则设置数据为null
    if ($require && isset($err)) {
        error($vartext . $err);
    } elseif (isset($err)) {
        $data = null;
    }
    
    // 如果设置有默认值,默认值
    if (array_key_exists('d_default', $condition)) {
        $data = (! is_null($data)) ? $data : $condition['d_default'];
    }
    
    // 去空格
    if (is_string($data)) {
        $data = trim($data);
    }
    
    // 销毁错误
    unset($err);
    
    // 返回收据
    return escape_string($data);
}

escape_string():
escape_string()主要就是针对在filter()中验证后的数据进行转义处理。如果是数组就遍历数组中元素一一转移,如果是对象就遍历对象中的公共成员变量后转义。使用的处理函数htmlspecialchars、addslashes,可以说能直接把你的payload转的面目前非了。

推荐阅读