The main goal of this article is to analyze, summarize, and summarize the historical Spring framework vulnerabilities, so as to try to find out the potential patterns, in order to learn from the past. Of course, as a Java novice, before directly analyzing the vulnerabilities, we will first learn some core concepts in Spring from the developer’s perspective, so as to lay the foundation for the subsequent understanding.

Preface

Spring was originally created in 2003 as a competitor to J2EE due to the complexity of the early J2EE specifications and the lack of community input. As the years have passed, Spring has developed a complementary relationship with JavaEE, not embracing the JavaEE platform specifications, but integrating carefully selected individual specifications from it, such as Servlet, JPA, JMS, and so on.

When we talk about Spring, we can either refer to the Spring framework project itself or to the entire family of projects based on the Spring framework, and we can see more vulnerabilities in the projects on top of the framework when we introduce specific vulnerabilities later.

Spring Framework

The Spring Framework is a powerful Java application framework designed to provide an efficient and scalable development environment. It is also inherently modular, and applications can select the modules they need. Examples include spring-core, a module that contains the core container, configuration model, and dependency injection mechanism, SpEL, a scripting engine, and the Servlet-based Spring MVC Web and Spring WebFlux frameworks.

In addition to the Spring framework itself, there are other projects such as Spring Boot, Spring Security, Spring Data, Spring Cloud, Spring Batch, etc., each with its own source code repository and versioning. A complete list of projects can be found at spring.io/projects. This section focuses on the core concepts and historical vulnerabilities of Spring Framework itself, other projects will be covered separately in the following sections.

Dependency Injection and IoC

Any article that introduces the Spring framework is inseparable from IoC, which is called Inversion of Control. Many articles on the web say that IoC/Dependency Injection is the process of constructing an instance of a bean from an XML configuration file through reflection, but they don’t explain the purpose of doing so.

To understand IoC, you need to understand Dependency Injection (DI) first. In Java, dependency injection is essentially a design pattern. The basic principle of Dependency Injection is that the application component should not be responsible for finding resources or other dependent collaborative objects, but the container should be responsible for configuring the objects, and the logic of finding resources should be extracted from the application component’s code and left to the DI container.

For example, suppose class A needs to use the methods of interface B. Then you need to establish an association, or dependency, between class A and interface B. The most primitive way is to create an instance of interface B in class A, but this approach requires the developer to maintain the dependencies manually and requires refactoring and modification of the code if the dependencies change. Dependency injection was created to solve this problem. With Dependency Injection, the dependencies are managed by the DI container, and the container configuration file only needs to be modified when the dependencies change.

In short, if a bean is a dependency of another bean, this usually means that one bean is set as a property of the other bean.

The objects that form the backbone of a user’s application in Spring and are managed by the Spring IoC container are called beans, which are similar in structure to Java beans but have their lifecycle managed by the Spring IoC container, and the Spring beans and their dependencies are reflected in the configuration metadata used by the container, which can come from XML configuration metadata can come from XML configuration files or Java annotations.

The org.springframework.beans and org.springframework.context packages are the basis for the Spring Framework’s IoC container, where the BeanFactory interface provides a configuration mechanism that can manage any type of object.

ApplicationContext is a sub-interface of BeanFactory that represents the Spring IoC container responsible for instantiating, configuring and assembling beans; there are several common built-in implementations of this interface. For example, ClassPathXmlApplicationContext and FileSystemXmlApplicationContext.

One of the core features of the Spring framework is the IoC container, and for developers, all they need to do is tell the container which beans need to be managed, which is usually the Spring configuration process. two ways of configuring the IoC container are provided in Spring, XML-based configuration and annotation-based configuration.

An example of an XML-based configuration is shown below.

1
2
3
4
5
6
<bean id="userService" class="com.example.UserService">
   <property name="userDao" ref="userDao"/>
</bean>

<bean id="userDao" class="com.example.UserDao">
</bean>

Spring reads the configuration file and creates the corresponding POJO object (bean) through the IoC container, manages its lifecycle and dynamically injects the corresponding dependencies.

A similar effect can be achieved by using annotations, as shown below.

1
2
3
4
5
6
7
8
@Component
public class UserService {

    @Autowired
    private UserDao userDao;

    // Required setters and methods
}

Both approaches have their advantages and disadvantages, and both Spring provides a lot of detailed configuration methods, such as setting scopes, factory classes and constructor calling rules, etc. You can refer to the official documentation for detailed configuration methods.

Spring MVC

As a Web framework, the Spring Framework naturally includes traditional Web MVC functionality at its core. As mentioned earlier, the Spring Framework is divided into modules, and the MVC framework has been a core module in Spring since the beginning, with code in the spring-webmvc subdirectory of github.com/spring-projects/spring-framework. Its formal name is “Spring Web MVC”, and it is often referred to as Spring MVC. Note that this is a module in the Spring Framework and not a separate project.

Servlets are one of the key standards in Java EE web servers, and applications that meet the Servlet specification can be loaded and executed by the standard Servlet container. For Spring MVC, which also embraces this standard, an important Servlet is the DispatcherServlet, and from the 6.0 source code, the DispatcherServlet inheritance chain is as follows.

1
DispatcherServlet -> FrameworkServlet -> HttpServletBean -> HttpServlet

Like all other Servlets, we can register and initialize the DispatcherServlet through the web.xml or ServletContext interfaces and implement web request processing by adding controllers. spring MVC provides an annotation-based programming model. The @Controller and @RestController components use annotations to express request mapping, request input, exception handling and more. Annotated controllers have flexible method signatures, do not need to inherit from base classes, and do not need to implement specific interfaces. Here is a simple example.

1
2
3
4
5
6
7
8
9
@Controller
public class HelloController {

    @GetMapping("/hello")
    public String handle(Model model) {
        model.addAttribute("message", "Hello World!");
        return "index";
    }
}

The controller example above accepts a Model instance and returns a string representing the view name; it can actually support a variety of flexible method signatures, which the Spring Framework will parse appropriately. A more common example is as follows.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
@RestController
@RequestMapping("/persons")
class PersonController {

    @GetMapping("/{id}")
    public Person getPerson(@PathVariable Long id) {
        return PersonDao.getById(id);
    }

    @PostMapping
    @ResponseStatus(HttpStatus.CREATED)
    public void add(@RequestBody Person person) {
        // ...
    }
}

@RequestMapping represents a shared mapping at the class level, while @RestController can be thought of as a combination of @Controller and @ResponseBody. @ResponseBody means that the return of the controller method is bound directly to the return body of the Web request.

The Controller method can return a single asynchronous result by returning DeferredResult or Callable, or multiple asynchronous results by ResponseBodyEmitter, SseEmitter, the recent hot ChatGPT streaming response is based on SSE (Server-Sent Events).

Spring MVC for the request and response to provide a flexible processing interface, here is only a general understanding of its basic methods, later introduced when the specific vulnerability to expand the introduction.

Spring WebFlux

Spring WebFlux is an emerging web framework often compared to Spring MVC, introduced in Spring Framework 5.0 with the module name spring-webflux . It is a web framework based on the reactive technology stack, with full asynchronous support, support for Reactive Streams backpressure and more.

Backpressure: A technical term for the amount that consumers can tell producers how much they need to avoid buffer overflows or data loss caused by higher generation rates than consumption rates.

The use of Spring WebFlux is similar to Spring MVC, with the major difference being that WebFlux supports reactive types in the model, such as Mono<User> or io.reactivex.Single<User>, as shown in the following simple example.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
@RestController
public class UserController {
    private final UserRepository userRepository;

    public UserController(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    @GetMapping("/users")
    public Flux<User> getAllUsers() {
        return userRepository.findAll();
    }

    @GetMapping("/users/{id}")
    public Mono<User> getUserById(@PathVariable String id) {
        return userRepository.findById(id);
    }
}

Spring MVC already supports asynchronous non-blocking, so why introduce WebFlux? One of the main reasons is performance, fully asynchronous processing can reduce the number of concurrent threads. And the introduction of the asynchronous API can take advantage of the lambda expression feature introduced in Java 8 for functional programming, allowing declarative combinations of asynchronous logic, as shown below.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
@PostMapping("/owners/{ownerId}/pets/{petId}/edit")
public Mono<String> processSubmit(@Valid @ModelAttribute("pet") Mono<Pet> petMono) {
    return petMono
        .flatMap(pet -> {
            // ...
        })
        .onErrorResume(ex -> {
            // ...
        });
}

SpEL

SpEL is the Spring Expression Language, whose syntax is similar to the standard JUEL, but provides additional features such as method invocation and string templates for rich functionality. SpEL is one of the core parts of the Spring framework and is used in the XML configuration, annotations, and MVC controllers of the Spring framework.

Use SpEL to set the properties of a bean in XML.

1
2
3
4
5
<bean id="numberGuess" class="org.spring.samples.NumberGuess">
    <property name="randomNumber" value="#{ T(java.lang.Math).random() * 100.0 }"/>

    <!-- other properties -->
</bean>

Set the field defaults using SpEL in the annotation.

1
2
3
4
5
6
7
public class FieldValueTestBean {

    @Value("#{ systemProperties['user.region'] }")
    private String defaultLocale;

    // ...
}

SpEL is derived from Spring, but can be used independently, for example.

1
2
3
ExpressionParser parser = new SpelExpressionParser();
Expression exp = parser.parseExpression("new String('hello world').toUpperCase()"); (1)
String message = exp.getValue(String.class);

It is worth noting that the Expression.getValue method has several overloads, some of which accept an additional parameter of type EvaluationContext. The EvaluationContext interface is used when evaluating expressions to resolve properties, methods or fields, and helps perform type conversions. provides two implementations, namely.

  1. StandardEvaluationContext: supports all features and configuration options of the SpEL language, which is the default value;
  2. SimpleEvaluationContext: provides only a subset of the basic functionality and configuration options of the SpEL language, for cases where the full functionality of the SpEL language is not needed and there are restrictions on expressions;

In general, the use of the default StandardEvaluationContext with user-controllable expressions can lead to arbitrary Java code execution, when the SimpleEvaluationContext is typically used to restrict the expression to access only a limited number of properties. A similar vulnerability can be seen later.

Historical Vulnerabilities

This section documents and analyzes some historical vulnerabilities in the Spring Framework. Since the Spring Framework itself is the foundation of the Spring meta-universe, vulnerabilities that occur have a wide impact and deserve our focused attention. For space reasons, we won’t go too far into the analysis here, and we’ll give links to the best existing analysis articles where possible.

CVE-2010-1622

Spring MVC allows developers to bind business objects (beans) to HTML forms and modify them with requests such as the one below.

1
2
3
POST /adduser HTTP/1.0
...
firstName=Tavis

If the business object User is bound, then the request will end up executing user.firstName = "Tavis" behind the scenes. This is implemented based on the JavaBean interface.

Spring also supports the use of complex settings, for example user.address.street=Test will be converted to the following.

1
frmObj.getUser().getAddress().setStreet("Test")  

Since the Bean class inherits from Object by default and the properties of the parent class are not filtered out during introspection, an attacker can call frmObj.getClass().getClassLoader() by means of class.classLoader to further modify the properties in the ClassLoader and resulting in arbitrary code execution. The initial author’s suggestion was to prevent the problem by specifying stopClass through Introspector.getBeanInfo(Person.class, Object.class), but Spring actually fixed it by blacklisting, which also laid the groundwork for the subsequent bypass.

A detailed analysis can be found at: https://blog.o0o.nu/2010/06/cve-2010-1622.html

CVE-2013-4152

Spring MVC can bind requested data to Bean objects, which can be forms, XML, JSON, etc. This vulnerability is an XXE injection issue caused by parsing XML external entities when binding XML requests to Bean objects. The sample Controller is as follows.

1
2
3
4
5
6
7
8
@Controller
public class HomeController {
    @RequestMapping(value="/home", method=RequestMethod.POST, consumes="application/xml")
    public ModelAndView home(@RequestBody User user) {
        //System.out.println(user);
        return new ModelAndView("home", "message", user.getUserName());
    }
} 

User is defined as follows.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement(name = "user")
public class User {
   private String userID;
   private String userName;
   @XmlElement
   public String getUserID() { return userID; }
   public void setUserID(String userID) { this.userID = userID; }
   @XmlElement
   public String getUserName() { return userName; }
   public void setUserName(String userName) { this.userName =userName; }
} 

The fix for this type of vulnerability depends on the XML API used.

  • DOM: use DocumentBuilderFactory.setExpandEntityReferences(false); to disable external entities;
  • SAX: use XMLReader.setFeature("http://xml.org/sax/features/external-general-entities", false); to disable external entities;
  • StAX: XMLInputFactory.setProperty(XMLInputFactory.IS_REPLACING_ENTITY_REFERENCES, false);;
  • Spring OXM: DOM-like and uses Jaxb2Marshaller to specify the DOMSource;

The key fix code for this vulnerability is as follows.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
--- spring-web/src/main/java/org/springframework/http/converter/xml/Jaxb2CollectionHttpMessageConverter.java	(revision 2843b7d2ee12e3f9c458f6f816befd21b402e3b9)
+++ spring-web/src/main/java/org/springframework/http/converter/xml/Jaxb2CollectionHttpMessageConverter.java	(revision )
@@ -226,7 +226,9 @@
 	 * @return the created factory
 	 */
 	protected XMLInputFactory createXmlInputFactory() {
-		return XMLInputFactory.newInstance();
+		XMLInputFactory inputFactory = XMLInputFactory.newInstance();
+		inputFactory.setProperty(XMLInputFactory.IS_REPLACING_ENTITY_REFERENCES, false);
+		return inputFactory;
 	}
 
 }

Detailed information can be found below.

There are also several similar external entity injection vulnerabilities, such as CVE-2013-7315, CVE-2013-6429, in addition to the bypass CVE-2014-0054 caused by the incomplete fix above, as the above only fixes the Jaxb2CollectionHttpMessageConverter and does not fix the Jaxb2RootElementHttpMessageConverter . In general, it is still necessary to carefully audit the XML parsing processing-related functions, and I personally believe that this is also a major problem caused by the XXE function not being disabled by default.

CVE-2014-3625

Spring MVC, while being an MVC framework, also supports common features in web servers, such as static resource access. For example, we can configure XML to set the static resources we need to access, here is a simple spring-web-config.xml example.

1
2
3
4
5
 <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
     <property name="prefix" value="/WEB-INF/views/" />
     <property name="suffix" value=".jsp" />
 </bean>
<mvc:resources mapping="/resources/**" location="/resources/">

where the <mvc:resources> tag indicates that the request for the URL path /resources is mapped to a static resource to a file under the relative path /resources in the root of the web application.

Or you can add it via Java code, see spring-showcase.

1
2
3
4
5
// org.springframework.samples.mvc.config.WebMvcConfig
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
    registry.addResourceHandler("/resources/**").addResourceLocations("/resources/");
}

The POC for this vulnerability is relatively simple, see the demo code below.

1
2
3
4
5
$ git clone https://github.com/ilmila/springcss-cve-2014-3625/ && cd springcss-cve-2014-3625/
$ mvn jetty:run
$ curl 'http://localhost:8080/spring-css/resources/file:/etc/passwd'
root0:0:root:/root:/bin/bash
...

The cause of the vulnerability lies in the static resource parsing place, and the invocation link is as follows.

1
2
3
4
5
org.springframework.web.servlet.resource.ResourceHttpRequestHandler#handleRequest
org.springframework.web.servlet.resource.ResourceHttpRequestHandler#getResource
org.springframework.web.servlet.resource.PathResourceResolver#getResource
org.springframework.core.io.Resource#createRelative
org.springframework.core.io.PathResource#createRelative

The relevant code is as follows (v4.1.1.RELEASE).

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
  protected Resource getResource(HttpServletRequest request) throws IOException{
    String path = (String) request.getAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE);
    if (path == null) {
      throw new IllegalStateException("Required request attribute '" +
          HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE + "' is not set");
    }
    if (!StringUtils.hasText(path) || isInvalidPath(path)) {
      if (logger.isTraceEnabled()) {
        logger.trace("Ignoring invalid resource path [" + path + "]");
      }
      return null;
    }
    Resource resource = resolveChain.resolveResource(request, path, getLocations());
    if (resource == null || getResourceTransformers().isEmpty()) {
      return resource;
    }
    ResourceTransformerChain transformChain = new DefaultResourceTransformerChain(resolveChain, getResourceTransformers());
    resource = transformChain.transform(request, resource);
    return resource;
  }
  protected boolean isInvalidPath(String path) {
    return (path.contains("WEB-INF") || path.contains("META-INF") || StringUtils.cleanPath(path).startsWith(".."));
  }

The problem is that Resource#createRelative returns a result of file:/etc/passwd. This led to a traversal of the subdirectory restriction implementation. The fix for this vulnerability has gone through multiple commits and can be found at the following link.

Another vulnerability derived from this vulnerability is CVE-2018-1271, namely arbitrary file reading under Spring MVC Windows, because the path filtering only considers ../, not considering the case of \ in Windows, for more details, please refer to CVE-2018-1271: Directory Traversal with Spring MVC on Windows.

CVE-2018-1270

This vulnerability is a Spring Messaging remote code execution vulnerability. spring Messaging is also an optional module in the Spring framework, not described earlier, whose code module directory is spring-messaging. it provides a mechanism for messaging between applications, most typically Websocket functionality, and on top of The most typical is the Websocket functionality, as well as the STOMP (Simple Text Oriented Messaging Protocol) protocol on top of Websocket, in addition to providing a mock alternative to SockJS for cases where Websocket is not supported.

Messaging is primarily targeted at full-duplex asynchronous transfers, and therefore also uses the common publish/subscribe mechanism. Multiple clients can subscribe to the same topic waiting for messages to achieve a chat room-like effect.

Returning to the vulnerability, the PoC is as follows, with the main feature being the addition of a custom selector header when subscribing to /topic/greetings.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
function connect() {
    var header  = {"selector":"T(java.lang.Runtime).getRuntime().exec('calc.exe')"};
    var socket = new SockJS('/gs-guide-websocket');
    stompClient = Stomp.over(socket);
    stompClient.connect({}, function (frame) {
        setConnected(true);
        console.log('Connected: ' + frame);
        stompClient.subscribe('/topic/greetings', function (greeting) {
            showGreeting(JSON.parse(greeting.body).content);
        },header);
    });
}

The selector is retrieved on the server side and executed as a SpEL expression, as follows.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
// org/springframework/messaging/simp/broker/DefaultSubscriptionRegistry.java
  private MultiValueMap<String, String> filterSubscriptions(
      MultiValueMap<String, String> allMatches, Message<?> message) {
    EvaluationContext context = null;
    for (String sessionId : allMatches.keySet()) {
      for (String subId : allMatches.get(sessionId)) {
        // ...
        Expression expression = sub.getSelectorExpression();
        if (context == null) {
          context = new StandardEvaluationContext(message);
          context.getPropertyAccessors().add(new SimpMessageHeaderPropertyAccessor());
        }
        // ..
        if (Boolean.TRUE.equals(expression.getValue(context, Boolean.class))) {
        result.add(sessionId, subId);
        }
      }
    }
    // ...

Version: 5.0.4.RELEASE

As mentioned earlier, expression.getValue executes the corresponding expression, and if the content of the expression is controllable, it can lead to arbitrary code execution. The fix for this class is to set the execution context EvaluationContext to SimpleEvaluationContext to restrict the behavior of expressions. For a specific patch, see the following link.

As seen in this vulnerability, there are many places within the Spring framework that use SpEL to implement dynamic filtering, attribute access, and other features that could cause unintended harm if left unchecked.

CVE-2020-5398

This is an RFD vulnerability in Spring Framework, a new web attack technique introduced in Blackhat EU 2014. The main idea of the attack is to achieve the file download function for the target domain through the feature of certain Web interfaces returning user requests, combined with the browser automatically downloading files and the technique of forging file suffixes in the URL. For example, the following domain name adds an extra suffix through the path parameter ;, so that the browser automatically recognizes the downloaded file as ChromeSetup.bat, and the HTTP response contains the content of the GET parameter q, and finally executes calc.exe through the || feature of Windows commands, bypassing the previous useless data.

1
https://www.google.com/s;/ChromeSetup.bat?q=||calc

This is only a cursory introduction, and the details of the RFD vulnerability can be found in the original author’s white paper later in this article.

Back to Spring, this vulnerability causes RFD because when returning files through Spring’s interface, RFD can be achieved if the user can control some of the returned file names. Specifically, when using the Content-Disposition header returned by the org.springframework.http.ContentDisposition class, a user can affect the return header by controlling the malicious filename. The following two interfaces are primarily affected:

1
2
ContentDisposition.Builder#filename(String)
ContentDisposition.Builder#filename(String, US_ASCII)

CVE-2022-22965

This vulnerability is the well-known Spring4Shell vulnerability, but in terms of the principle of the vulnerability, its core is actually a bypass of CVE-2010-1622, which was described in the previous article. As mentioned earlier, when fixing CVE-2010-1622, developers used a blacklist to restrict the bean property name to classLoader or protectionDomain, which did solve the problem at the time. However, with the introduction of Java 9, some new properties were introduced, such as module, so you can use this property to bypass the above restriction and modify the classLoader again to achieve RCE.

However, the PoC for this vulnerability is actually a webshell write that takes advantage of Tomcat’s log writing feature. This is because there are more restrictions on how to directly modify the classLoader. Although there are many analysis articles on the Internet, most of them only debug the PoC single step, and do not mention the core of the vulnerability. I believe that the core of the vulnerability still lies in Spring for JavaBean introspection API improper use, that is, Introspector.getBeanInfo does not specify the stopClass caused.

The patch for this vulnerability is divided into several commits, starting with the first patch.

 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
diff --git a/spring-beans/src/main/java/org/springframework/beans/CachedIntrospectionResults.java b/spring-beans/src/main/java/org/springframework/beans/CachedIntrospectionResults.java
index 8332045197..bd234eb58f 100644
--- a/spring-beans/src/main/java/org/springframework/beans/CachedIntrospectionResults.java
+++ b/spring-beans/src/main/java/org/springframework/beans/CachedIntrospectionResults.java
@@ -22,6 +22,7 @@ import java.beans.Introspector;
 import java.beans.PropertyDescriptor;
 import java.lang.reflect.Method;
 import java.lang.reflect.Modifier;
+import java.net.URL;
 import java.security.ProtectionDomain;
 import java.util.Collections;
 import java.util.HashSet;
@@ -292,10 +293,12 @@ public final class CachedIntrospectionResults {
                                        // Only allow all name variants of Class properties
                                        continue;
                                }
-                               if (pd.getWriteMethod() == null && pd.getPropertyType() != null &&
-                                               (ClassLoader.class.isAssignableFrom(pd.getPropertyType()) ||
-                                                               ProtectionDomain.class.isAssignableFrom(pd.getPropertyType()))) {
-                                       // Ignore ClassLoader and ProtectionDomain read-only properties - no need to bind to those
+                               if (URL.class == beanClass && "content".equals(pd.getName())) {
+                                       // Only allow URL attribute introspection, not content resolution
+                                       continue;
+                               }
+                               if (pd.getWriteMethod() == null && isInvalidReadOnlyPropertyType(pd.getPropertyType())) {
+                                       // Ignore read-only properties such as ClassLoader - no need to bind to those
                                        continue;
                                }

Restricts the types of introspective get attributes. Blacklisted attribute types include .

  • Attributes named content and of type URL
  • Attributes that do not have a setter and are of type blacklisted include.
    • AutoCloseable
    • ClassLoader
    • ProtectionDomain

Then it was enhanced again two weeks later.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
diff --git a/spring-beans/src/main/java/org/springframework/beans/CachedIntrospectionResults.java b/spring-beans/src/main/java/org/springframework/beans/CachedIntrospectionResults.java
index 7b7a67d91c..4187097ce3 100644
--- a/spring-beans/src/main/java/org/springframework/beans/CachedIntrospectionResults.java
+++ b/spring-beans/src/main/java/org/springframework/beans/CachedIntrospectionResults.java
@@ -286,9 +287,13 @@ public final class CachedIntrospectionResults {
                        // This call is slow so we do it once.
                        PropertyDescriptor[] pds = this.beanInfo.getPropertyDescriptors();
                        for (PropertyDescriptor pd : pds) {
-                               if (Class.class == beanClass &&
-                                               ("classLoader".equals(pd.getName()) ||  "protectionDomain".equals(pd.getName()))) {
-                                       // Ignore Class.getClassLoader() and getProtectionDomain() methods - nobody needs to bind to those
+                               if (Class.class == beanClass && (!"name".equals(pd.getName()) && !pd.getName().endsWith("Name"))) {
+                                       // Only allow all name variants of Class properties
+                                       continue;
+                               }
+                               if (pd.getPropertyType() != null && (ClassLoader.class.isAssignableFrom(pd.getPropertyType())
+                                               || ProtectionDomain.class.isAssignableFrom(pd.getPropertyType()))) {
+                                       // Ignore ClassLoader and ProtectionDomain types - nobody needs to bind to those
                                        continue;
                                }

The Class class is further restricted to allow only the name related properties in it. It’s full of patchiness, but it works!

Subsequent code updates put blacklisted properties like classLoader into isInvalidReadOnlyPropertyType, which is what we see now.

The referenced article is as follows:

Summary

This section briefly studied some of the features and functionality of Spring Framework itself, and based on this, a few randomly selected relevant vulnerabilities were analyzed to try to have a preliminary understanding of the overall structure, history of vulnerabilities and code quality of Spring Framework. It can be found that the Spring Framework, as the cornerstone of the popular Web framework, has not had many serious vulnerabilities in its history, RCE-type vulnerabilities are more often found in SpEL, and the JavaBean binding in the MVC framework can be considered a unique attack surface. As a security researcher, on the one hand, we can analyze these historical attack surfaces in depth to discover new ways to bypass them; on the other hand, we can also look for new attack surfaces, which often have a greater impact.

It is worth mentioning that the vulnerabilities introduced here are not complete due to the author’s level of knowledge, and more Spring Framework vulnerabilities can be found in the official bulletin at Spring Security Advisories - springio/security

At the same time, for researchers who want to dive deep inside the Spring framework to find new attack surfaces, it might be a better option to carefully read its official documentation and debug and analyze it in conjunction with the source code at:

The Spring project has many rich projects besides the Framework itself, such as Spring Boot, Spring Data, Spring Security, etc. Each project has a separate code repository, which is different from the modules of the Spring Framework. The list of projects can be found in: spring.io/projects.

We will continue to study some of the mainstream Spring projects when we have time, so that we can have a deeper understanding of the local design in conjunction with the overall Spring ecosystem.

Reference: https://evilpan.com/2023/04/21/spring-framework-bugs/