Spring Framework RCE,早期公告

工程 | Rossen Stoyanchev | 2022年3月31日 | ...

更新

目录

概览

我在此宣布 Spring Framework 中存在一个 RCE 漏洞,该漏洞在 CVE 发布之前被提前泄露。该问题最早于格林威治时间周二深夜接近午夜时由蚂蚁集团 FG 的 codeplutos, meizjm3i 向 VMware 报告。周三,我们进行了调查、分析、确定修复方案和测试,目标是在周四紧急发布。与此同时,同样在周三,详细信息在线上被完全泄露,这就是为什么我们在发布和 CVE 报告之前提供此更新的原因。

漏洞

该漏洞影响运行在 JDK 9+ 上的 Spring MVC 和 Spring WebFlux 应用。特定的攻击方式要求应用打包成传统的 WAR 并在 Servlet 容器上部署。如果应用作为 Spring Boot 可执行 jar(即默认方式)部署,则不受此攻击方式的影响。然而,该漏洞的性质更为普遍,可能存在其他利用方式。

我是否受影响?

这是报告中特定场景的要求:

  • 运行在 JDK 9 或更高版本上。
  • 打包成传统的 WAR 并部署在独立的 Servlet 容器上。使用内嵌 Servlet 容器响应式 Web 服务器的典型 Spring Boot 部署不受影响。
  • 依赖于 spring-webmvcspring-webflux
  • Spring Framework 版本 5.3.0 到 5.3.17,5.2.0 到 5.2.19,以及更旧的版本。

补充说明

  • 该漏洞涉及 ClassLoader 访问,并取决于实际使用的 Servlet 容器。已知 Tomcat 10.0.19、9.0.61、8.5.77 及更早版本存在漏洞。Payara 和 Glassfish 也已知存在漏洞。其他 Servlet 容器也可能存在漏洞。
  • 该问题与用于从请求参数(查询参数或表单数据)填充对象的数据绑定有关。数据绑定用于带有 @ModelAttribute 注解或可选地不带该注解,且没有其他 Spring Web 注解的控制器方法参数。
  • 该问题与 @RequestBody 控制器方法参数(例如 JSON 反序列化)无关。但是,如果这些方法通过数据绑定从查询参数填充了其他方法参数,则仍可能存在漏洞。

状态

  • 包含修复的 Spring Framework 5.3.18 和 5.2.20 版本已发布。
  • 依赖于 Spring Framework 5.3.18 的 Spring Boot 2.6.6 和 2.5.12 版本已发布。
  • CVE-2022-22965 已发布。
  • Apache Tomcat 已发布 10.0.20、9.0.62 和 8.5.78 版本,这些版本从 Tomcat 端关闭了攻击向量,请参阅Spring Framework RCE,缓解替代方案

建议的临时解决方案

首选的应对措施是更新到 Spring Framework 5.3.185.2.20 或更高版本。如果已完成此操作,则无需其他临时解决方案。但是,某些情况下可能无法快速升级。因此,我们在下方提供了一些临时解决方案。

请注意,临时解决方案不一定是互斥的,因为安全措施最好是“纵深防御”。

升级 Tomcat

对于在 Tomcat 上运行且使用不受支持的 Spring Framework 版本的旧应用,升级到 Apache Tomcat 10.0.209.0.628.5.78 可以提供足够的保护。但是,这应被视为战术性解决方案,主要目标应是尽快升级到当前受支持的 Spring Framework 版本。如果采用此方法,您还应考虑设置禁止的字段以实现纵深防御。

降级到 Java 8

如果您既无法升级 Spring Framework 也无法升级 Apache Tomcat,则降级到 Java 8 是一个可行的临时解决方案。

禁止的字段

另一个可行的临时解决方案是通过在 WebDataBinder 上全局设置 disallowedFields 来禁用特定字段的绑定。


@ControllerAdvice
@Order(Ordered.LOWEST_PRECEDENCE)
public class BinderControllerAdvice {

    @InitBinder
    public void setAllowedFields(WebDataBinder dataBinder) {
         String[] denylist = new String[]{"class.*", "Class.*", "*.class.*", "*.Class.*"};
         dataBinder.setDisallowedFields(denylist);
    }

}

这通常有效,但作为集中应用的临时修复方案,可能会留下一些漏洞,特别是如果某个控制器通过其自身的 @InitBinder 方法在本地设置了 disallowedFields,这会覆盖全局设置。

为了以更安全的方式应用此临时解决方案,应用可以扩展 RequestMappingHandlerAdapter,以便在所有其他初始化完成后更新 WebDataBinder。为此,Spring Boot 应用可以声明一个 WebMvcRegistrations bean (Spring MVC) 或一个 WebFluxRegistrations bean (Spring WebFlux)。

例如在 Spring MVC 中(WebFlux 中类似)

package car.app;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.web.servlet.WebMvcRegistrations;
import org.springframework.context.annotation.Bean;
import org.springframework.web.bind.ServletRequestDataBinder;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.annotation.InitBinderDataBinderFactory;
import org.springframework.web.method.support.InvocableHandlerMethod;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter;
import org.springframework.web.servlet.mvc.method.annotation.ServletRequestDataBinderFactory;


@SpringBootApplication
public class MyApp {


	public static void main(String[] args) {
		SpringApplication.run(CarApp.class, args);
	}


	@Bean
	public WebMvcRegistrations mvcRegistrations() {
		return new WebMvcRegistrations() {
			@Override
			public RequestMappingHandlerAdapter getRequestMappingHandlerAdapter() {
				return new ExtendedRequestMappingHandlerAdapter();
			}
		};
	}


	private static class ExtendedRequestMappingHandlerAdapter extends RequestMappingHandlerAdapter {

		@Override
		protected InitBinderDataBinderFactory createDataBinderFactory(List<InvocableHandlerMethod> methods) {

			return new ServletRequestDataBinderFactory(methods, getWebBindingInitializer()) {

				@Override
				protected ServletRequestDataBinder createBinderInstance(
						Object target, String name, NativeWebRequest request) throws Exception {
					
					ServletRequestDataBinder binder = super.createBinderInstance(target, name, request);
					String[] fields = binder.getDisallowedFields();
					List<String> fieldList = new ArrayList<>(fields != null ? Arrays.asList(fields) : Collections.emptyList());
					fieldList.addAll(Arrays.asList("class.*", "Class.*", "*.class.*", "*.Class.*"));
					binder.setDisallowedFields(fieldList.toArray(new String[] {}));
					return binder;
				}
			};
		}
	}
}

对于不使用 Spring Boot 的 Spring MVC 应用,可以从使用 @EnableWebMvc 切换到直接扩展 DelegatingWebMvcConfiguration,具体描述请参阅文档的高级配置部分,然后重写 createRequestMappingHandlerAdapter 方法。

误解

曾有关于弃用 SerializationUtils 提交的猜测。该类在框架内仅有一处使用,且不暴露给外部输入。此次弃用与此漏洞无关。

这与刚在此漏洞报告发布前发布的Spring Cloud Function 的 CVE 产生了混淆。它也与此无关。

获取 Spring 新闻通讯

订阅 Spring 新闻通讯保持联系

订阅

抢占先机

VMware 提供培训和认证,助力您快速提升。

了解更多

获得支持

Tanzu Spring 通过单一订阅提供 OpenJDK™、Spring 和 Apache Tomcat® 的支持及二进制文件。

了解更多

即将举行的活动

查看 Spring 社区所有即将举行的活动。

查看全部