CSZ CMS 1.2.7 xss分析与复现

CSZ CMS 1.2.7 xss分析与复现

简介

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

CSZ CMS基于服务器端脚本语言PHP,并使用MySQL或MariaDB数据库进行数据存储。CSZ CMS是开源的内容管理系统。

漏洞概述

拥有访问私有消息的未授权用户可以向管理面板嵌入Javascript代码。

影响版本

1.2.7

环境搭建

下载地址:https://sourceforge.net/projects/cszcms/files/install/CSZCMS-V1.2.7.zip/download

按照指示安装即可

漏洞复现

新建一个用户

点击inbox发送私信,选定管理员用户


修改User-Agent为<script>alert(1)</script>

管理员登陆后台即触发xss

分析

查看数据库中的email_logs可知,将访问的user-agent存储到了数据库中

我们首先观察路由,漏洞点在/member/insertpm页面,查看控制器,找到cszcms/controllers/Member.php


找到insertpm方法:


关注点在下半部分:

$this->input->post即是调用的system/core/Input.php的post方法:


当$xss_clean的参数设置为true,则会进行xss过滤,这也是为什么发送信息处并没有出现xss

继续看,$this->Csz_auth_model->send_pm方法位于cszcms/models/Csz_auth_model.php中的send_pm():

/**
     * Send multiple Private Messages
     * Send multiple private messages to another users
     * 
     * @param array $receiver_ids Array of User ids of private message receiver 
     * @param string $title Title/subject
     * @param string $message Message
     * @param int $sender_id User id of private message sender
     * @param string $re_message Reply the original message
     * 
     * @return array/bool Array with User ID's as key and TRUE or a specific error message OR FALSE if sender doesn't exist
     */
    public function send_pm($receiver_ids, $title, $message, $sender_id = '', $re_message = '') {
        if (!$sender_id) {
            $sender_id = $this->session->userdata('user_admin_id');
        }
        if ($sender_id && (!$this->is_useractive($sender_id))) {
            return FALSE;
        }else{
            if ($receiver_ids && is_numeric($receiver_ids) && $sender_id != $receiver_ids) {
                if($re_message){ 
                    $message = '{[' . str_replace("\r\n" . "\r\n", "\r\n", $re_message) . "]} " . "\r\n" . "\r\n" . $message;
                }
                $data = array(
                    'sender_id' => $sender_id,
                    'receiver_id' => $receiver_ids,
                    'title' => $title,
                    'message' => $message,
                    'date_sent' => date('Y-m-d H:i:s')
                );
                $this->db->insert('user_pms', $data);
                $sender_user = $this->Csz_admin_model->getUser($sender_id);
                $receive_user = $this->Csz_admin_model->getUser($receiver_ids);
                if($receive_user->pm_sendmail == '1'){
                    $config = $this->Csz_model->load_config();
                    $message_html = 'Dear ' . $receive_user->name . ',<br><br>' . $message . '<br><br>Best Regards,<br>'.$sender_user->name;
                    @$this->Csz_model->sendEmail($receive_user->email, '[PM] ' . $title . ' ('.$config->site_name.')', $message_html, $sender_user->email, $sender_user->name);
                }
                return TRUE;
            }else{
                return FALSE;
            }
        }
    }

调用了@$this->Csz_model->sendEmail方法,位于cszcms/models/Csz_model.php,我们继续跟进:

public function sendEmail($to_email, $subject, $message, $from_email, $from_name = '', $bcc = '', $reply_to = '', $alt_message = '', $attach_file = array(), $save_log = TRUE) {
        $this->load->library('email');
        $load_conf = $this->load_config();
        $protocal = $load_conf->email_protocal;
        if (!$protocal) {
            $protocal = 'mail';
        }
        $config = array();
        $config['useragent'] = $this->Csz_admin_model->cszGenerateMeta();
        $config['protocol'] = $protocal;  /* mail, sendmail, smtp */
        if ($protocal == 'smtp') {
            $config['smtp_host'] = $load_conf->smtp_host;
            $config['smtp_user'] = $load_conf->smtp_user;
            $config['smtp_pass'] = $load_conf->smtp_pass;
            $config['smtp_port'] = $load_conf->smtp_port;
        } else if ($protocal == 'sendmail' && $load_conf->sendmail_path) {
            $config['mailpath'] = $load_conf->sendmail_path;
        }
        $config['mailtype'] = 'html';
        $config['charset'] = 'utf-8';
        $config['wordwrap'] = TRUE;
        $this->email->initialize($config);
        $this->email->set_newline("\r\n");
        $this->email->from($from_email, $from_name); // change it to yours
        $this->email->to($to_email); // change it to yours
        $this->email->subject($subject);
        $this->email->message($message);
        if ($bcc) {
            $this->email->bcc($bcc);
        }
        if($reply_to){
            $this->email->reply_to($reply_to);
        }
        if ($alt_message) {
            $this->email->set_alt_message($alt_message);
        }
        if (is_array($attach_file) && !empty($attach_file)) {
            foreach ($attach_file as $value) {
                $this->email->attach($value, 'attachment');
            }
        }
        if ($this->email->send()) {
            $result = 'success';
        } else {
            $result = $this->email->print_debugger(FALSE);
        }
        if($save_log === TRUE && $load_conf->email_logs == 1){
            $data = array(
                'to_email' => $to_email,
                'from_email' => $from_email,
                'from_name' => $from_name,
                'subject' => $subject,
                'message' => $message,
                'email_result' => $result,
            );
            $this->db->set('user_agent', $this->input->user_agent(), TRUE);
            $this->db->set('ip_address', $this->input->ip_address(), TRUE);
            $this->db->set('timestamp_create', $this->timeNow(), TRUE);
            $this->db->insert('email_logs', $data);
            $this->db->cache_delete_all();
            unset($data);
        }
        unset($to_email, $subject, $message, $from_email, $from_name, $bcc, $reply_to, $alt_message, $attach_file, $save_log, $config, $load_conf, $protocal);
        return $result;
    }

关键就在于后面的数据库操作:

$this->db->set('user_agent', $this->input->user_agent(), TRUE);
        $this->db->set('ip_address', $this->input->ip_address(), TRUE);
        $this->db->set('timestamp_create', $this->timeNow(), TRUE);
        $this->db->insert('email_logs', $data);
        $this->db->cache_delete_all();

$this->db->set方法位于:system/database/DB_query_builder.php:

/**
 * The "set" function.
 *
 * Allows key/value pairs to be set for inserting or updating
 *
 * @param   mixed
 * @param   string
 * @param   bool
 * @return  CI_DB_query_builder
 */
public function set($key, $value = '', $escape = NULL)
{
    $key = $this->_object_to_array($key);

    if ( ! is_array($key))
    {
        $key = array($key => $value);
    }

    is_bool($escape) OR $escape = $this->_protect_identifiers;

    foreach ($key as $k => $v)
    {
        $this->qb_set[$this->protect_identifiers($k, FALSE, $escape)] = ($escape)
            ? $this->escape($v) : $v;
    }

    return $this;
}

简单理解即为插入或更新值作初始化

继续,$this->input->user_agent()位于:system/core/Input.php,其具体实现如下:

/**
 * Fetch User Agent string
 *
 * @return  string|null User Agent string or NULL if it doesn't exist
 */
public function user_agent($xss_clean = NULL)
{
    return $this->_fetch_from_array($_SERVER, 'HTTP_USER_AGENT', $xss_clean);
}

这里的xss过滤操作默认是null,也就是没有过滤

这就是问题所在,我们即可以通过篡改user_agent的值实现xss,往下看,$this->db->insert('email_logs', $data)即向emali_logs表插入数据,$this->db->insert实现如下(system/core/Input.php):

/**
     * Insert
     *
     * Compiles an insert string and runs the query
     *
     * @param   string  the table to insert data into
     * @param   array   an associative array of insert values
     * @param   bool    $escape Whether to escape values and identifiers
     * @return  bool    TRUE on success, FALSE on failure
     */
    public function insert($table = '', $set = NULL, $escape = NULL)
    {
        if ($set !== NULL)
        {
            $this->set($set, '', $escape);
        }

        if ($this->_validate_insert($table) === FALSE)
        {
            return FALSE;
        }

        $sql = $this->_insert(
            $this->protect_identifiers(
                $this->qb_from[0], TRUE, $escape, FALSE
            ),
            array_keys($this->qb_set),
            array_values($this->qb_set)
        );

        $this->_reset_write();
        return $this->query($sql);
    }

依然没有任何过滤

然而,关于为什么登入后台即会弹窗,搜索一番后发现,开发者直接将其echo出来(cszcms/views/admin/home.php):

修复

在1.2.8中删除了相关代码(cszcms/views/admin/home.php):

参考链接:

CI框架相关:https://www.cnblogs.com/lazycat-cz/p/4315453.html

cszcms开发文档:https://docs.cszcms.com/

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

相关推荐

How to hook Android Native methods with Frida (Noob Friendly)

原文地址:https://erev0s.com/blog/how-hook-android-native-methods-frida-noob-friendly/ 在上一篇文章中,我们以Android应用程序为例,并假设我们想要使用C/C+

CVE-2020-0932:使用TYPECONVERTERS在MICROSOFT SHAREPOINT上执行远程代码

来源:https://www.zerodayinitiative.com/blog/2020/4/28/cve-2020-0932-remote-code-execution-on-microsoft-sharepoint-using-ty

域控提权合集

0x01、前言 菜鸡一枚,标题起的可能有点大,只是个人笔记整理的一个合集(所以基本每个例子都会有实例)。所以虽然说是合集,可能都没有囊括到各位大佬会的一半。还请各位大佬轻喷 0x02、目录 GPP和SYSVOL中的密码 MS14-068 D

从0学习WebLogic CVE-2020-2551漏洞

最近遇到的实际环境为weblogic,所以这里顺便总结下测2020-2551的一些相关的点 2551也是学习了很多优秀的师傅文章,非常感谢,个人水平较差、文中错误内容还请师傅们指教纠正。 0X00 漏洞利用基础学习 从0开始学习复现这个洞不

一次稍显曲折的爆破经历

拿到手域名一个 cc.test.com 打开后直接就是一个登陆框 随手输入admin准备打密码,提示管理员不存在 说明此处可以爆破用户名,使用字典爆破得到三个用户名 test wangw wangy 登陆抓包密码被md5加密了 使用多账号爆

蚁剑改造过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

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

挖出来之后看了下官网发现不到半个月之前更新了最新版,下下来之后发现这洞修了..我吐了 干脆直接发出来 分享下思路吧 0x00漏洞分析 漏洞发生在PbootCMS内核的模板解析函数中 为了方便看直接上一个完整的Parser代码吧 public

SaltStack CVE-2020-11651/11652 分析

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