Remote code execution

Overview

Serialization is a useful and widely supported feature. However, it also provides an easy target for hackers to try and execute malicious commands using the external shell. This article will demonstrate, by using code samples, how serialization vulnerabilities can be exploited to execute commands remotely, and how, by implementing secure coding practices, the risk can be minimized.

About Serialization:

All OOP languages support the ability to implement custom serialization such as JSON and XML. Most of them also provide native support for default serialization. The idea is simple – you take an object, extract its data, transform it to a textual or binary representation and save it for later use. Later, whenever you need to use the object, you load the data, parse it and reconstruct the object. Provided that both the serializer and the de-serializer are familiar with the structure of the object, we can avoid having to serialize the metadata of the object and reduce the latency and the size of the serialized data.

Serialization in JAVA

Not only does the Java ecosystem support default serialization, but it also encourages the developer to use serialization almost everywhere. The useful Java Beans mechanism is based on the ability to serialize and later reconstruct the serialized objects.  By using Java Beans we can easily separate the business logic from the IT aspects and scale our system without changing any code. We simply serialize some of the objects, send them to another process for processing, serialize the transformed object and send it back. In addition, Java Beans can be used as an easy way to receive structured data and commands from other processes.

Sounds good – as long as we can vouch for the source of the serialized objects.

Let’s examine the Java Management eXtensions (JMX) mechanism. It listens to a network port awaiting connections. When a connection is established the JMX mechanism reads the input stream expecting a serialized command Java Bean and attempts to de-serialize it and cast it to the expected class. Once reconstructed the object is used by the JMX mechanism to change program parameters or, potentially execute a command.

Exploiting the Serialization Vulnerability

There is no doubt as to the usefulness of the serialization mechanism, however it is also a very effective attack vector.  Understanding the way this mechanism works, we will try to use our knowledge to execute the following code, which can be generalized to any code execution:

This command is supported on all platforms and allows the developer to execute the string “command” on the external shell (e.g. – “cmd” for windows, or “bash” for linux).

code-sample-11

This command is supported on all platforms and allows the developer to execute the string “command” on the external shell (e.g. – “cmd” for windows, or “bash” for linux).

Here are three approaches an attacker can take in order to exploit the serialization mechanism:

  1. Wrap the malicious command in a serialized java object and send it – The easiest approach would be to write some malicious code, wrap it in a Java object, serialize the object and send it through the JMX channel. Due to the inherent limitation of serialization, the class of the serialized object has to exist in the class-path of the application for the de-serialization mechanism to succeed, we will end up with a de-serialization exception.
  2. Express the malicious command using code that already exists on the server – Another option is to send a serialized object of a different type that already exists in the class-path. This is more difficult since we have to somehow guess which classes exist in the class-path and “trick” them into doing what we want. We may find such unsafe code in interpreter libraries, Reflection APIs and Shell delegators.
    However, even if we manage to find such code and bypass the serialization limitation, we would end up with a Class Cast Exception when JMX tries to cast the de-serialized object into the class it expects.
  3. Express the malicious command using code that already exists on the server and that is executed as part of the de-serialization process – A more subtle approach would be to look for a class that exists in the class-path and can be executed as part of the deserialization process. For example, a code that executes automatically when the constructor of the class is invoked.
    This way our command is executed after the de-serialization but before the object is cast to another type.

Apache-Commons-Collections as an Enabler for Approach #3:

We are looking for interpreter code that executes when an object is initialized and that is widely used by applications. A good place to start would be the open source libraries and even better – the root libraries that are referenced by many other projects. Let us examine such commonly referenced library – the apache-commons-collections library. The apache-commons-collections is a library for handling collections that is very widely used. Many application servers have it on their class-path even before any application is deployed. Furthermore, thanks to build automation tools such as Maven, many applications use open source libraries without even checking the dependencies they introduce into the project.

Among the classes that are contained within the apache-common-collections is LazyMap. This class supports the “decorate” design pattern, which enables for transformers (processing code) to be triggered automatically when a Map Entry is accessed. (Un)Fortunately one of the out-of-the-box transformers is the InvokerTransformer which receives a string containing a method name, and executes the method whenever the Map Entry is accessed. We also perform another trick using Java Annotations in order to force entry access and thus trigger the decoration transformer chains.
The following code snippet is taken from a proof of concept implementation of the apache-commons-collections vulnerability. The complete implementation is available on Github.

serialization vulnerabilities

This is exactly what we were looking for– a class which (1) exists in the class-path and (2) contains interpreted code that (3) executed automatically when the object is de-serialized.

Since JMX is used in many Java deployments in production environments it is an appealing target. However, JMX is not the only target out there, you can also look at other examples such as: Enterprise Java Beans (EJB), and Java Message Service (JMS), Remote Method Invocation (RMI).  These examples are straightforward – send your malicious object and it will be de-serialized and executed. Many application servers have similar mechanisms to receive serialized objects, however they follow a proprietary wrapping protocol. By monitoring the network while using the proprietary client to send messages to the server we can mimic the expected protocol and inject our serialized object into the message. For additional information and other examples see this great article.

Implications:

Once an attacker succeeds in executing shell code via the application server, the possibilities are unlimited. This includes: spreading to other internal and otherwise more secure servers, injecting code by using the Java Compiler API, accessing otherwise encrypted data using system credentials (application level), and even changing the home page to include malicious java-script code. Apache-commons-collections is just one example. If we follow the same logic as described in this article, we can probably find others. Especially if we know which libraries exist in the application’s class-path.

Signature based antiviruses would never be able to detect such malicious activity, as the malicious activity is performed by the application server.

How Can We Reduce our Exposure to Serialization Risks?

There are quite a few practices that can significantly reduce the risk of exposure to serialization vulnerabilities.

Here are the main ones being:

IT Best Practices

  • Deploy a behavioral intrusion detection system. Such systems will detect and alert when a legit process performs suspicious operations.
  • Use a firewall to eliminate access to ports that should not be accessed from outside the data center (e.g. – JMX ports)
  • Minimize exposing data about your system publically, including social networks and online communities. Attackers may use questions posted in forums such as StackOverflow to collect information about your organization and assess your vulnerabilities.

Development Best Practices 

  • Use an up-to-date Java version.
  • Avoid de-serialization of Java objects from unknown source.
  • When using open source libraries, check the dependencies they introduce into the project and check if they have known vulnerabilities and security fixes.
  • Use secured communication channels wherever objects are being serialized and de-serialized.

Conclusion

Serialization is a popular and useful feature, which also emerges as an effective attack vector.  I hope this article was helpful in exposing Serialization Vulnerabilities and eliminating them.

Shahar Gaziel is an Enterprise Architect at CYBERBIT’s Endpoint Security Group

Learn more about Cyberbit EDR Kernel-Based Endpoint Detection vs. Whitelisting

See a Cyber Range Training Session in Action