Spring4Shell: Security Analysis of the latest Java RCE '0-day' vulnerabilities in Spring

We discussed this vulnerability during Episode 133 on 04 April 2022

Sometimes vulnerabilities come from trying to be too generic/handle all the possibilities, this is one of those situations. What you have the Spring Framework letting users write simple Java classes with fields, getters/setters and setting those up as models for a particular endpoint. Spring will then try to map request parameters to class fields and set them appropriately, to do this it uses the java.beans.Introspection class which uses a bit of Java magic to look at the internal java representation for an arbitrary object and determine what fields have getters/setters and can be modified. It can also recurse into the nested objects.

While the fields you’d expect do come back, one unexpected property though is class. This is because any plain object/class in Java automatically inherits from Object.Class which has the getClass() method. Form there you can go on down the class tree, potentially leading to the ability to set some important and sensitive classes.

There was a 2010 attack taking advantage of this same issue, where the exploit targeted class.classLoader.URLs to trick the system into adding a remote address to the paths the class loader would look for classes in. This was patched by checking if the property name was classLoader and it was in the Class class.

This worked for awhile, it appears a few years later, they also blocked the “protectionDomain” field in this commit. Though I’m not sure about the details on that attack (if any).

So effectively the patches have been through blocklisting whatever means someone found to exploit it. That’s true for Spring4Shell too, Java 9 introduced Modules and with that the module field, which itself also exposed classLoader but as the module isn’t the Class class it bypasses the existing checks. Though, Java 9 doesn’t load remote JARs anymore, so the old exploit couldn’t be revived.

Instead, the exploitation takes a more round about way, through class.module.classLoader.resources.context.parent.pipeline.first which reminds me a lot of Python sandbox escaping where you work your way to the base python class then try and access different fields working your way to arbitrary classes. In this case it is working it way to some of Tomcat’s logging properties. A full exploit would use multiple requests to set several of these properties resulting in writting a shell to the server as a “log”.

class.module.classLoader.resources.context.parent.pipeline.first.pattern=%25%7Bprefix%7Di%20[url safe JSP file here]%20%25%7Bsuffix%7Di
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=shell
class.module.classLoader.resources.context.parent.pipeline.first.fileDateFormat=

While this particular exploit requires Tomcat, it is likely other fields could be abused in other situations to gain code execution.

The patch once again is block listing but they are a bit more comprehensive this time. Only allowing access to name from within Class and denying access to the ClassLoader and ProtectionDomain classes all together.