CSZ CMS 1.2.7 xss分析与复现

2020-05-21 约 697 字 预计阅读 4 分钟

声明:本文 【CSZ CMS 1.2.7 xss分析与复现】 由作者 cru**** 于 2020-05-21 08:57:13 首发 先知社区 曾经 浏览数 99 次

感谢 cru**** 的辛苦付出!

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/

关键词:[‘安全技术’, ‘漏洞分析’]


author

旭达网络

旭达网络技术博客,曾记录各种技术问题,一贴搞定.
本文采用知识共享署名 4.0 国际许可协议进行许可。

We notice you're using an adblocker. If you like our webite please keep us running by whitelisting this site in your ad blocker. We’re serving quality, related ads only. Thank you!

I've whitelisted your website.

Not now