1. 基本概念
Spring Boot Controller 内存马 是针对 Spring Boot 框架特性设计的内存马,利用 Spring MVC 中 RequestMappingHandlerMapping 的动态注册能力,在运行时将恶意 Controller 注入 Spring 容器,无需文件落地即可实现 URL 绑定与恶意逻辑执行。
核心特点
-
框架依赖:基于 Spring MVC 组件(
DispatcherServlet、RequestMappingHandlerMapping)实现,仅适用于 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 项目
-
通过 创建项目,选择以下配置:
-
Spring Boot 版本:2.4.2(文档验证稳定版本,其他 Web 支持版本通用)
-
构建工具:Maven
-
依赖组件:Spring Web(必须,提供 MVC 与 Servlet 容器支持)
-
-
下载项目压缩包,解压后用 IDEA 打开,核心项目结构如下:
1
2
3
4
5
6
7
8
9spring-demo/
├── src/
│ └── main/
│ └── java/
│ └── com/example/demo/
│ ├── DemoApplication.java # 启动类
│ └── demos/web/
│ └── MyController.java # 自定义 Controller
└── pom.xml # 依赖配置
2.2 编写 Controller 类
通过 @RestController 和 @RequestMapping 注解定义接口,示例代码如下:
1 | package com.example.demo.demos.web; |
2.3 启动与访问
-
运行
DemoApplication启动类(含@SpringBootApplication注解) -
浏览器访问
http://localhost:8080/index,页面显示index,说明 Controller 正常加载
3. Spring Boot Controller 加载流程(框架内部机制)
通过调试模式跟踪请求流程,可揭示 Spring Boot 如何将 URL 与 Controller 方法绑定。
3.1 核心请求链路
当访问 http://localhost:8080/index 时,请求经过以下关键步骤:
-
Servlet 容器层:请求先经过 Tomcat 内置容器的 Filter 链(如
WsFilter、RequestContextFilter) -
Spring MVC 入口:进入
DispatcherServlet(Spring MVC 核心前端控制器) -
请求分发:
DispatcherServlet.doDispatch()方法负责请求分发,核心逻辑是「找到匹配的 Controller 方法」 -
Handler 映射:通过
getHandler()方法遍历handlerMappings列表,找到能处理/index路径的映射器 -
方法执行:调用匹配的 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 实现步骤拆解
-
获取 Spring 上下文:通过
RequestContextHolder拿到当前请求的WebApplicationContext(Spring 容器核心) -
获取映射器实例:从上下文获取
RequestMappingHandlerMapping对象(负责 Controller 映射注册) -
定义恶意 Controller:创建含恶意逻辑(如命令执行)的 Controller 类
-
构建映射规则:通过
RequestMappingInfo定义恶意 Controller 绑定的 URL 路径(如/evil) -
注册映射关系:调用
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
1 | import org.springframework.context.ApplicationContext; |
6. 触发步骤
-
启动目标应用:运行含上述代码的 Spring Boot 项目(确保
InjectEntryController被 Spring 扫描到) -
触发内存马注入:
-
浏览器访问
http://localhost:8080/inject/controller -
页面显示
Spring Boot Controller 内存马注入成功!,说明恶意 Controller 已注册
-
-
执行恶意逻辑:
-
访问
http://localhost:8080/evil?cmd=whoami(Windows 系统)或http://localhost:8080/evil?cmd=id(Linux 系统) -
页面返回命令执行结果(如当前用户信息)
-
-
隐蔽性验证:
-
删除
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),通常通过RequestContextHolder或ServletContext间接获取 -
隐蔽性:相比 Servlet/Filter 内存马,Controller 内存马更贴合 Spring Boot 正常业务逻辑,URL 路径可伪装成普通接口,更难被察觉
-
防御方向:监控
RequestMappingHandlerMapping.registerMapping方法的异常调用、检测未知 URL 路径(如/evil