YCCMS代码审计(新手教学方向)

前言

在逛CNVD时发现这款CMS存在不少常见的漏洞,并且看样子漏洞没有修复,一时好奇就下载下来看了看。经过简单的分析发现该CMS触发漏洞的方式挺常见的,正好可以从代码方面
分析一下这些漏洞的成因,加深对一些常见漏洞的理解
CMS下载地址: http://ahdx.down.chinaz.com/202003/yccms_v3.4.rar

基础知识

该CMS采用的是MVC框架,MVC全名是Model View Controller,是模型(model)-视图(view)-控制器(controller)的缩写。用一种业务逻辑、数据、界面显示分离的方法组织代码,将业
务逻辑聚集到一个部件里面,在改进和个性化定制界面及用户交互的同时,不需要重新编写业务逻辑。controller文件夹存放控制器文件,view文件夹存放视图文件,model文件夹存放数据文件

未授权更改管理员账号密码

首先来看一下漏洞利用过程,在未登录的情况下构造url,只需要更改username password notpassword的值即可更改数据库中admin账号的相关信息

去数据库中查看发现已经更改了账号密码

根据url来定位一下漏洞函数,函数位于controllerAdminAction.class.php中的update函数

public function update(){
        if(isset($_POST['send'])){
            if(validate::isNullString($_POST['username'])) Tool::t_back('用户名不能为空','?a=admin&m=update');
            if(validate::isNullString($_POST['password'])) Tool::t_back('密码不能为空!','?a=admin&m=update');
            if(!(validate::checkStrEquals($_POST['password'], $_POST['notpassword']))) Tool::t_back('两次密码不一致!','?a=admin&m=update');
            $this->_model->username=$_POST['username'];
            $this->_model->password=sha1($_POST['password']);
            $_edit=$this->_model->editAdmin();
            if($_edit){
                tool::layer_alert('密码修改成功!','?a=admin&m=update',6);
                }else{
                tool::layer_alert('密码未修改!','?a=admin&m=update',6);
            }
        }

            $this->_tpl->assign('admin', $_SESSION['admin']);
            $this->_tpl->display('admin/public/update.tpl');
    }

可以看到前面都是一些判断,重点关注下editAdmin()函数,该函数位于modelAdminModel.class.php

public function editAdmin(){
        $_sql="UPDATE
                    my_admin
                SET
                    username='$this->username',
                    password='$this->password'
                WHERE
                    id=1
                LIMIT 1";
        return parent::update($_sql);
    }

该函数的父类为Model, 位于modelModel.class.php,看一下update函数

protected function update($_sql){
        return $this->execute($_sql)->rowCount();
    }

调用execute函数去执行sql语句

protected function execute($_sql){
        try{
            $_stmt=$this->_db->prepare($_sql);
            $_stmt->execute();
        }catch (PDOException $e){
            exit('SQL语句:'.$_sql.'<br />错误信息:'.$e->getMessage());
        }
        return $_stmt;
    }
}

这一系列的操作主要是用来生成SQL语句然后执行SQL语句,editAdmin函数直接把传进来的username password拼接到sql语句中,然后去更新相关表中id=1的数据,这也就造成了任意更改管理员账号密码

任意文件删除

POST /admin/?a=pic&m=delall HTTP/1.1
Host: 127.0.0.1:8082
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:69.0) Gecko/20100101 Firefox/69.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Content-Type: application/x-www-form-urlencoded
Content-Length: 89
Origin: http://127.0.0.1:8082
Connection: close
Referer: http://127.0.0.1:8082/admin/?a=pic
Upgrade-Insecure-Requests: 1

pid%5B0%5D=../1.txt&chkall=on&send=%E5%88%A0%E9%99%A4%E9%80%89%E4%B8%AD%E5%9B%BE%E7%89%87t

只需要更改pid[0]即可在无登录条件下任意删除文件,删除根目录下的1.txt

已经删除成功了

其实这还是犯了一个最容易犯的错误,没有对传进来的路径进行过滤就拼接了目录,导致了任意文件删除漏洞的产生
根据url定位到相关函数位置,位于/controller/PicAction.class.php

public function delall(){
        if(isset($_POST['send'])){
            if(validate::isNullString($_POST['pid'])) tool::layer_alert('没有选择任何图片!','?a=pic',7);
            $_fileDir=ROOT_PATH.'/uploads/';
            foreach($_POST['pid'] as $_value){
                $_filePath=$_fileDir.$_value;
                if(!unlink($_filePath)){
                    tool::layer_alert('图片删除失败,请设权限为777!','?a=pic',7);
                }else{
                    header('Location:?a=pic');
                }
            }

        }

    }

对 pid传进来的值并没有进行过滤就进行了了路径的拼接,导致了路径穿越漏洞,触发任意文件删除漏洞

任意文件上传漏洞一

在不需要登录的情况上传成功

定位到漏洞位置: controllerCallAction.class.php

public function upLoad() {
        if (isset($_POST['send'])) {
            $_logoupload = new LogoUpload('pic',$_POST['MAX_FILE_SIZE']);
            $_path = $_logoupload->getPath();
            $_img = new Image($_path);
            $_img->xhImg(960,0);
            $_img->out();
            //echo $_path;
            $_logoupload->alertOpenerClose('图片上传成功!','..'.$_path);
        } else {
            exit('警告:文件过大或者其他未知错误导致浏览器崩溃!');
        }
    }

然后跟进到类LogoUpload ,位于publicclassLogoUpload.class.php,上传首要关注上传是是否允许上传非图片格式的文件

private function checkType() {
        if (!in_array($this->type,$this->typeArr)) {
            Tool::alertBack('警告:LOGO图片必须是PNG格式!');
        }
    }

private $typeArr = array('image/png','image/x-png');//类型合集

根据Content-Type的值来判断是否是图片格式,只要Content-Type是这两种类型就可以,那直接伪造Content-Type就可以了

任意文件上传二


在不需要登录的情况下可以看到已经上传成功,上传地址为E:/phpstudy/WWW/yccms/uploads/20200509133351770.php
定位漏洞位置为controllerCallAction.class.php中的xhUp函数

public function xhUp() {
        if (isset($_GET['type'])) {
            $_fileupload = new FileUpload('filedata',10);
            $_err=$_fileupload->checkError();
            $_path = $_fileupload->getPath();
            $_msg="'..$_path'";
            $_img = new Image($_path);
            $_img->xhImg(650,0);
            $_img->out();
            echo "{'err':'".$_err."','msg':".$_msg."}";
            exit();
        } else {
        Tool::alertBack('警告:由于非法操作导致上传失败!');
        }
    }

跟进到类FileUpload, 位于publicclassFileUpload.class.php,然后看到同样也是检查的传入的Content-Type的值

private function checkType() {
        if (!in_array($this->type,$this->typeArr)) {
            Tool::alertBack('警告:不合法的上传类型!');
        }
    }

private $typeArr = 
array('image/jpeg','image/pjpeg','image/png','image/x-png','image/gif');

后记

当然该cms还存在登录处验证码重放漏洞 反射型XSS漏洞

XSS payload http:xxx.xxx/admin/?a=html&art=<sCrIpT>alert(1)<%2FsCrIpT>&m=arts

对于任意文件删除漏洞,可以通过在拼接路径时先进行正则匹配过滤,或者通过加密的ID去删除,不要直接拼接路径
对于任意文件上传漏洞可以更换后缀的验证方式为白名单的验证方式
对于任意篡改管理员账号密码可以对传入的用户名进行校验是否是当前用户
对于上述所有的操作都要进行权限访问控制,不要进行未授权的操作

原文地址:https://www.cnblogs.com/0daybug/p/12932677.html