【JAVA代审】newbee-mall开源电商项目代码审计

admin 2026-01-27 14:34:43 网络安全文章 来源:ZONE.CI 全球网 0 阅读模式

文章总结: 本文对newbee-mall开源电商项目进行Java代码审计,分析了鉴权机制及历史越权修复。重点挖掘支付逻辑漏洞,包括负数商品导致的低价购及参数操纵导致的零元购。同时披露了后台任意文件上传与编辑器存储型XSS漏洞,并辟谣CVE-2024-48178实为CSRF登出风险。文末包含大量培训广告。 综合评分: 72 文章分类: 代码审计,WEB安全,漏洞分析,漏洞POC,软文广告


历史漏洞之水平越权

1.在2022年2月之前的版本中,在ltd.newbee.mall.service.impl.NewBeeMallUserServiceImpl下的updateUserInfo函数中,是这么写的:

@Overridepublic NewBeeMallUserVO updateUserInfo(MallUser mallUser, HttpSession httpSession) {    NewBeeMallUserVO userTemp = (NewBeeMallUserVO) httpSession.getAttribute(Constants.MALL_USER_SESSION_KEY);    MallUser userFromDB = mallUserMapper.selectByPrimaryKey(userTemp.getUserId());    if (userFromDB != null) {        if (StringUtils.hasText(mallUser.getNickName())) {            userFromDB.setNickName(NewBeeMallUtils.cleanString(mallUser.getNickName()));        }        if (StringUtils.hasText(mallUser.getAddress())) {            userFromDB.setAddress(NewBeeMallUtils.cleanString(mallUser.getAddress()));        }        if (StringUtils.hasText(mallUser.getIntroduceSign())) {            userFromDB.setIntroduceSign(NewBeeMallUtils.cleanString(mallUser.getIntroduceSign()));        }        if (mallUserMapper.updateByPrimaryKeySelective(userFromDB) > 0) {            NewBeeMallUserVO newBeeMallUserVO = new NewBeeMallUserVO();            userFromDB = mallUserMapper.selectByPrimaryKey(mallUser.getUserId());            BeanUtil.copyProperties(userFromDB, newBeeMallUserVO);            httpSession.setAttribute(Constants.MALL_USER_SESSION_KEY, newBeeMallUserVO);            return newBeeMallUserVO;        }    }    return null;}

问题就出现在17行的userFromDB = mallUserMapper.selectByPrimaryKey(mallUser.getUserId());这里根据传入的mallUser的Id进行修改,而mallUser的Id我们是可控的,可以跟代码看一下:

如图,我们可以通过传入mallUser这个类中的Id,来越权修改别人的信息。

2.那么作者是如何修复改漏洞的呢,很简单,就是直接把第17行删了。为什么可以这样呢?因为在前两行:

NewBeeMallUserVO userTemp%20=%20(NewBeeMallUserVO)%20httpSession.getAttribute(Constants.MALL_USER_SESSION_KEY);MallUser userFromDB%20=%20mallUserMapper.selectByPrimaryKey(userTemp.getUserId());

这里的userFromDB是根据登录用户的session来判断的,因此只能修改自己的信息。也就是说,其实作者之前写的那行代码不仅多余,而且还造成了越权漏洞。

支付漏洞 低价购

1.在\src\main\java\ltd\newbee\mall\controller\mall\ShoppingCartController.java中有如下代码:

@GetMapping("/shop-cart/settle")public&nbsp;String&nbsp;settlePage(HttpServletRequest&nbsp;request,&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;HttpSession&nbsp;httpSession)%20{&nbsp;%20&nbsp;%20int%20priceTotal%20=&nbsp;0;&nbsp;%20&nbsp;&nbsp;NewBeeMallUserVO&nbsp;user%20=%20(NewBeeMallUserVO)%20httpSession.getAttribute(Constants.MALL_USER_SESSION_KEY);&nbsp;%20&nbsp;&nbsp;List<NewBeeMallShoppingCartItemVO>%20myShoppingCartItems%20=%20newBeeMallShoppingCartService.getMyShoppingCartItems(user.getUserId());&nbsp;%20&nbsp;&nbsp;if&nbsp;(CollectionUtils.isEmpty(myShoppingCartItems))%20{&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;&nbsp;//无数据则不跳转至结算页&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;&nbsp;return&nbsp;"/shop-cart";&nbsp;%20&nbsp;%20}&nbsp;else&nbsp;{&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;&nbsp;//总价&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;&nbsp;for&nbsp;(NewBeeMallShoppingCartItemVO&nbsp;newBeeMallShoppingCartItemVO%20:%20myShoppingCartItems)%20{&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20priceTotal%20+=%20newBeeMallShoppingCartItemVO.getGoodsCount()%20*%20newBeeMallShoppingCartItemVO.getSellingPrice();&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20}&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;&nbsp;if&nbsp;(priceTotal%20<&nbsp;1)%20{&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;&nbsp;NewBeeMallException.fail("购物项价格异常");&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20}&nbsp;%20&nbsp;%20}&nbsp;%20&nbsp;%20request.setAttribute("priceTotal",%20priceTotal);&nbsp;%20&nbsp;%20request.setAttribute("myShoppingCartItems",%20myShoppingCartItems);&nbsp;%20&nbsp;&nbsp;return&nbsp;"mall/order-settle";}

漏洞出现在如下代码中:

这里作者值判断了priceTotal,即总的价格大于1,并未判断是否每件价格都大于1,并且未规定购买数量不能为负数,因此我们可以购买一些负数的低价商品,再购买一件高价商品,实现低价购买,如图为加入购物车的数据包:

POST%20/shop-cart%20HTTP/1.1Host:%20127.0.0.1:28089Content-Length:%2033sec-ch-ua-platform:%20"Windows"X-Requested-With:%20XMLHttpRequestUser-Agent:%20Mozilla/5.0%20(Windows%20NT%2010.0;%20Win64;%20x64)%20AppleWebKit/537.36%20(KHTML,%20like%20Gecko)%20Chrome/143.0.0.0%20Safari/537.36Accept:%20*/*sec-ch-ua:%20"Google%20Chrome";v="143",%20"Chromium";v="143",%20"Not%20A(Brand";v="24"Content-Type:%20application/jsonsec-ch-ua-mobile:%20?0Origin:%20http://127.0.0.1:28089Sec-Fetch-Site:%20same-originSec-Fetch-Mode:%20corsSec-Fetch-Dest:%20emptyReferer:%20http://127.0.0.1:28089/goods/detail/10195Accept-Encoding:%20gzip,%20deflate,%20brAccept-Language:%20zh-CN,zh;q=0.9Cookie:%20JSESSIONID=5E4F79C3A28A36AADEDEB26CAB069CF4Connection:%20keep-alive{"goodsId":10195,"goodsCount":-4}

订单提交成功:

零元购

1.漏洞代码的路由在src\main\java\ltd\newbee\mall\controller\mall\OrderController.java中:

跟进一下paySuccess方法:

这里是说newBeeMallOrderMapper.updateByPrimaryKeySelective(newBeeMallOrder)的值大于0就可以判定为支付成功,但问题i就是,payType是我们传入的参数,是可控的呀,我们直接修改payType的值就可以让newBeeMallOrderMapper的值修改,导致if语句判断成功,实现0元购。

如图,成功支付。

后台任意文件上传

1.上传代码在src\main\java\ltd\newbee\mall\controller\common\UploadController.java中:

@Controller@RequestMapping("/admin")public&nbsp;class&nbsp;UploadController&nbsp;{&nbsp;%20&nbsp;&nbsp;@Autowired&nbsp;%20&nbsp;&nbsp;private&nbsp;StandardServletMultipartResolver%20standardServletMultipartResolver;&nbsp;%20&nbsp;&nbsp;@PostMapping({"/upload/file"})&nbsp;%20&nbsp;&nbsp;@ResponseBody&nbsp;%20&nbsp;&nbsp;public&nbsp;Result&nbsp;upload(HttpServletRequest%20httpServletRequest,&nbsp;@RequestParam("file")&nbsp;MultipartFile%20file)&nbsp;throws&nbsp;URISyntaxException,%20IOException%20{&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;&nbsp;String&nbsp;fileName&nbsp;=&nbsp;file.getOriginalFilename();&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;&nbsp;BufferedImage&nbsp;bufferedImage&nbsp;=&nbsp;ImageIO.read(file.getInputStream());&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;&nbsp;if&nbsp;(bufferedImage%20==&nbsp;null)%20{&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;&nbsp;return&nbsp;ResultGenerator.genFailResult("请上传图片类型的文件");&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20}&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;&nbsp;String&nbsp;suffixName&nbsp;=&nbsp;fileName.substring(fileName.lastIndexOf("."));&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;&nbsp;//生成文件名称通用方法&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;&nbsp;SimpleDateFormat&nbsp;sdf&nbsp;=&nbsp;new&nbsp;SimpleDateFormat("yyyyMMdd_HHmmss");&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;&nbsp;Random&nbsp;r&nbsp;=&nbsp;new&nbsp;Random();&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;&nbsp;StringBuilder&nbsp;tempName&nbsp;=&nbsp;new&nbsp;StringBuilder();&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20tempName.append(sdf.format(new&nbsp;Date())).append(r.nextInt(100)).append(suffixName);&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;&nbsp;String&nbsp;newFileName&nbsp;=&nbsp;tempName.toString();&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;&nbsp;File&nbsp;fileDirectory&nbsp;=&nbsp;new&nbsp;File(Constants.FILE_UPLOAD_DIC);&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;&nbsp;//创建文件&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;&nbsp;File&nbsp;destFile&nbsp;=&nbsp;new&nbsp;File(Constants.FILE_UPLOAD_DIC%20+%20newFileName);&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;&nbsp;try&nbsp;{&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;&nbsp;if&nbsp;(!fileDirectory.exists())%20{&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;&nbsp;if&nbsp;(!fileDirectory.mkdir())%20{&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;&nbsp;throw&nbsp;new&nbsp;IOException("文件夹创建失败,路径为:"&nbsp;+%20fileDirectory);&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20}&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20}&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20file.transferTo(destFile);&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;&nbsp;Result&nbsp;resultSuccess&nbsp;=&nbsp;ResultGenerator.genSuccessResult();&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20resultSuccess.setData(NewBeeMallUtils.getHost(new&nbsp;URI(httpServletRequest.getRequestURL()%20+&nbsp;""))%20+&nbsp;"/upload/"&nbsp;+%20newFileName);&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;&nbsp;return&nbsp;resultSuccess;&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20}&nbsp;catch&nbsp;(IOException%20e)%20{&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20e.printStackTrace();&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;&nbsp;return&nbsp;ResultGenerator.genFailResult("文件上传失败");&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20}&nbsp;%20&nbsp;%20}&nbsp;%20&nbsp;&nbsp;@PostMapping({"/upload/files"})&nbsp;%20&nbsp;&nbsp;@ResponseBody&nbsp;%20&nbsp;&nbsp;public&nbsp;Result&nbsp;uploadV2(HttpServletRequest%20httpServletRequest)&nbsp;throws&nbsp;URISyntaxException,%20IOException%20{&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20List<MultipartFile>%20multipartFiles%20=&nbsp;new&nbsp;ArrayList<>(8);&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;&nbsp;if&nbsp;(standardServletMultipartResolver.isMultipart(httpServletRequest))%20{&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;&nbsp;MultipartHttpServletRequest&nbsp;multiRequest&nbsp;=&nbsp;(MultipartHttpServletRequest)%20httpServletRequest;&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20Iterator<String>%20iter%20=%20multiRequest.getFileNames();&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;&nbsp;int&nbsp;total&nbsp;=&nbsp;0;&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;&nbsp;while&nbsp;(iter.hasNext())%20{&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;&nbsp;if&nbsp;(total%20>&nbsp;5)%20{&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;&nbsp;return&nbsp;ResultGenerator.genFailResult("最多上传5张图片");&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20}&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20total%20+=&nbsp;1;&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;&nbsp;MultipartFile&nbsp;file&nbsp;=&nbsp;multiRequest.getFile(iter.next());&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;&nbsp;BufferedImage&nbsp;bufferedImage&nbsp;=&nbsp;ImageIO.read(file.getInputStream());&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;&nbsp;//%20只处理图片类型的文件&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;&nbsp;if&nbsp;(bufferedImage%20!=&nbsp;null)%20{&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20multipartFiles.add(file);&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20}&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20}&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20}&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;&nbsp;if&nbsp;(CollectionUtils.isEmpty(multipartFiles))%20{&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;&nbsp;return&nbsp;ResultGenerator.genFailResult("请选择图片类型的文件上传");&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20}&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;&nbsp;if&nbsp;(multipartFiles%20!=&nbsp;null&nbsp;&&%20multipartFiles.size()%20>&nbsp;5)%20{&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;&nbsp;return&nbsp;ResultGenerator.genFailResult("最多上传5张图片");&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20}&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20List<String>%20fileNames%20=&nbsp;new&nbsp;ArrayList(multipartFiles.size());&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;&nbsp;for&nbsp;(int&nbsp;i&nbsp;=&nbsp;0;%20i%20<%20multipartFiles.size();%20i++)%20{&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;&nbsp;String&nbsp;fileName&nbsp;=&nbsp;multipartFiles.get(i).getOriginalFilename();&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;&nbsp;String&nbsp;suffixName&nbsp;=&nbsp;fileName.substring(fileName.lastIndexOf("."));&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;&nbsp;//生成文件名称通用方法&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;&nbsp;SimpleDateFormat&nbsp;sdf&nbsp;=&nbsp;new&nbsp;SimpleDateFormat("yyyyMMdd_HHmmss");&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;&nbsp;Random&nbsp;r&nbsp;=&nbsp;new&nbsp;Random();&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;&nbsp;StringBuilder&nbsp;tempName&nbsp;=&nbsp;new&nbsp;StringBuilder();&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20tempName.append(sdf.format(new&nbsp;Date())).append(r.nextInt(100)).append(suffixName);&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;&nbsp;String&nbsp;newFileName&nbsp;=&nbsp;tempName.toString();&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;&nbsp;File&nbsp;fileDirectory&nbsp;=&nbsp;new&nbsp;File(Constants.FILE_UPLOAD_DIC);&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;&nbsp;//创建文件&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;&nbsp;File&nbsp;destFile&nbsp;=&nbsp;new&nbsp;File(Constants.FILE_UPLOAD_DIC%20+%20newFileName);&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;&nbsp;try&nbsp;{&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;&nbsp;if&nbsp;(!fileDirectory.exists())%20{&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;&nbsp;if&nbsp;(!fileDirectory.mkdir())%20{&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;&nbsp;throw&nbsp;new&nbsp;IOException("文件夹创建失败,路径为:"&nbsp;+%20fileDirectory);&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20}&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20}&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20multipartFiles.get(i).transferTo(destFile);&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20fileNames.add(NewBeeMallUtils.getHost(new&nbsp;URI(httpServletRequest.getRequestURL()%20+&nbsp;""))%20+&nbsp;"/upload/"&nbsp;+%20newFileName);&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20}&nbsp;catch&nbsp;(IOException%20e)%20{&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20e.printStackTrace();&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;&nbsp;return&nbsp;ResultGenerator.genFailResult("文件上传失败");&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20}&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20}&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;&nbsp;Result&nbsp;resultSuccess&nbsp;=&nbsp;ResultGenerator.genSuccessResult();&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20resultSuccess.setData(fileNames);&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;&nbsp;return&nbsp;resultSuccess;&nbsp;%20&nbsp;%20}}

可以看到,作者通过ImageIO.read来判断传入的是否为图片,但是作者没有考虑到,我们可以先传入图片,再在图片的尾部添加恶意代码,也就是所谓的“图片马”。

2.我们先找到一处文件上传点,如图:

数据包如下:(图片内容用xxx代替)

POST%20/admin/upload/file%20HTTP/1.1Host:%20localhost:28089Content-Length:%2013342Cache-Control:%20max-age=0sec-ch-ua:&nbsp;"Google%20Chrome";v="143",&nbsp;"Chromium";v="143",&nbsp;"Not%20A(Brand";v="24"sec-ch-ua-mobile:%20?0sec-ch-ua-platform:&nbsp;"Windows"Origin:%20http://localhost:28089Content-Type:%20multipart/form-data;%20boundary=----WebKitFormBoundaryRII4LixNAimwdMeHUpgrade-Insecure-Requests:%201User-Agent:%20Mozilla/5.0%20(Windows%20NT%2010.0;%20Win64;%20x64)%20AppleWebKit/537.36%20(KHTML,%20like%20Gecko)%20Chrome/143.0.0.0%20Safari/537.36Accept:%20text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7Sec-Fetch-Site:%20same-originSec-Fetch-Mode:%20navigateSec-Fetch-Dest:%20iframeReferer:%20http://localhost:28089/admin/goods/editAccept-Encoding:%20gzip,%20deflate,%20brAccept-Language:%20zh-CN,zh;q=0.9Cookie:%20JSESSIONID=0779CECA6C110A0E83FB037F5AB3B99AConnection:%20keep-alive------WebKitFormBoundaryRII4LixNAimwdMeHContent-Disposition:%20form-data;%20name="file";%20filename="test.jpg"Content-Type: image/jpegxxx------WebKitFormBoundaryRII4LixNAimwdMeH--

当用户在点入test5商品时,就会触发xss:

CVE-2024-48178

1.该cve的发现者将分析写在了github上:https://github.com/dabaizhizhu/123/issues/10 但是我分析过后,发现该cve并不存在,即不存在ssrf漏洞。

如图,按照cve发现者的意思,我们将img改为dnslog地址,并上架商品,我们登录普通用户进行访问。触发的dnslog如下:

很明显这里并不是服务端发起的请求,而是用户通过浏览器发出的请求,因此肯定不是ssrf漏洞。我们通过调试也能发现:

我们在src\main\java\ltd\newbee\mall\controller\mall\GoodsController.java打一个断点进行调试。

当调试到如图,也就是spring内部发出的一条请求后,触发了dnslog:

也就是说,spring发送给浏览器渲染,然后浏览器访问了这个url,触发了dnslog:

但是话又说回来,这个地方不存在ssrf,但是用户能请求到,是否也是有问题的呢?答案是肯定的。我们可以结合网站的csrf漏洞,进行结合利用。经过测试发现,代码中没有对csrf的防护,且http://localhost:28089/logout这个退出url是存在csrf漏洞的,因此我们可以将商品的url改成http://localhost:28089/logout:

此时,只要别人访问了我们的商品,就会自动退出。

总结

实践出真知,不能一昧的相信网上的文章(甚至连cve都有可能是错的),自己调试分析才是硬道理。

最后

有其它问题或者对文章内容有疑问的,可以加作者微信,加交流群的话备注”交流群”


公众号培训广告,有需要可联系(混口饭吃)

新一期课程介绍:菜狗安全《代码审计培训》2026年新征程

JAVA审计闭源专题课表

内容对比上一期会更细点,并且主要围绕企业闭源项目,如果说上一期是CVE随便刷,那么这一轮就是奔着CNVD,CNNVD去了

课程内容涵盖

1、基础篇

  • 通用学习路线与指南

  • 这个会作为我们新一期课程的第一讲,主要是为师傅们梳理学习的思路,建立一套“以业务为核心,架构为先导”的底层审计逻辑,从而具备审任何语言都能快速上手的通杀能力

  • 漏洞的本质

  • 审计审计,我们审计的主要是漏洞,那么对于漏洞的理解就显得尤为重要,从漏洞产生的本质出发,理解漏洞,吃透漏洞,会大大提升对于不同类型漏洞的审计关注点,大大提升审计效率

  • 闭源项目常见架构审计差异与反编译

  • 讲解常见闭源项目的不同架构差异,以及源码到手后的准备工作

  • 不同架构项目路由传参以及鉴权装配模式

  • 讲解常见闭源项目的不同架构路由配置以及接口构造与参数传递,以及框架特性导致的鉴权装配模式差异

  • JAVAweb开发

  • 这一块内容是分割在每节漏洞审计的前置部分,不同于市面上的常规开发课程,通过实际业务分析漏洞产生原因,在实战案例前就学会如何挖漏洞!!!

2、鉴权分析

  • 讲解java项目中常见的鉴权方法

  • 不同鉴权方式的路由白名单配置

  • 以及实战如何绕过鉴权

  • 实战情况中存在鉴权绕过的缺陷逻辑

3、常见漏洞审计

  • SQL注入挖掘

  • 文件操作类挖掘

  • 组件安全审计

  • SSRF审计

  • XXE审计

  • RCE审计篇

  • 越权类挖掘

  • ……

4、审计番外内容

  • ……

这个第一期上过挺多次了,主要还是根据学员的需求来加的

5、企业源码审计实战

选取国内某OA,某ERP,某管理系统,等企业级源码,甚至是我实战中遇到的企业泄露源码(脱敏处理后的),从反编译->项目架构分析->鉴权分析->漏洞审计的完整流程,并且每节都有0day挖掘案例,手把手教学,避免一看就会,一上手就废。

这个模块第一期上了17次直播,也是给学员听爽了,也是修改为常驻,慢慢上

秉承节节有0Day,节节挖0Day,从源码解压到漏洞审计完整流程,现场手把手教学审0day

学员内部福利

1、国内几个源码站会员:9k9k,刀客,小蚂蚁,优选源码,源码庄,狗凯等,如果有需要其他源码站会员可联系我,我看情况开通

2、内部代码审计文档库加配套对应源码(持续更新中)

每篇都是从项目架构分析->鉴权分析->漏洞审计,以思路为主,并非简单漏洞披露,源码类型广

3、一些源码资源

4、不定期抽奖和布置考核任务给予奖品,形成玩中学,学中玩的学习氛围(太多了随便放两张)

考核靶场

4、部分项目(代码审计,渗透测试,讲师,HVV等等)优先推送

5、更多内容筹备中。。。

往期精彩课程:

代码审计第一期(JAVA专题)菜狗安全《代码审计培训》双十一优惠开启:手把手0day挖掘教学

价格

哎呀,要不要涨价呢,我给出的回答是,短期内不考虑,目前课程原价依旧1299,并且从今天到第二轮开课1月30号之前1199,一次报名后面可以一直听不会有二次收费,如果是学生确实没啥💴就看B站公开课吧,入门绝对够,付费课有能力再考虑

暂时我也想不到其它问题了,如果有其他疑问可以在群里问,或者私信我

心动不如行动

有需要或者有问题可以加微信,备注“培训”

学员反馈

放几张意思下,还有一堆懒得找了


免责声明:

本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。

任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。

本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我

本文转载自:菜狗安全 学员投稿 学员投稿《【JAVA代审】newbee-mall开源电商项目代码审计》

收到请回复 网络安全文章

收到请回复

文章总结: 文档内容为极简的聊天记录片段,仅包含问候语阿乐你好、日期2026年1月27日09:02和地点上海,不包含任何技术内容、安全信息或可操作价值。 综合评
评论:0   参与:  0