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:
We will break the gadget into 2 parts :
- The PriorityQueue gadget
- The TemplatesImpl gadget
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()
:
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));
comparator.compare(obj1, obj2)
is equivalent toTransformingComparator.compare(obj1,obj2)
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.
As you can see in the picture:
obj1: 1
andobj: 2
( this is because we callqueue.add(1)
twice in the code ).this.transformer
isInvokerTransformer
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));
this.transformer.transform
will turn intoInvokerTransformer.transform(obj)
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:
After the setFieldValue
is called:
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()
newTransformer call getTransletInstance
Implementation of getTransletInstance
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.
Deserialization process
During the deserialization process, the function TransformerComparator.compare(obj1, obj2 )
is called again
With values:
obj1
isTemplatesImpl
( because we switch the content of queue )obj2
is still1
this.transformer
isInvokerTransformer
=> this.transformer.transform(obj1)
will become InvokerTransformer.transform(TemplatesImpl)
. Which will lead us to this function
With values:
- input:
TemplatesImpl
- cls:
com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl
- method:
cls.getMethod(this.iMethodName, this.iParamsType)
will be equivalent toTemplatesImpl.getMethod("newTransformer", Class[0]
=>newTransformer
And finally the function: method.invoke(input, this.iArgs)
will invoke newTransformer()
method of TemplatesImpl
and trigger our chain, build class from bytecodes
Result:
tags: java, - deserialization