Safer deserialization in Spring Security OAuth2

The Java standard library provides the ObjectInputStream class which offers a convenient way for deserializing Java objects. Unfortunately, this way is not safe by default. Using this class may open the doors for Java deserialization attacks which in the worse case may result in arbitrary code execution.

I recently discovered that Spring Security OAuth2 library may be vulnerable to such an attack. Fortunately, there is one strong pre-requisite for a successful attack which may be difficult to meet for an adversary. Nevertheless, I thought it might be better to make the library a bit safer, and the project maintainers kindly accepted the contribution. Here are the details.

Safer deserialization in Spring Security OAuth 2.4.0

The issue

Spring Security OAuth2 can store authentication info and user details to a SQL or Redis database. Before storing data to the database, the library serialize it with the default Java serialization mechanism offered by theObjectOutputStream. Then, after reading the data from the database, the library unsafely deserializes it with the ObjectInputStream class. See SerializationUtils class:

public static  T deserialize(byte[] byteArray) {
         ObjectInputStream oip = null;
         try {
             oip = new ConfigurableObjectInputStream(
                     new ByteArrayInputStream(byteArray),
                     Thread.currentThread().getContextClassLoader());
             @SuppressWarnings("unchecked")
             T result = (T) oip.readObject();

The ConfigurableObjectInputStream class, which is provided by the Spring Framework, just wraps the InputObjectStream class without adding any security check.

It means that if an attacker is able to put malicious data into the database, then he can in the worse case execute arbitrary code, and as a result, compromise the whole application. However, there are a couple of requirements for a successful exploit.

First, the attacker has to build a deserialization gadget using classes that are available in the application’s JVM. Most probably it should not be a big problem since the Java standard library provides many dangerous classes. Plus, an application can load many libraries which may also help to build a deserialization gadget.

Second, to take advantage of this deserialization flaw, the attacker has to find a way how he can put malicious data into the database. For example, he can try to find an SQL injection. However, it may be not that easy, so that this requirement may be difficult to meet.

I found the problem by reviewing the code. Then, I reported it to the Pivotal Security Team. But they decided not to consider it as a vulnerability in the library because of the second requirement above. However, they welcomed a patch that adds a defense-in-depth measure to prevent such deserialization attacks.

The solution

To prevent Java deserialization vulnerabilities, an application has to restrict a set of classes which may be deserialized. One of the best ways is implementing a whitelist of allowed classes. There are three ways of how such a protection mechanism can be implemented:

  1. Use the filtering API introduced in JEP 290.
  2. Use the ValidatingObjectInputStream class from Apache Commons IO.
  3. Override the ObjectInputStream.resolveClass() method, and implement the whitelisting there.

The first way would require an application to use the Java versions which have JEP 290. The second way would add an additional dependency to Spring Security OAuth. I decided to follow the third way since it doesn’t introduce any additional dependency:

  • Added a new SaferObjectInputStream class which checks if classes are allowed for deserialization.
  • Defined a whitelist of classes as java.lang.*, java.util.* and org.springframework.security.*.
  • Updated the RedisTokenStore class to use the SaferObjectInputStream with the whitelist.
  • Updated the JDBC classes to use the SaferObjectInputStream with the whitelist.

The update was released in Spring Security OAuth2 2.3.7 but unfortunately, the solution caused a problem. It turned out that the whitelist is too strict. If an application stores custom implementations of tokens or user details, then the new version of the library fails to deserialize them due to the restrictive whitelist. Furthermore, there was no way how a user could modify the whitelist to make the application work again. The issue was reported by several users.

I fixed the problem in 2.4.0 by introducing a new API which allows a user to specify a custom whitelist. The default whitelist still contains java.lang.*, java.util.* and org.springframework.security.* classes. At first, I updated the library to apply the whitelist by default. But then, the project maintainer asked me to make it an opt-in option. The reason was that 2.4.0 is a minor release, so that it should not introduce any problem for an application when it migrates to the new version.

Here is what I did to make deserialization great again:

  1. Added a new SerializationStrategy interface with two implementations: DefaultSerializationStrategy and WhitelistedSerializationStrategy.
  2. The DefaultSerializationStrategy uses unsafe deserialization.
  3. The WhitelistedSerializationStrategy allows specifying a whitelist of classes which are allowed for deserialization. If no classes specified, the strategy uses the default whitelist: java.lang.*, java.util.* and org.springframework.security.*.
  4. Added a new static SerializationStrategy field to the SerializationUtils class. The default strategy is unsafe DefaultSerializationStrategy.
  5. The strategy can be overridden by calling a new SerializationUtils.setSerializationStrategy() method, or by specifying the strategy in the META-INF/spring.factories file.

Now a user can enable the default whitelist with the following code:

SerializationUtils.setSerializationStrategy(new WhitelistedSerializationStrategy());

Or, the user can implement his own serialization strategy, for example, by extending the WhitelistedSerializationStrategy class:

package org.custom.impl.oauth2;

public class CustomSerializationStrategy
    extends WhitelistedSerializationStrategy {

        private static final List<String> ALLOWED_CLASSES 
                = new ArrayList<String>();
        static {
            ALLOWED_CLASSES.add("java.lang.");
            ALLOWED_CLASSES.add("java.util.");
            ALLOWED_CLASSES.add("org.springframework.security.");
            ALLOWED_CLASSES.add("org.custom.impl.oauth2.");
        }

        CustomSerializationStrategy() {
            super(ALLOWED_CLASSES);
        }
    }
}

And here is how the user can specify the WhitelistedSerializationStrategy strategy in META-INF/spring.factories file:

org.springframework.security.oauth2.common.util.SerializationStrategy = \
org.springframework.security.oauth2.common.util.WhitelistedSerializationStrategy

Of course, META-INF/spring.factories allows using a custom serialization strategy as well.

Conclusion

By default, Spring Security OAuth2 library still uses deserialization in an unsafe way which may make an application vulnerable. Although the discussed deserialization flaw may be difficult to exploit, it may be better to enable the WhitelistedSerializationStrategy to be on the safe side.

References

  1. Spring Security OAuth
  2. ObjectInputStream
  3. JEP 290
  4. ValidatingObjectInputStream

CVE-2019-12415: XML processing vulnerability in Apache POI

Apache POI is a popular Java library for working with Microsoft documents. For example, it allows you reading and writing Microsoft Excel files using Java. When I was recently looking into the library, I noticed a little vulnerability which then became CVE-2019-12415. The issue has been fixed in POI 4.1.1. Below are the details.

CVE-2019-12415: XML processing vulnerability in Apache POI
Continue reading

Safer deserialization with new Jackson 2.10

New Jackson 2.10 was released on Sep 26th, 2019. Everyone who uses the library and also scans their applications for known vulnerabilities knows about the problem with endless CVEs that have been reporting against Jackson. Let’s try to understand what makes an application vulnerable and how the new version of Jackson can help to prevent deserialization vulnerabilities.

Safer deserialization with new Jackson 2.10
Continue reading

Life in a bubble

Moving to a new country doesn’t sound like the hardest thing to do. There are harder tasks such as researching dark matter and energy or maybe crochet. However, moving abroad doesn’t sound like the easiest exercise either. When you move to a new place, you usually need to learn many new and not always easy things. Those may be laws, traditions, cultural differences, and of course, the language in which people speak in the country. Otherwise, you may end up living in a bubble.

Maple leaf in Germany
Continue reading

TLS enhancements in Java 13

Java 13 was released on Sep 13th, 2019. Although the new Java doesn’t contain major updates in security libraries, nevertheless it has several notable updates in the TLS implementation. Let’s take a closer look at how Java 13 helps to make your TLS connections faster and more secure.

TLS enhancements in Java 13
Continue reading

What’s new in Java 13

Java 13 is going to be released on Sep 17th, 2019. Besides ~2300 bug fixes and small enhancements, the new version of Java contains 5 major enhancements which are also called JEPs (Java Enhancement Proposals). Let’s take a closer look at these major updates: text blocks, switch expressions, re-implemented the legacy Socket API, updates to ZGC and dynamic CDS archives.

What is new in Java 13
Continue reading

Weather station based on ESP32 and MicroPython

In one of the previous posts I briefly described sending data to Google Sheets from a ESP32 board using MicroPython. As I mentioned earlier, the code is available on GitHub. Here are the main features:

  1. Measuring temperature and humidity with DHT22 sensor.
  2. Sending data to a Google Sheet.
  3. Authentication via Google OAuth 2.0 service to get access to the sheet.
  4. Configuring the device via web browser.

The Google Sheet doesn’t need to be publicly available on the Internet. The device doesn’t require any middleman such as PushingBox or IFTTT.

In this post, let’s focus a bit on technical details.

ESP32 board and DHT22 sensor
Continue reading

MicroPython on ESP32: sending data to Google Sheets

On a wonderful weekend in summer time, instead of going out to a beach or somewhere else, I was staying at home and wondering if it’s possible to send data from an ESP board to a Google sheet using my favorite MicroPython. Let’s say it can send temperature and humidity measured by a DHT22 sensor. That’s how the project started.

(this post contains a brief description of the project, more technical details can be found in the next post)

ESP32 development board and DHT22 sensor: sending data to a Google sheet.
Continue reading

Transistor delay circuit

The transistor delay circuit may be helpful to learn some electronics basics. The circuit is pretty simple. It only contains a transistor, a capacitor, several resistors, a switch and an LED. The circuit uses an RC filter to turn an LED on with a little delay. Let’s see how we can choose elements for the circuit, and how the delay depends on parameters of the elements.

Transistor delay circuit on a breadboard
Continue reading

Small hydroponic system at home

Increasing living space unavoidably results in filling up the new available space. In the end of last year I moved to a bigger apartment. Since I still have the same furniture, the unused space and volume keep bothering me. In the winter I built a shelving and now I store some useful stuff on it. In spring I got an idea to make a small garden at home. On weekend I built several wooden boxes, and put cherry tomatoes, onions and dill into it. But then I thought it’s not enough. I bought a couple of plastic containers and put more tomatoes. But I thought even that was not enough, and I got an idea to make a hydroponic system.

hydroponic system at home
Continue reading