On the evening of March 31, the Spring community released an article called “Spring Framework RCE, Early Announcement”, which officially announced the recent Spring vulnerability. It confirms that the rumored vulnerabilities do exist and are not the CVEs announced on March 28th and 29th as many recent articles say, so if you are solving the problem according to those articles, please start over with this official announcement.

The identified RCE vulnerability in the Spring Core Framework is CVE number CVE-2022-22965.

CVE-2022-22965

The vulnerability was reported to VMware late Tuesday night by AntGroup FG’s codePlutos, meizjm3i. On Wednesday, Spring officials investigated the issue, analyzed it and determined a solution, while an emergency release was planned for Thursday.

Since the vulnerability was leaked on the web, Spring officials have released an emergency version of the fix in question, which is more extensive because it is a vulnerability in the Spring core framework. So in this blog post is also continuously updated progress, the following progress timeline as of this posting.

timeline

Here’s a look at the official content and solution of this mysterious vulnerability that has been rumored on the Internet for 2 days.

Scope of Impact

The following conditions need to be met for this vulnerability to be exploited.

  • JDK 9 +
  • Deployment with Apache Tomcat
  • Packaged using WAR
  • Relying on spring-webmvc or spring-webflux

Although it is likely that most users are still running with JDK 8, or using the built-in Tomcat, the characteristics of the vulnerability are more common and do not exclude the existence of other exploits. Therefore, it is recommended to upgrade to the latest version as soon as possible to avoid the possible risks.

Solution

Since this is not a webcast but an official Spring announcement, the solution is already relatively well developed and easy to implement. Affected users can address the risk of this vulnerability by doing the following.

  • Spring 5.3.x users upgrade to 5.3.18+
  • Spring 5.2.x users upgrade to 5.2.20+
  • Spring Boot 2.6.x users upgrade to 2.6.6+
  • Spring Boot 2.5.x users upgrade to 2.5.12+

For the recurrence of the vulnerability and more details, I won’t go into specifics here because of space limitations.

Then, it needs to be mentioned here again in particular that those who have already received the news and acted on it before, should have used the following solution to deal with it if they guessed correctly, right?

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
@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);
    }

}

This official Spring tweet confirms that the method works, but may leave some other pitfalls, especially when the Controller sets disalloedFields locally via its own @InitBinder method, which overrides the global setting.

To apply the solution in a more secure way, the application can extend RequestMappingHandlerAdapter to update WebDataBinder after all other initialization is finished. Officially, a better solution is given, such as the following.

 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
32
33
34
35
36
37
38
39
40
41
@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;
    }
   };
  }
 }
}

For Spring MVC users who are not under a Spring Boot application, you can switch directly from @EnableWebMvc to the extension DelegatingWebMvcConfiguration, as described in the advanced configuration section of the documentation, and then override the createRequestMappingHandlerAdapter method to do so.