想不到!Spring MVC 拦截器也有漏洞。。
共 6529字,需浏览 14分钟
· 2022-01-23
点击关注公众号,Java干货及时送达
1 基础拦截器和调用流程的探索
学习、探索和实现过程很多都基于大佬的文章:
https://landgrey.me/blog/19/
https://landgrey.me/blog/12/
1.1 基础拦截器
前不久实现cotroller内存马能添加冰蝎代码后,又想到spring mvc的拦截器应该也可以用于注入内存马,目前的关键点在于找到拦截器是如何被触发以及如何动态添加拦截器
首先来写个正常的拦截器TestInterceptor类,并添加xml配置
![](https://filescdn.proginn.com/b8a7a7f3d2896debfab6050e69b59bcf/d9bb3a1f2703a64684a7943427357a8c.webp)
![](https://filescdn.proginn.com/61fb86ac4d0e73f4f5b3b1d7f01ae485/9c9b179623d65a22a5932126611a2a60.webp)
然后启动程序,在访问/home/index,并添加code参数弹个计算器
![](https://filescdn.proginn.com/20c0ae16cf830a64d775f42517739647/a71ff82f4827359ec9edbd76bc1f12a2.webp)
1.2 探索拦截器的调用链
断点打在TestInterceptor类中,调试看看调用链
preHandle:31, TestInterceptor (bitterz.interceptors)
applyPreHandle:134, HandlerExecutionChain (org.springframework.web.servlet)
doDispatch:956, DispatcherServlet (org.springframework.web.servlet)
doService:895, DispatcherServlet (org.springframework.web.servlet)
processRequest:967, FrameworkServlet (org.springframework.web.servlet)
doGet:858, FrameworkServlet (org.springframework.web.servlet)
service:621, HttpServlet (javax.servlet.http)
service:843, FrameworkServlet (org.springframework.web.servlet)
service:728, HttpServlet (javax.servlet.http)
internalDoFilter:305, ApplicationFilterChain (org.apache.catalina.core)
doFilter:210, ApplicationFilterChain (org.apache.catalina.core)
invoke:222, StandardWrapperValve (org.apache.catalina.core)
invoke:123, StandardContextValve (org.apache.catalina.core)
invoke:472, AuthenticatorBase (org.apache.catalina.authenticator)
invoke:171, StandardHostValve (org.apache.catalina.core)
invoke:99, ErrorReportValve (org.apache.catalina.valves)
invoke:947, AccessLogValve (org.apache.catalina.valves)
invoke:118, StandardEngineValve (org.apache.catalina.core)
service:408, CoyoteAdapter (org.apache.catalina.connector)
process:1009, AbstractHttp11Processor (org.apache.coyote.http11)
process:589, AbstractProtocol$AbstractConnectionHandler (org.apache.coyote)
run:312, JIoEndpoint$SocketProcessor (org.apache.tomcat.util.net)
runWorker:1142, ThreadPoolExecutor (java.util.concurrent)
run:617, ThreadPoolExecutor$Worker (java.util.concurrent)
run:745, Thread (java.lang)
推荐一个 Spring Boot 基础教程及实战示例:
https://github.com/javastacks/spring-boot-best-practice
关键点在doDispatch方法,先通过getHandler方法获取了mappedHandler对象
![](https://filescdn.proginn.com/129a20dbbd3eebee0b3c68047df14be1/b9864bd4e7cd70ad2139719f51afb025.webp)
在后方调用mappedHandler的applyPreHandler方法
![](https://filescdn.proginn.com/8d37a9918b2f0135d45fedb38e358194/7555d5f466b0b838a721c742c5a85f2d.webp)
这个方法中就是依次调用每个interceptor实例的preHandle方法,实际上就进入了前面写好的TestInterceptor类的preHandle方法中。
![](https://filescdn.proginn.com/0468ae81f02d5faf3210e73064219445/6d41dcc08d3c793839acb5d8e460abe8.webp)
1.3 探索拦截器是如何被添加的
跟踪mappedHandler的获取过程,先是调用了org.springframework.web.servlet.DispatcherServlet中的getHandler方法
![](https://filescdn.proginn.com/129a20dbbd3eebee0b3c68047df14be1/b9864bd4e7cd70ad2139719f51afb025.webp)
跟进getHandler方法,这里会遍历this.handlerMappings,获取HandlerMapping的实例,再调用getHandler方法
![](https://filescdn.proginn.com/cb6763ac1f348fc16224cf5719d85050/3b32bbbb1eeed775c0bdf6f3bf80c1d2.webp)
这里断点跟进getHandler函数处,会发现实际上调用了org.springframework.web.servlet.handler.AbstractHandlerMapping类中的getHandler方法。最新面试题整理好了,点击Java面试库小程序在线刷题。
![](https://filescdn.proginn.com/4457f86a42bfdc32d9a670a217b2fb83/5c38318e6eb3020b05cce24bf7863c6b.webp)
再跟进getHandlerExecutionChain方法,发现其中会遍历adaptedInterceptors这数组,并判断获取的interceptor实例是不是MappedInterceptor类的实例对象,而MappedInterceptor类就是对拦截器HandlerInterceptor接口的实现,所以前面定义的TestInterceptor自然会被加入chain中并返回
![](https://filescdn.proginn.com/ee265da02929adc1a0b06ef3014d25eb/60465152e4d13fa19cd3b49ef5b6c1bc.webp)
至此,拦截器的加载和调用流程就清楚了, 动态添加拦截器的话,只需要在org.springframework.web.servlet.handler.AbstractHandlerMapping类的实例对象的adaptedInterceptors数组中添加恶意interceptor实例对象即可!
那么关键就在于找到org.springframework.web.servlet.handler.AbstractHandlerMapping类的实例对象,CTRL+ALT+B找到所有AbstractHandlerMapping的子类,并在beanFactory的beanDefinitionNames中找到它的实例org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping
![](https://filescdn.proginn.com/34e4ea404c5335005ea51e5612550984/37f673e506052043556283410576dfd8.webp)
因此可以通过context.getBean("org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping")获取该对象,再反射获取其中的adaptedInterceptors属性,并添加恶意interceptor实例对象即可完成内存马的注入。
点击关注公众号,Java干货及时送达
2 实践
首先用springmvc 写了一个包含fastjson的反序列化漏洞的controller.
推荐一个 Spring Boot 基础教程及实战示例:https://github.com/javastacks/spring-boot-best-practice
@RequestMapping(value = "/postjson", method = RequestMethod.GET)
public String postJson(HttpServletRequest request){
return "postjson";
}
@RequestMapping(value = "/readjson", method = RequestMethod.POST)
public String readJson(HttpServletRequest request){
String jsonStr = request.getParameter("jsonstr");
System.out.println(jsonStr); // 在控制台输出jsonStr
Object obj = JSON.parseObject(jsonStr);
System.out.println(obj); // 等同于数据操作
return "readjson"; // 返回一个页面给用户
}
![](https://filescdn.proginn.com/388b1a2ee2703f75b2a31756102f2b88/382500bfaed0b46a9ffd504df390a5b9.webp)
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class TestInterceptor extends HandlerInterceptorAdapter {
public TestInterceptor() throws NoSuchFieldException, IllegalAccessException, InstantiationException {
// 获取context
WebApplicationContext context = (WebApplicationContext) RequestContextHolder.currentRequestAttributes().getAttribute("org.springframework.web.servlet.DispatcherServlet.CONTEXT", 0);
// 从context中获取AbstractHandlerMapping的实例对象
org.springframework.web.servlet.handler.AbstractHandlerMapping abstractHandlerMapping = (org.springframework.web.servlet.handler.AbstractHandlerMapping)context.getBean("org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping");
// 反射获取adaptedInterceptors属性
java.lang.reflect.Field field = org.springframework.web.servlet.handler.AbstractHandlerMapping.class.getDeclaredField("adaptedInterceptors");
field.setAccessible(true);
java.util.ArrayList
这里提交两次payload是为了确认:不重复添加interceptor的代码生效了
![](https://filescdn.proginn.com/b4583b5dd544218d25a6962e5dabdf8b/da6a8581deef500a4291cda88bf56de8.webp)
![](https://filescdn.proginn.com/ec8fb23bef6170396f603887cf949cf0/7ab851e149a9af8d717af1d5720b564d.webp)
可见Interceptor内存马已经注入了,现在弹个计算器验证一下
![](https://filescdn.proginn.com/621276288e4bc1bade6354ed2538fc11/78a9246eee684fb1f5979fcd9547a72d.webp)
作者:bitterz
地址:https://www.cnblogs.com/bitterz/
![](https://filescdn.proginn.com/66221739988faa4b4929c180b26aa7a8/c669cda85a696e92b67284c7d085d9c4.webp)
关注Java技术栈看更多干货
![](https://filescdn.proginn.com/a0b4f89c50497e7a4460b2bd51333574/6000189c8e7f85a10881886448b34e89.webp)