文章内容
不久前(3月31日晚),Spring社区发布了一篇名为《Spring Framework RCE, Early Announcement》的文章,官宣了最近网传的Spring漏洞。这也证实了网传漏洞确实存在,并且并非最近很多文章说提到的3月28、29日公布的CVE。这次确定的Spring核心框架中的RCE漏洞,CVE号为CVE-2022-22965。
一、CVE-2022-22965
官方链接:https://spring.io/blog/2022/03/31/spring-framework-rce-early-announcement
Vmware:https://tanzu.vmware.com/security/cve-2022-22965
这个漏洞是在周二深夜,由AntGroup FG的codePlutos,meizjm3i向VMware报告。周三,Spring官方对该问题进行了调查、分析并确定了解决方案,同时计划在周四进行紧急版本的发布。由于该漏洞被泄漏在网络上,所以Spring官方紧急发布了相关修复的版本,因为是Spring核心框架中的漏洞,所以涉及面较广。
二、CVE-2022-22965影响范围
该漏洞的利用需要满足下面的条件:
- JDK 9 +
- 使用Apache Tomcat部署
- 使用WAR方式打包
- 依赖spring-webmvc或spring-webflux
虽然可能国内大部分用户还在用JDK 8、或者采用内置Tomcat的方式运行,但由于该漏洞的特性比较普遍,不排除其他利用方式的存在。所以,还是建议在有条件的情况下,尽快升到最新版本来避免可能存在的风险发生。
三、CVE-2022-22965解决方案
因为这次不是网传,而是Spring官宣,所以解决方案已经相对完善和容易了,受影响的用户可以通过下面的方法解决该漏洞的风险:
- Spring 5.3.x用户升级到5.3.18+
- Spring 5.2.x用户升级到5.2.20+
- Spring Boot 2.6.x用户升级到2.6.6+
- Spring Boot 2.5.x用户升级到2.5.12+
对于该漏洞的复习及更多细节,这里因为篇幅有限,就不具体介绍了。然后,这里需要特别再提一下,之前已经收到消息并有所行动的小伙伴,没猜错的话应该都是用下面的解决方案来处理的吧?
@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);
}
}
这次Spring官方推文里,证实了该方法是有效的,但可能会留下一些其他隐患,特别是当Controller通过其自己的@InitBinder方法在本地设置disalloedFields时,该方法会覆盖全局设置。
为了以更安全的方式应用解决方案,应用程序可以扩展RequestMappingHandlerAdapter,以便在所有其他初始化结束后更新WebDataBinder。官方给出了更好的解决方案,比如下面这样:
@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,如文档的高级配置部分所述:https://docs.spring.io/spring-framework/docs/current/reference/html/web.html#mvc-config-advanced-java,然后重写createRequestMappingHandlerAdapter方法来实现。
四、老版本的Spring应用该如何应对CVE-2022-22965漏洞?
这次的RCE漏洞宣布之后,官方给出的主要解决方案是升级版本,但只有Spring 5.2、5.3和Spring Boot 2.5、2.6提供了对应的升级版本。那么对于一些还在用Spring 5.0、5.1甚至Spring 4.x、或者Spring Boot 1.x和Spring 2.4及以下版本的用户该怎么办呢?
1、第一种方法
官方给出过一种通过扩展RequestMappingHandlerAdapter来实现的方法。同时也给出了一个Spring Boot下使用Spring MVC的实现方案,如果是WebFlux的话略做修改即可。但如果不是Spring Boot的话,则Bean的初始化方式还要再改改。
@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机制不太熟悉的话,可能还会遇到不少麻烦。
2、第二种方法
下面要讲的方法主要是规避的思路。什么是规避呢?就是针对该漏洞的利用条件去做一些调整。比如,这次漏洞的条件是这些:
- JDK 9 +
- 使用Apache Tomcat部署
- 使用WAR方式打包
- 依赖spring-webmvc或spring-webflux
那么我就可以选择规避其中的1个条件就能防止漏洞的利用了,比如:
- 降级到JDK 8
- 使用Undertow来部署
- 如果是Spring Boot的早期项的话,还能调整打包方式,采用JAR的方式打包和运行来规避。
另外,这次漏洞之后Tomcat的版本也更新了,所以当你用WAR部署的情况下,可以直接下载最新的Tomcat版本来规避也是一种不错的选择。