2025/3/18:请求jsp获得servlet的过程

最初想法是调试tomcat最近几个代码执行的洞,看到CVE-2024-50379用非标准后缀使请求通过DefaultServlet处理触发文件上传,于是看看这个请求在tomcat具体咋绑定到JspServlet上,当复习了

环境准备

IDEA2024.2.3 java8u65 tomcat9.0.70 ant1.10.15

编译源码搭建环境大致参考这个:Tomcat IDEA源码调试环境搭建_企业 tomcat idea 本地代码调试-CSDN博客

将tomcat源码导入IDEA中后先新建pom.xml,并添加为maven工程;

将java目录设置为源代码根目录;

之后在IDEA中下载ant插件,执行ant ide-intellij命令构建,构建成功后按提示设置PATH后执行ant deploy

在IDEA中选择Java草稿设置运行配置来配置虚拟机选项;

若提示缺少aQute的包直接pom里添加拉取就行,提示VERSION_9变量找不到报错把那段代码注释不影响正常启动

调试流程

先根据请求(我使用GET请求/index.jsp)流程在JspServlet的service打个断点,访问下默认的index.jsp,如下图可以在request.mappingData中找到保存的信息,可以看到StandardWrapper对应的值为jsp

在StandardWrapperValve#invoke中,将加载servlet变量的值获取servlet

在118行的赋值后servlet的值为JspServlet,因此接下来寻找wrapper的赋值过程

wrapper的赋值来源于request.mappingData中wrapper保存的值,向上查找可以发现其在CoyoteAdapter中赋值。经过调试可知,在CoyoteAdapter#service函数中执行至354行时,mappingData被赋值

跟进函数postParseRequest,可以发现在714行处赋值

进入map函数后,会对mappingData内容赋值

随后进入Mapper#internalMap,首先赋值host,在772行处

设置context,随后进入internalMapWrapper

这个函数会将contextVersion的内容赋给mappingData.wrapper:

internalMapWrapper函数的逻辑大致为:依次检测contextVersion的exactWrappers、wildcardWrappers、extensionWrappers、welcomeResources、defaultWrapper是否符合赋值给mappingData的条件

进入internalMapWrapper函数时截取了路径的偏移量,备用

针对exactWrappers的处理由internalMapExactWrappe进行,进行精确匹配

处理精确匹配wrapper,这里可以看到wrappers内容为空,实际上不会做任何处理

这里检测路径是否符合通配符,进入internalMapWildcardWrapper看看

这里首先判断path开头是否匹配wrapper,startsWith内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public boolean startsWith(String s) {
char[] c = buff;
int len = s.length();
if (c == null || len > end - start) {
return false;
}
int off = start;
for (int i = 0; i < len; i++) {
if (c[off++] != s.charAt(i)) {
return false;
}
}
return true;
}

从前往后匹配,如果path开头与wrappers匹配则进入循环

如果path与wrapper.name长度相等,则认为成功匹配,否则匹配路径中的/截取原有路径重新查找

之后的if条件语句试图设置重定向,这里由于noServletPath仍然为false不进入,因此整个internalMapWildcardWrapper部分没有做任何处理

重点关注Extension部分,该部分的internalMapExtensionWrapper函数

在924行进入internalMapExtensionWrapper函数,函数内容如下:

这里path的值来源于request。从断点跟进exactFind,在exactFind中跟进find函数,到达Mapper#find,注意这个函数由有4个参数,此后进入Mapper#compare

可以看到是直接使用字符串比较,到这里其实匹配的过程已经结束了

假设此时仍然没找到wrapper,会匹配欢迎文件(默认index.html、index.htm、index.jsp)

这段代码把欢迎文件名均加载进数组,在循环中将path与文件名逐个比较,依次进行精确匹配、模糊匹配、扩展名匹配

在以上代码完成后会对对一些特殊的欢迎文件进行处理

这里主要为了解决一些特殊的欢迎文件如index.do等,当前面匹配都失败直接使用默认wrapper

验证一下

将访问的路由改为/index.Jsp,可以看到Http11Processor#service中直接截取了路径

大小写不同无法通过字符串比较,没有通过jsp路径检测因此使用DefaultSetvlet处理