Some time ago I wrote about one security issue which I found in the library. This post describes another little vulnerability in Apache Olingo. The issue has been fixed in the 4.7.0 release as well.

By the way, Apache Olingo is a Java library that implements the Open Data Protocol (OData). This protocol allows the creation and consumption of queryable and interoperable RESTful APIs in a simple way.

CVE-2019-17556: Unsafe deserialization in Apache Olingo

The issue

Apache Olingo has the AbstractService class which is part of the public API. According to the Javadoc, the class is an entry point for the proxy mode. It gives access to entity container instances. The constructor of the class takes compressed metadata. More precisely, the constructor expects a serialized XMLMetadata object which is encoded with Base64 and compressed with GZIP. Which serialization mechanism does it use? It’s the default Java serialization which is known to be vulnerable to deserialization attacks. Here is how the constructor processes the compressed metadata:

protected AbstractService(final String compressedMetadata, final String metadataETag,
      final ODataServiceVersion version, final String serviceRoot, final boolean transactional) {

    ByteArrayInputStream bais = null;
    GZIPInputStream gzis = null;
    ObjectInputStream ois = null;
    XMLMetadata metadata = null;
    try {
      // use commons codec's Base64 in this fashion to stay compatible with Android
      bais = new ByteArrayInputStream(new Base64().decode(compressedMetadata.getBytes("UTF-8")));
      gzis = new GZIPInputStream(bais);
      ois = new ObjectInputStream(gzis);
      metadata = (XMLMetadata) ois.readObject();

First, it uses theBase64.decode() method to decode the input data. Then, it converts the decoded data to a byte array and wraps the array into a ByteArrayInputStream instance. Next, the input stream is wrapped into a GZIPInputStream object to decompress the data. Finally, the GZIP input stream is wrapped into an ObjectInputStream to deserialize the metadata.

By default, the ObjectInputStream doesn’t apply any check when it deserialized data. It means that if an attacker is able to feed malicious metadata to a class that extends the AbstractService, then in the worse case it can result in executing the attacker’s code.

The severity of the issue highly depends on how a particular application uses the AbstractService class, and where the metadata comes from.

If an application loads the metadata from a protected resource, the attacker has to find a way how to write malicious metadata to the protected resource. It may be not easy. For example, if the metadata is loaded from a properly configured database, then the attacker has to find a SQL injection or another way that would allow him to inject malicious data into the database. Or, if the application loads the metadata from a file which has proper access rights, then the attacker has to find a way how he can write to the file.

On the other hand, if an application receives the metadata via the network, it may be much easier for the attacker to make the application load his malicious data.

Nevertheless, the project maintainers agreed to make the AbstractService class a bit more safe to use.

The solution

The problem has been mitigated by implementing a whitelist for classes which are allowed for deserialization. It may be done, for example, by extending the ObjectInputStream class and implementing the whitelisting in the overridden resolveClass() method. But it was not necessary because Olingo uses Apache IO which offers the ValidatingObjectInputStream class for implementing such a whitelist. Another option might be the filters that have been added in JEP 290, but this way would require using newer versions of Java.

The fix was quite simple:

  • Added a factory method createObjectInputStream() which creates a ValidatingObjectInputStream instance configured with a whitelist of classes which are allowed for deserialization.
  • The constructor of the AbstractService class calls the createObjectInputStream() method to get a deserializer for metadata.
  • By default, the whitelist contains only classes from the org.apache.olingo package.
  • If a user would like to extend the default whitelist, he can override the getAllowedClasses() method to make it return a list of allowed classes.

The patch has been released in Apache Olingo 4.7.0.

Is my application vulnerable?

Definitely, not each and every application which uses older versions of Apache Olingo is vulnerable. There are at least two things which need to be checked in an application which uses Apache Olingo:

  1. Does it uses the AbstractService class?
  2. Is there a way how someone can feed malicious data to the class?

Answers to the questions above depend on a specific application. If both answers are yes, then it’s likely that the application is vulnerable.

Conclusion

This issue is another example of a deserialization vulnerability when an application uses the default Java deserialization mechanism. Not all applications, which use Apache Olingo, are vulnerable by default. But it may be better to stay on the safe side and update the library.

References