JEECG代码审计之文件上传
JEECG代码审计之文件上传
0x01 简述
JEECG(J2EE Code Generation)是一款基于代码生成器JEE的智能开发平台。引领新的开发模式(Online Coding->代码生成器->手工MERGE智能开发),可以帮助解决Java项目90%的重复工作,让开发更多关注业务逻辑。既能快速提高开发效率,帮助公司节省人力成本,同时又不失灵活性。
0x02 环境搭建
mysql5.7
idea、java7、tomcat7
jeecg3.8:https://github.com/chen-tj/jeecg3.8
- 
下载源码,导入maven项目,刷新maven下载依赖  
- 
导入数据库,配置数据库文件(需要手动创建jeecg数据库) 

- 
配置tomcat启动  


0x03 路由简介
JEECG快速开发平台基于spring MVC 框架
@Controller将一个类声明为控制器类,再通过@RequestMapping配置路由映射。
简单举例说明:
项目中src/main/java/com/jeecg/demo/controller/MultiUploadController.java文件
@RequestMapping("/multiUploadController")
	@RequestMapping(params = "list")
对应的url地址为:http://localhost:8080/multiUploadController.do?list

访问http://localhost:8080/multiUploadController.do?list

RequestMapping的params参数: 指定request中必须包含某些参数值时,才让该方法处理。
因此访问:
http://localhost:8080/multiUploadController.do?list和
http://localhost:8080/multiUploadController.do?list=112313312都是一样的
0x04 漏洞复现
登陆后台后
访问:http://192.168.120.140:8080/jeecgFormDemoController.do?commonUpload

上传文件,抓包改名

返回包图片地址

成功访问

0x05 漏洞分析
通过url地址http://192.168.120.140:8080/jeecgFormDemoController.do?commonUpload寻找控制器
定位src/main/webapp/webpage/common/upload/uploadView.jsp

ModelAndView返回到视图文件system/commonupload/commonUploadFile
双击shift查找

分析代码,弹出了个div窗口,通过onclick事件调用了commonUpload函数


查看systemController控制器

传递到了common/upload/uploadView文件,层层寻找终于找到cgUploadController为最终的上传控制器


上传文件没有判断文件名后缀,导致直接getshell
/src/main/java/org/jeecgframework/web/cgform/controller/upload/CgUploadController.java
	/**
	 * 自动上传保存附件资源的方式
	 * @return
	 */
	@RequestMapping(params = "ajaxSaveFile")
	@ResponseBody
	public AjaxJson ajaxSaveFile(MultipartHttpServletRequest request) {
		AjaxJson ajaxJson = new AjaxJson();
		Map<String, Object> attributes = new HashMap<String, Object>();
		try {
			Map<String, MultipartFile> fileMap = request.getFileMap();
			String uploadbasepath = ResourceUtil.getConfigByName("uploadpath");
			// 文件数据库保存路径
			String path = uploadbasepath + "/";// 文件保存在硬盘的相对路径
			String realPath = request.getSession().getServletContext().getRealPath("/") + "/" + path;// 文件的硬盘真实路径
			realPath += DateUtils.getDataString(DateUtils.yyyyMMdd) + "/";
			path += DateUtils.getDataString(DateUtils.yyyyMMdd) + "/";
			File file = new File(realPath);
			if (!file.exists()) {
				file.mkdirs();// 创建文件时间子目录
			}
			if(fileMap != null && !fileMap.isEmpty()){
				for (Map.Entry<String, MultipartFile> entity : fileMap.entrySet()) {
					MultipartFile mf = entity.getValue();// 获取上传文件对象
					String fileName = mf.getOriginalFilename();// 获取文件名
					String swfName = PinyinUtil.getPinYinHeadChar(oConvertUtils.replaceBlank(FileUtils.getFilePrefix(fileName)));// 取文件名首字母作为SWF文件名
					String extend = FileUtils.getExtend(fileName);// 获取文件扩展名
					String noextfilename=DateUtils.getDataString(DateUtils.yyyymmddhhmmss)+StringUtil.random(8);//自定义文件名称
					String myfilename=noextfilename+"."+extend;//自定义文件名称
					String savePath = realPath + myfilename;// 文件保存全路径CgUploadController
					write2Disk(mf, extend, savePath);
					TSAttachment attachment = new TSAttachment();
					attachment.setId(UUID.randomUUID().toString().replace("-", ""));
					attachment.setAttachmenttitle(fileName.substring(0,fileName.lastIndexOf(".")));
					attachment.setCreatedate(new Timestamp(new Date().getTime()));
					attachment.setExtend(extend);
					attachment.setRealpath(path + myfilename);
					String globalSwfTransformFlag = ResourceUtil.getConfigByName("swf.transform.flag");
					if("true".equals(globalSwfTransformFlag) && !FileUtils.isPicture(extend)){
						attachment.setSwfpath( path + FileUtils.getFilePrefix(myfilename) + ".swf");
						SwfToolsUtil.convert2SWF(savePath);
					}
					systemService.save(attachment);
					attributes.put("url", path + myfilename);
					attributes.put("name", fileName);
					attributes.put("swfpath", attachment.getSwfpath());
					attributes.put("fileid", attachment.getId());
				}
			}
			ajaxJson.setAttributes(attributes);
		} catch (Exception e) {
			e.printStackTrace();
			ajaxJson.setSuccess(false);
			ajaxJson.setMsg(e.getMessage());
		}
		return ajaxJson;
	}
					MultipartFile mf = entity.getValue();// 获取上传文件对象
					String fileName = mf.getOriginalFilename();// 获取文件名
					String swfName = PinyinUtil.getPinYinHeadChar(oConvertUtils.replaceBlank(FileUtils.getFilePrefix(fileName)));// 取文件名首字母作为SWF文件名
					String extend = FileUtils.getExtend(fileName);// 获取文件扩展名
					String noextfilename=DateUtils.getDataString(DateUtils.yyyymmddhhmmss)+StringUtil.random(8);//自定义文件名称
					String myfilename=noextfilename+"."+extend;//自定义文件名称
					String savePath = realPath + myfilename;// 文件保存全路径CgUploadController
					write2Disk(mf, extend, savePath);
write2Disk函数用的是FileOutputStream文件输出流

- 由于需要登陆才能上传,所以这里附上一个未授权漏洞
 http://localhost:8080/webpage/system/druid/websession.json


 
                
            
         
         浙公网安备 33010602011771号
浙公网安备 33010602011771号