记录黑客技术中优秀的内容,传播黑客文化,分享黑客技术精华

从mcms历史漏洞中捡漏

2022-09-14 03:45

前言

这段时间忙于工作,无法自拔~~~

上周末刚好有空,随便逛了一下java开源cms,发现一个star挺多的mcms,就看了一下issues,发现了两个比较有意思的地方(主要感觉问题没有修复完全),写出来请大伙指点指点。

分析

毕竟水平太次,只能写点简单的东西了~~~

后台模版管理

该cms开发上使用了Freemarker框架,在一些历史版本中,也存在很多模版注入的问题。

这些问题大部分是通过后台模版管理模块可上传zip文件进行自解压或者可修改模版htm文件插入payload达到目的。

  • 上传zip文件解压:

    net.mingsoft.basic.action.ManageFileAction#uploadTemplate:

    跟进uploadTemplate:

    public ResultData uploadTemplate(BaseFileAction.Config config) throws IOException {
    String[] errorType = this.uploadFileDenied.split(",");
    String fileName = config.getFile().getOriginalFilename();
    if (fileName.lastIndexOf(".") < 0) {
    this.LOG.info("文件格式错误:{}", fileName);
    return ResultData.build().error(this.getResString("err.error", new String[]{this.getResString("file.name")}));
    } else {
    String fileType = fileName.substring(fileName.lastIndexOf("."));
    boolean isReal = (new File(this.uploadTemplatePath)).isAbsolute();
    String realPath = null;
    if (!isReal) {
    realPath = BasicUtil.getRealPath("");
    } else {
    realPath = this.uploadTemplatePath;
    }

    if (!config.isRename()) {
    fileName = config.getFile().getOriginalFilename();
    if (fileName.endsWith(".") && System.getProperty("os.name").startsWith("Windows")) {
    this.LOG.info("文件类型被拒绝:{}", fileName);
    return ResultData.build().error(this.getResString("err.error", new String[]{this.getResString("file.type")}));
    }

    fileType = fileName.substring(fileName.lastIndexOf("."));
    } else {
    fileName = System.currentTimeMillis() + fileType;
    }

    String[] var7 = errorType;
    int var8 = errorType.length;

    String path;
    for(int var9 = 0; var9 < var8; ++var9) {
    path = var7[var9];
    if (fileType.equalsIgnoreCase(path)) {
    this.LOG.info("文件类型被拒绝:{}", fileType);
    return ResultData.build().error(this.getResString("err.error", new String[]{this.getResString("file.type")}));
    }
    }

    String uploadFolder = realPath + File.separator;
    if (StringUtils.isNotBlank(config.getUploadPath())) {
    uploadFolder = uploadFolder + config.getUploadPath() + File.separator;
    }

    File saveFolder = new File(uploadFolder);
    File saveFile = new File(uploadFolder, fileName);
    if (!saveFolder.exists()) {
    FileUtil.mkdir(saveFolder);
    }

    config.getFile().transferTo(saveFile);
    path = uploadFolder.replace(realPath, "") + "/" + fileName;
    return ResultData.build().success((new File("/" + path)).getPath().replace("\\", "/").replace("//", "/"));
    }
    }

    这里有个黑名单的判断。通过String[] errorType = this.uploadFileDenied.split(",");和注解拿到黑名单

    application.yml

    不能为exe jsp jspx sh 等类型文件,然后通过unZip接口解压:

    net.mingsoft.basic.action.TemplateAction#unZip:

    这里主要手法就是上传包含payload的模板文件,然后设置进行引用。

  • 修改模板文件:

    同样后台提供了net.mingsoft.basic.action.TemplateAction#writeFileContent去写内容:

    通过该接口,在一些历史版本中同样可以写入模板注入payload。

  • 思考

    到这里,如果是以tomcat部署war的形式,是否可以通过上传zip(war)自解压到上级目录getshell

    在5.2.1版本中,进一步跟进net.mingsoft.basic.action.TemplateAction#unZip:

    调试后发现最后会通过cn.hutool.core.io.FileUtil#checkSlip判断解压文件位置是否跳出父目录,我们通过../进行路径穿越的话显然是会报错:

    这样看的话,这个整个应用中应该不存在这个问题,但是当我git下最新版本(5.2.8)后却发现这里的unzip没有调用该方法,那么说明在最新版本中是可以利用的。

    private void unzip(File zipFile, String descDir) throws IOException {
    ZipArchiveInputStream inputStream = new ZipArchiveInputStream(new BufferedInputStream(new FileInputStream(zipFile)));
    File pathFile = new File(descDir);
    if (!pathFile.exists()) {
    pathFile.mkdirs();
    }

    ZipArchiveEntry entry = null;

    while((entry = inputStream.getNextZipEntry()) != null) {
    String[] dirs = entry.getName().split("/");
    String tempDir = descDir;
    String[] var8 = dirs;
    int var9 = dirs.length;

    for(int var10 = 0; var10 < var9; ++var10) {
    String dir = var8[var10];
    if (dir.indexOf(".") == -1) {
    tempDir = tempDir + File.separator.concat(dir);
    FileUtil.mkdir(tempDir);
    }
    }

    if (entry.isDirectory()) {
    File directory = new File(descDir, entry.getName());
    directory.mkdirs();
    } else {
    BufferedOutputStream os = null;

    try {
    this.LOG.debug("file name => {}", entry.getName());

    try {
    os = new BufferedOutputStream(new FileOutputStream(new File(descDir, entry.getName())));
    IOUtils.copy(inputStream, os);
    } catch (FileNotFoundException var15) {
    this.LOG.error("模版解压{}不存在", entry.getName());
    var15.printStackTrace();
    }
    } finally {
    IOUtils.closeQuietly(os);
    }
    }
    }

    }

    将整个项目打包成war部署:

    构造zip文件:

    通过后台上传,成功解压到webapps目录:

    getshell:

    其实这算是常规方法,很多类似应用都存在同样问题。

修改ueditor配置上传

5.2.1版本中存在net.mingsoft.basic.action.web.EditorAction#editor一个前台的接口:

跟进com.mingsoft.ueditor.MsUeditorActionEnter:

这个逻辑很简单,将我们传入jsonconfig写入获取的jsonobject中,这里的jsonobject实际上就是static/plugins/ueditor/1.4.3.3/jsp/config.json:

接着执行com.baidu.ueditor.ActionEnter#exec:

跳到com.baidu.ueditor.ActionEnter#invoke:

这里this.actionType通过传参action得到,可以控制要执行的动作:

actioncode为1、2、3、4对应的都是上传:

跟进case:

conf = this.configManager.getConfig(actionCode);//获取配置
state = (new Uploader(this.request, conf)).doExec();//执行上传

com.baidu.ueditor.ConfigManager#getConfig:

看到这里,基本上就可以干很多事了,那么新版本中也是进行了修复:

使用cn.hutool.core.io.FileUtil#normalize对三个路径进行了修复。但是上传的文件后缀还是可以控制的,在特殊情况下也可以通过jspx而getshell。(至于为什么只对路径进行修复,我想应该是该issue提交的师傅原来利用该缺陷通过修改上传地址上传了一个包含freemarker的payload从而进行模板执行,那么估计人家想的就是控制路径了吧)

在项目(最新版本)以war包部署在tomcat时:

总结

怎么说呢,不管怎样,还是学习比上班要快乐~~~~

参考

https://gitee.com/mingSoft/MCMS/issues/I4QZ1O
https://gitee.com/mingSoft/MCMS/issues/I4Q4NV


知识来源: https://xz.aliyun.com/t/11695

阅读:411896 | 评论:0 | 标签:cms 漏洞

想收藏或者和大家分享这篇好文章→复制链接地址

“从mcms历史漏洞中捡漏”共有0条留言

发表评论

姓名:

邮箱:

网址:

验证码:

黑帝公告 📢

十年经营持续更新精选优质黑客技术文章Hackdig,帮你成为掌握黑客技术的英雄

🙇🧎¥由自富财,长成起一↓

❤用费0款退球星,年1期效有员会

标签云 ☁