Remotely Exploitable Java Zero Day Exploits through Deserialization
A recent analysis by Foxglove Security of a talk "AppSecCali: Marshalling Pickles" (video, slides) given by @frohoff and @gebl in January 2015, has confirmed multiple zero day, remotely executable exploits, for Java applications that deserialize objects from untrusted network sources and use libraries such as Apache Commons Collections, Groovy or Spring. Since a number of application servers transparently support deserialization of objects from data streams or through cookie values, it is trivial to pass exploit code through an HTTP request to a server which may be behind a firewall or scanners.
The proof of concept code against Apache Commons Collections 3.x is alleged to work against JBoss, Jenkins, Weblogic and WebSphere. Although this is not as trivial to exploit as the Apache Struts vulnerability in 2014, the code is as powerful and is likely to be found in many more places than currently detected.
The vulnerability stems from the fact that deserializing a Java object can instantiate any arbitrary classname with arbitrary data. In the case of Apache Commons Collections 3.x, this includes a Transformer class that allows conversion of an input object to another object type. This is called automatically by the deserialization routines, which allows the implementation to supply a completely different object. This is combined with the ChainedTransformer and ConstantTransformer to instantiate an object that creates an iConstant field in the serialized form that in turn uses an InvokerTransfomer instance to execute a single arbitrary method.
The proof of concept code has been tested by the author and verifies that with Apache Commons Collections 3.2.1 the vulnerability is real. The proof of concept is specifically developed for Unix systems (as it creates a file by executing
Runtime.exec("touch /tmp/pwned"); but in principle any command can be run on any operating system. The proof of concept is also specifically targeted at Apache Commons Collections 3.x; this is because Apache Commons Collections 4.x uses a different package namespace (
org.apache.commons.collections4). There are alternative exploits against Apache Commons Collections 4.x from the upstream repository along with Groovy 2.x (less than 2.4.4) and Spring 4.x.
Update: Guillaume Laforge contacted InfoQ to confirm that the exploit in Groovy was fixed in 2.4.4 under CVE-2015-3253 in GROOVY-7504 earlier this year, as its first release from the Apache Foundation. Since the issue is present in all older versions of Groovy, users are advised to upgrade to the current release from Apache to avoid this specific issue.
In all cases, the data supplied by the end user allows an arbitrary chain of objects to be constructed, which ultimately results in a sequence of bytes being deserialized resulting in code execution. By creating a payload class that has a class initialzier including a
Runtime.exec() with a user-supplied command, and then wrapping that in a series of objects which when executed will result in the payload being given. Although the proof-of-concept uses a simple
Runtime.exec() for the Apache Collections classes, an alternative attack against a JDK copy of
com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl (which is present in even the latest versions of OpenJDK 8) can also be exploited; this is demonstrated against the Spring attack through an auto-wiring mechanism and a type provider.
Unfortunately these vulnerabilities cannot be effectively protected against in an easy way. As well as being applicable to large numbers of Java applications (using or having Groovy on the class path, or Spring 4.x or Apache Commons Collections 3.x or 4.x), there is no easy way to prevent Object serialization from being used in a JVM, since it is used by potentially many Java operations.
As the proof of exploits show, a large number of Java application servers are vulnerable to remote code execution through the ability to send data via a network port; Jenkins, for example, uses Object serialization though the Jenkins CLI interface, and so is remotely vulnerable to anyone who can reach that port. CloudBees, one of the primary sponsors of Jenkins, have issued a security advisory recommending the immediate disablement of the Jenkins remote CLI service to prevent this vulnerability, and note that all CloudBees Jenkins on-premise installations are vulnerable. In the case of WebSphere, the attack is demonstrated through a SOAP request to the AdminService; and in fact, any javax.management port (which uses remote object serialization and has the buggy libraries) is potentially vulnerable. This is the same route used for the JBoss exploit, which posts a request to the
JMXInvoker service. Any server running RMI is also potentially vulnerable - though in practice, RMI ports open to the internet are themselves a security smell.
As Foxglove Security notes, there isn't an effective way of determining what the objects are when being passed over the wire. Java serialized objects will start with
0xaced0005, or if they are base64 encoded,
rO0. Looking for strings for classes used in the exploits can also be used to create signatures, since the class names are represented in UTF-8 strings in the classes:
There is no practical reason why any of these classes should be coming in through a serialized object through cookies or any other binary stream; it may be the case that these classes can't be deleted without affecting real functionality, but network monitoring software could examine the content of HTTP headers and cookies to look for sequences of these strings inside base64 encoded objects that are identified with
rO0. Unfortunately since responses may be compressed, or if using HTTP/2 may be coming through different pipelined requests.
Due to the number of places object deserialization can occur from networked data (or through custom network protocols) the vulnerabilities may be difficult to close. As with all security mechanisms, validating input is the key way of preventing exploits; in the case of Java Object deserialisation, it should not be used, instead preferring custom serialisation formats such as encoding through JSON or XML, or a specific library such as Protobuf or Thrift. Note that the SOAP WebSphere exploit uses XML to pass in a Base64 encoded payload which is then subject to serialization; so even using known protocols for data transfer does not avoid the potential for exploits.
Additional Update - November 10th
The Apache Commons team is using the ticket Collections-580 to address the issue in the 3.2 and 4.0 branches of commons-collection by disabling de-serialization of the class InvokerTransformer. Bernd Eckenfels, Committer, and Gary Gregory, Vice President of Apache Commons have published a blog post in which they state that "A to-do item being discussed is whether to provide programmatic enabling of the feature on a per-transformer basis." They also note that
Even when the classes implementing a certain functionality cannot be blamed for this vulnerability, and fixing the known cases will also not make the usage of serialization in an untrusted context safe, there is still demand to fix at least the known cases, even when this will only start a Whack-a-Mole game. In fact, it is for this reason the original team did not think it is necessary to alert the Apache Commons team, hence work has begun relatively late.
Fixed in Groovy 2.4.4+
We encourage people to migrate to a newer version of Apache Groovy.
The Spring Framework perspective
That said, as with previous deserialization exploits, the only safe measure is to not accept Java serialization on endpoints with untrusted clients. Since Spring is not exposing any such endpoints by default, this is entirely up to the application. Even if Commons Collections tightens its exploitable classes as well, there will probably be others - in particular less common ones and non-open-source ones - on your classpath that are exploitable along the same lines, even if nobody showed them in an article yet.
NotSoSerial: A JavaAgent rejecting deserialization of untrusted classes
To mitigate attacks effectively, we need a solution which gives us control of which classes we allow deserialisation for. We need to whitelist which types can be deserialized.
This is why I created NotSoSerial. NotSoSerial is a Java agent which act like a "deserialisation firewall".
By default it rejects deserialisation for a list of known "bad" classes. But more interesting, NotSoSerial can also whitelist classes.
Whitelisting only allows deserialization of classes you know your app actually needs to deserialize. I think this is the more long term solution to these problems.
NotSoSerial also has a recording mode which logs every type that is actually being deserialized by your application. This can be useful for analysis on its own, but can also be used to build a whitelist of accepted types which can later be enforced.
Finally, with an empty whitelist you can effectively disable deserialization altogether. Which turns out to work just fine for a lot of apps. This gives you a 100% guarantee you'll never ever experience a deserialization vulnerability.
Here's the Github repo for NotSoSerial:
Does apache avro deserializer has this problem ?
Re: Does apache avro deserializer has this problem ?
what about Commons-collection version 1.x and 2.x ? are these impacted too
Deserialization with Input Validation
It uses Java's serialization stream protocol v2, however unlike ObjectInputStream, it requires explicit constructors, these require input validation be performed to abort the creation of an object instance prior to java.lang.Object's constructor being called (traditionally used to eliminate finalizer attacks, introduced in Java 6).
Because the child class constructor is used, not the first superclass that has a zero arg constructor, all class's ProtectionDomain's will be present on the call stack for security checks.
An implementing class is annotated with @AtomicSerial, the constructor signature is:
public MyObject(GetArg arg) throws IOException
Note that a class that implements @AtomicSerial needn't be public, just the constructor signature, it must also implement Serializable, however readObject methods are never called.
GetArg is a child class of GetField, with caller sensitive methods that limit access by the caller class's type.
GetArg also has convenience static methods to assist with common input validation checks.
Consider each object in a serialized object graph, the first object in that graph who's invariants aren't satisfied will throw an InvalidObjectException, prior to that object's creation.
Stateless objects aren't required to implement @AtomicSerial, only a zero arg constructor.
Some standard java platform classes, that don't have the required constructor are replaced in the stream by @AtomicSerial implementing serializers that validate then build an instance of the original class and return it using readResolve(), for example, Integer, Short, Long, Double, Throwable, Permission etc.
In a number of cases, Collection's for example, the API requires that defensive copies be created during construction, in these cases the original Collection isn't transmitted in the stream, only an immutable safe instance that also performs validation during construction. Eg, HashMap is not supported as a deserializable class, however Map is, if a HashMap is serialized, if you try to deserialize it, you'll get a Map, not HashMap, you then pass this Map to Collections.checkMap, then to the constructor of HashMap, so you check all key and value types and defensively copy it during the construction of your object. Remember that all objects contained within the map, must also implement @AtomicSerial or be stateless, such as a Comparator in TreeMap (TreeMap would be replaced in the stream by a SortedMap), in order to be deserialized.
All validation is performed prior to calling java.lang.Object's default constructor. Intra object validation between super and child classes can also be performed during construction, prior to an instance being created.
DeSerializationPermission, will allow an administrator to further limit the classes allowed to be used in deserialization by their CodeSource.
ExternalizablePermission will also restrict the use of this interface by CodeSource.
DownloadPermission, will prevent code being downloaded.
Most Serializable objects won't be serializable, however they can be made so by subclassing and implementing @AtomicSerial, in this case the child class will be made fully responsible for transmitting state.
All these permissions can be dynamically granted after authentication using TLS communication and x509 certificates.
Stream resets are performed periodically, if a stream reset is not performed before a certain data limit is reached (that also limits array lengths), the stream will throw a StreamCorruptedException and return control to the caller to prevent DOS.
This can be thought of as a restricted subset of Serializable.