Yunucms代码审计:后台XSS和数据库信息泄露

2020-02-10 约 462 字 预计阅读 3 分钟

声明:本文 【Yunucms代码审计:后台XSS和数据库信息泄露】 由作者 Forthrglory 于 2020-02-10 08:50:50 首发 先知社区 曾经 浏览数 245 次

感谢 Forthrglory 的辛苦付出!

最近在审计yunucms,审计了几个后台漏洞,有XSS和文件泄露几个。

yunucms介绍

云优CMS是一款基于TP5.0框架为核心开发的一套免费+开源的城市分站内容管理系统。云优CMS前身为远航CMS。云优CMS于2017年9月上线全新版本,二级域名分站,内容分站独立,七牛云存储,自定义字段,自定义表单,自定义栏目权限,自定义管理权限等众多功能深受用户青睐。

建站

官网下载源码并进行过安装

需要注意的是需要填云账号,我去官网注册了一个随便填上了,账号testqwe,密码123456,手机号利用的在线短信注册的

填上MySQL密码即可

前台界面

漏洞复现

XSS

漏洞测试

后台TAG管理模块

进行添加TAG

在名称处填入XSS代码并提交

返回模块即可看到效果

查看源码,发现已经插入

查看数据库

代码审计

http://127.0.0.1/index.php?s=/admin/tagurl/addtagurl

该cms路由为目录/文件/方法,直接查看方法

public function addTagurl()
    {
        if(request()->isAjax()){ # 判断是否是ajax请求
            $param = input('post.'); # 获取参数
            $tagurl = new TagurlModel();
            $flag = $tagurl->insertTagurl($param); # 将结果进行保存并返回响应
            return json(['code' => $flag['code'], 'data' => $flag['data'], 'msg' => $flag['msg']]);
        }
        return $this->fetch();
    }

跟进insertTagurl方法

public function insertTagurl($param)
    {
        try{
            $result = $this->allowField(true)->save($param); # 保存当前数据对象
            if(false === $result){            
                return ['code' => -1, 'data' => '', 'msg' => $this->getError()];
            }else{
                return ['code' => 1, 'data' => '', 'msg' => '添加TAG成功'];
            }
        }catch( PDOException $e){
            return ['code' => -2, 'data' => '', 'msg' => $e->getMessage()];
        }
    }

继续跟进save方法

if (!empty($data)) {
            // 数据自动验证
            if (!$this->validateData($data)) { # 验证集为空,直接返回true
                return false;
            }
            // 数据对象赋值
            foreach ($data as $key => $value) {
                $this->setAttr($key, $value, $data); # 将参数赋值给$this->data数组
            }
            if (!empty($where)) {
                $this->isUpdate = true;
            }
        }

......        

$result = $this->getQuery()->insert($this->data);

......
``

validateData方法需要验证集,而本身没有传入

protected function validateData($data, $rule = null, $batch = null)
    {
        $info = is_null($rule) ? $this->validate : $rule;

        if (!empty($info)) {
            ......
        }
        return true;
    }

$this->validate参数为空,因此直接返回true

跟进insert方法

.....
        // 生成SQL语句
        $sql = $this->builder->insert($data, $options, $replace);
        $bind = $this->getBind();
        if ($options['fetch_sql']) {
            // 获取实际执行的SQL语句
            return $this->connection->getRealSql($sql, $bind);
        }

        // 执行操作
        $result = $this->execute($sql, $bind);

fetch_sql变量为false,跟进execute方法

......
    if ($procedure) { # false
                $this->bindParam($bind);
            } else {
                $this->bindValue($bind);
            }
......

最后跟进参数绑定方法

protected function bindValue(array $bind = [])
    {
        foreach ($bind as $key => $val) {
            // 占位符
            $param = is_numeric($key) ? $key + 1 : ':' . $key;
            if (is_array($val)) {
                if (PDO::PARAM_INT == $val[1] && '' === $val[0]) {
                    $val[0] = 0;
                }
                $result = $this->PDOStatement->bindValue($param, $val[0], $val[1]);
            } else {
                $result = $this->PDOStatement->bindValue($param, $val);
            }
            if (!$result) {
                throw new BindParamException(
                    "Error occurred  when binding parameters '{$param}'",
                    $this->config,
                    $this->getLastsql(),
                    $bind
                );
            }
        }
    }

可以看到最后是调用PDO对象对参数进行的绑定,除此之外并没有任何过滤,因此XSS代码可插入并执行

数据库泄露

漏洞测试

在后台系统管理->数据库管理模块将所有数据库备份

查看本地文件,所有备份保存在data目录下,发现名命是以时间命名,可以直接爆破得到

从前台访问并下载

下载完成后打开,泄露所有数据库信息

代码审计

POST /index.php?s=/admin/data/export HTTP/1.1

public function export($ids = null, $id = null, $start = null) {
        $Request = Request::instance();
        if ($Request->isPost() && !empty($ids) && is_array($ids)) { //初始化
            $path = config('data_backup_path');
            is_dir($path) || mkdir($path, 755, true);
            //读取备份配置
            $config = [
                'path' => realpath($path) . DIRECTORY_SEPARATOR,
                'part' => config('data_backup_part_size'),
                'compress' => config('data_backup_compress'),
                'level' => config('data_backup_compress_level'),
            ];

            //检查是否有正在执行的任务
            $lock = "{$config['path']}backup.lock";
            if (is_file($lock)) {
                return $this->error('检测到有一个备份任务正在执行,请稍后再试,或手动删除"'.$lock.'"后重试!');
            }
            file_put_contents($lock, $Request->time()); //创建锁文件
            //检查备份目录是否可写
            is_writeable($config['path']) || $this->error('备份目录不存在或不可写,请检查后重试!');
            session('backup_config', $config);

            //生成备份文件信息
            $file = [
                'name' => date('Ymd-His', $Request->time()),
                'part' => 1,
            ];
            session('backup_file', $file);
            //缓存要备份的表
            session('backup_tables', $ids);

            //创建备份文件
            $Database = new \com\Database($file, $config);
            if (false !== $Database->create()) {
                $tab = ['id' => 0, 'start' => 0];
                return $this->success('初始化成功!', '', ['tables' => $ids, 'tab' => $tab]);
            } else {
                return $this->error('初始化失败,备份文件创建失败!');
            }
        }
......

可以看到,备份文件的命名用的time方法,跟进

public function time($float = false)
    {
        return $float ? $_SERVER['REQUEST_TIME_FLOAT'] : $_SERVER['REQUEST_TIME'];
    }

可以看到利用REQUEST_TIME进行构造文件名,因此可以直接爆破得到并下载。

结束语

后台漏洞现在不太受待见,总觉得后台都进不去所以后台漏洞用处不大,不过后台漏洞更多的是利用组合拳。先拿到后台权限或者直接越权,拿下主机也是迟早的事。后台漏洞同样不可小觑。

文中如果有什么错误的地方还请各位师傅指教。

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


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