Skip to the content.
25 March 2022

Analyze Java deserialization: CommonsCollections2 gadget chain ( part 2 )

by Quang Vo

Introduction - CommonsCollections2 gadget chain analysis

To continue part 1, we will analyze CommonsCollections2 gadget chain next. This gadget chain is interesting because it introduces some new concepts ( to me ) about trampolines and sink holes.

Gadget chain ( copied from ysoserial ):

ObjectInputStream.readObject()
			PriorityQueue.readObject()
				...
					TransformingComparator.compare()
						InvokerTransformer.transform()
							Method.invoke()
								Runtime.exec()

Payload generator by ysoserial:

image

We will break the gadget into 2 parts :

The PriorityQueue gadget

java.util.PriorityQueue is a built-in Java class that implements a priority queue that can be ordered by a custom comparator. It implements Serializable interface and have a custom deserialization function readObject(), this is very crucial to our gadget chain.

The gadget chain start at PriorityQueue.readObject()

Implementation of PriorityQueue.readObject() :

image

For every Object in the PriorityQueue, it will call method readObject() and then call to this.heapify()

In this.heapify() function, we have a trampolines:

heapify() -> siftDown() -> siftDownComparator() -> comparator.compare(obj1, obj2) 

The final function called in the heapify() trampolines is comparator.compare(obj1,obj2) . Looking back at the gadget chain code, we can easily identify the value of our comparator

final PriorityQueue<Object> queue = new PriorityQueue<Object>(2,new TransformingComparator(transformer));

Let’s place a breakpoint at TransformingComparator.compare(obj1, obj2 ) so we can examine the value of each variables to have a better understanding how the gadget chain actually run.

image

As you can see in the picture:

This is because TransformerComparator needs a Transformer class, and we “give” it InvokerTransformer in the beginning of our gadget chain

final InvokerTransformer transformer = new InvokerTransformer("toString", new Class[0], new Object[0]);

final PriorityQueue<Object> queue = new PriorityQueue<Object>(2,new TransformingComparator(transformer));

image

If you read the part 1, you will see the similarity, the function that we want to pay close attention to is: method.invoke(input, this.iArgs). It belongs to the Java Reflection API, it allows us to invoke methods on a class.

So now we know that, at the end of this PriorityQueue gadget chain, we are able to invoke any methods on any class , our path to Remote Code Execution is getting closer.

The next line in gadget chain code:

// switch method called by comparator
Reflections.setFieldValue(transformer, "iMethodName", "newTransformer");

Again, Java Reflection API is used here, to set the value of iMethodName in InvokerTransformer to newTransformer, previously the iMethodName was toString

Before the Reflections.setFieldValue call:

image

After the setFieldValue is called:

image

Why do we need to set iMethodName to newTransformer ?. This method is very crucial for our next gadget chain, it helps us to be able to achieve RCE. We will move to the TemplatesImpl gadget to see how is this function is used to achieve RCE.

The TemplatesImpl gadget

org.apache.xalan.xsltc.trax.TemplatesImpl ( TemplatesImpl ) is normally used for XML parsing. What is interesting about this class, is that it holds an array of objects in bytecode in the variable _bytecodes. A call to defineTransletClasses() will read this bytecode and initialize the classes. Note that whereas serialized objects can only contain values, bytecode is much more powerful as it can include code.

Here our gadget will be:

getOutputProperties() -> newTransformer() -> getTransletInstance() -> defineTransletClasses()

image

newTransformer call getTransletInstance

Implementation of getTransletInstance image

Finally, it will load classes defined in __bytecodes

After provided bytecodes in TemplateImpl to achieve RCE, we add TemplateImpl to our PrioriyQueue and have it serialized.

image

Deserialization process

During the deserialization process, the function TransformerComparator.compare(obj1, obj2 ) is called again

image

With values:

=> this.transformer.transform(obj1) will become InvokerTransformer.transform(TemplatesImpl). Which will lead us to this function

image

With values:

And finally the function: method.invoke(input, this.iArgs) will invoke newTransformer() method of TemplatesImpl and trigger our chain, build class from bytecodes

Result:

image

tags: java, - deserialization