这两天尝试自己对着通告和commit自己分析,看不懂之后上网搜搜参考, 看到这篇,终于理解了。
在此之前需要先知道:
- windows下,
new File("a.jsp").exists()
,如果当前目录有a.Jsp而没有a.jsp,也是返回true的。 - windows下,
new FileInputStream("a.Jsp").read()
,如果当前目录有a.Jsp而没有a.jsp,能够读取a.Jsp的内容。
当我们访问/a.jsp时候,tomcat内部是怎么找到a.jsp这个文件的?
断点打在DirResourceSet#getResource
此时的调用栈
仔细看当前getResource方法的逻辑后,就知道,file方法不能返回null,否则返回的是EmptyResource。
进入file方法,他首先new一个File对象,然后:
- 是否以斜杠结尾,是否是个文件
- 是否可读
- getRoot().getAllowLinking()是否为true
- 是否是合法windows文件名
- 获取abs path 和 can path,看是否equals。
以上几个步骤,都必须为true,才能够返回一个File对象。
关键的是第5步,如何获取can path?调用的是file.getCanonicalPath()。
跟进,会进入到WinNTFileSystem#canonicalize
关键代码
if (!useCanonCaches) { |
总结file.getCanonicalPath 获取can path的步骤
- 从缓存里取res,若取到,则直接返回 res
- 若没取到,则从prefixCache取缓存的路径,然后和文件名拼在一起作为 res(这一步不重要)
- 若还是没取到,调用canonicalize0,结果为res,然后把res放入缓存
canonicalize0怎么计算can path
windows下,
若存在a.Jsp这个文件,canonicalize0(“a.jsp”) = “a.Jsp”
若不存在a.Jsp这个文件,canonicalize0(“a.jsp”) = “a.jsp”
总结一下,查找文件a.jsp时,调用getResource,getResource会调用file方法,file方法必须返回File(“a.jsp”)对象。
file方法返回File(“a.jsp”)对象的条件是,File(“a.jsp”)的can path和abs path相同。
abs path默认是a.jsp。
can path的获取,先从缓存里找,若找不到,则调用canonicalize0找。
windows下,
若存在a.Jsp这个文件,canonicalize0(“a.jsp”) = “a.Jsp”
若不存在a.Jsp这个文件,canonicalize0(“a.jsp”) = “a.jsp”
所以利用思路:
先发delete请求,删除a.Jsp文件。
第一步,GET /a.jsp,加入缓存,令 a.jsp -> a.jsp
第二步,PUT /a.Jsp,写入恶意文件
第三步,GET /a.jsp,解析恶意文件。(要get多次,后面几次才会解析,超过30秒后就再解析不到了。)