JEESITE V1.2.7 任意文件读取漏洞

打强网杯的时候,一道题目直接就是考JEESITE V1.2.7读配置文件,用以前挖的一个洞拿了flag,漏洞比较简单水一篇。
JEESITE存在两个版本JEESITE1、JEESITE4,其中1.2.7是JEESITE1中的最新版本,不过已经在四年前就不再维护。

分析

com/thinkgem/jeesite/common/servlet/UserfilesDownloadServlet.java

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
public void fileOutputStream(HttpServletRequest req, HttpServletResponse resp) 
throws ServletException, IOException {
String filepath = req.getRequestURI();
int index = filepath.indexOf(Global.USERFILES_BASE_URL);
if(index >= 0) {
filepath = filepath.substring(index + Global.USERFILES_BASE_URL.length());
}
try {
filepath = UriUtils.decode(filepath, "UTF-8");
} catch (UnsupportedEncodingException e1) {
logger.error(String.format("解释文件路径失败,URL地址为%s", filepath), e1);
}
File file = new File(Global.getUserfilesBaseDir() + Global.USERFILES_BASE_URL + filepath);
try {
FileCopyUtils.copy(new FileInputStream(file), resp.getOutputStream());
resp.setHeader("Content-Type", "application/octet-stream");
return;
} catch (FileNotFoundException e) {
req.setAttribute("exception", new FileNotFoundException("请求的文件不存在"));
req.getRequestDispatcher("/WEB-INF/views/error/404.jsp").forward(req, resp);
}
}

@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
fileOutputStream(req, resp);
}

@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
fileOutputStream(req, resp);
}

该servlet用来查看CK上传的图片,配置的路由为

1
2
3
4
<servlet-mapping>
<servlet-name>UserfilesDownloadServlet</servlet-name>
<url-pattern>/userfiles/*</url-pattern>
</servlet-mapping>

该Servlet方法很简单,直接获取RequestURI稍作处理后就开始读取文件。

1
2
3
4
5
String filepath = req.getRequestURI();
int index = filepath.indexOf(Global.USERFILES_BASE_URL);
if(index >= 0) {
filepath = filepath.substring(index + Global.USERFILES_BASE_URL.length());
}

filepath即为RequestURI中”/userfiles/“之后的内容,
File file = new File(Global.getUserfilesBaseDir() + Global.USERFILES_BASE_URL + filepath); 虽然限制了只能读取/userfiles/下的文件,但是如果能在filepath进行目录穿越依然能够实现任意文件读取。

不过这里存在一个问题,因为是通过RequestURI获取的文件名而不是GET/POST参数,当需要通过目录穿越读取任意文件时需要在URI中包含”../“,当URI为/userfiles/../xxxxx 会先在TOMCAT这目录穿越,最终请求到的是xxxxx接口,无法访问到UserfilesDownloadServlet。
虽然后面有对filepath进行url解码操作,但是req.getRequestURI获取到的是未URL解码的数据,造成无法二次编码最多只能一次编码,/userfiles/%2e%2e/xxxx 一次编码会被tomcat容器给处理掉然后目录穿越,最后还是无法访问到UserfilesDownloadServlet。

再回过头来看一下对filepath的处理,

1
2
3
4
5
String filepath = req.getRequestURI();
int index = filepath.indexOf(Global.USERFILES_BASE_URL);
if(index >= 0) {
filepath = filepath.substring(index + Global.USERFILES_BASE_URL.length());
}

USERFILES_BASE_URL的定义,
public static final String USERFILES_BASE_URL = “/userfiles/“;

首先从requesturi中查找/userfiles/的起始位置,然后截取它后面的内容作为filepath,这里userfiles后面有/,这时候借助TOMCAT的path parameter特性”;”后面的内容会当作参数,/userfiles;xxx/依然能够访问到UserfilesDownloadServlet。
但是这时候因为没能在requesturi中找到/userfiles/,就将整个requesturi带入到了文件读取的目录中这样肯定也是不行的。可以通过在/userfiles;xxx/再新增一个/userfiles/xxxx, 即/userfiles;xxx/userfiles/xxxxx,字符串截取后filepath后就为xxxxx,并且这时候可以发现requesturi中已经存在了两个目录,这时候再目录穿越/userfiles;xxx/userfiles/../xxxx, tomcat处理后/userfiles/xxxx 依然请求到了UserfilesDownloadServlet,并且此时filepath为../xxxx可以实现目录穿越读取任意文件了,只要requesturi中的目录够多那么就可以目录穿越任意数量目录,在/userfiles/前面新增大数量的目录即可。

-w1560

qwb利用,

登录邮箱拿到flag