打破静态方法限制:AviatorScript 中基于 MethodUtil 的 JDK 原生利用链
漏洞分析
AviatorScript 是一门寄生在 JVM (Hosted on the JVM)上的语言。曾经在Jeecg-boot中出现过相关的漏洞。目前网上公开的POC多为BCEL或org.springframework.cglib.core.ReflectUtils进行利用,前者对jdk版本较为苛刻,后者依赖于spring框架,本文将提供一种新的仅依赖jdk的利用方式。
一、背景介绍 ------ 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` 方法尤为关键。  - - - - - - ### MethodUtil.invoke 方法概述 `MethodUtil.invoke` 的核心作用是对反射调用进行封装,其逻辑上需要三个参数: 1. `Method` —— 目标方法的反射对象 2. `Object` —— 目标方法所属的实例对象 3. `Object[]` —— 方法调用时使用的参数数组 只要能够在 AviatorScript 表达式中成功构造这三个参数,就可以通过 `MethodUtil.invoke` 间接调用任意实例方法。 - - - - - - ### Method 参数的构造方式 在 `sun.reflect.misc.MethodUtil` 中,除了 `invoke` 方法外,还提供了用于获取 `Method` 对象的辅助方法,例如 `getMethod`。  `getMethod` 的作用是: - 指定目标类(`Class` 对象) - 指定方法名称 - 指定方法参数类型列表 即可返回对应的 `Method` 实例。 由于 AviatorScript 本身支持: - `Class.forName` 获取类对象 - 基本类型与数组的构造 - 对 `public static` 方法的直接调用 因此,在表达式执行阶段,`Method` 对象本身是可以被完整构造出来的。 - - - - - - ### 目标对象(Object 参数)的来源 `invoke` 的第二个参数用于指定反射调用的目标对象实例。 在 Java 中,只要能够获取到目标类的实例,即可调用其对应的实例方法。 在 AviatorScript 环境下,实例对象的来源通常包括: - 已存在的单例对象 - 通过静态工厂方法获取的实例 - 通过 `new` 关键字创建的对象 这使得 `MethodUtil.invoke` 在表达式语言中具备了较高的灵活性。 - - - - - - ### 参数数组(Object\[\])的构造 第三个参数为方法调用时使用的参数数组。 AviatorScript 提供了 tuple / array 等基础构造能力,可以将单个或多个参数封装为对象数组,从而满足反射调用对参数形式的要求。   至此,`MethodUtil.invoke` 所需的三个参数: 1. `Method` 2. 目标对象 3. 参数数组 均可以在表达式层面完成构造。 - - - - - - ### 利用思路的关键点总结 从整体利用思路来看,其核心不在于某一个具体 API,而在于以下几点: - AviatorScript 允许调用 JDK 的 `public static` 方法 - `MethodUtil` 对反射调用进行了统一封装 - 反射调用本身可以突破“只能调用静态方法”的限制 - 整个过程不依赖任何第三方框架 这意味着,即使在不存在 Spring、BCEL 等组件的环境中,只要表达式执行上下文未对 JDK 内部工具类进行限制,仍然可能存在可被利用的攻击面。 - - - - - - ### 基于以上分析我们可以构造出以下POC ```php 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')) ```  ### 小结 通过 `sun.reflect.misc.MethodUtil`,可以将 AviatorScript 的能力从: > 仅能调用 Java 的 `public static` 方法 进一步扩展为: > 在表达式执行阶段,间接调用任意实例方法,例如直接通过调用`ScriptEngineManager`执行JS代码,达到注入内存马等操作。 ```php 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\\");")); ```  五、实战 ---- 在本地搭建积木报表环境进行测试和验证,具体步骤如下: **生成 JS 类加载器**  **编码与拼接** - 将生成的 JS 代码进行 Base64 编码。 - 按照 POC(概念验证)示例,将编码后的内容拼接成完整 payload。  **发送 payload 与连接内存马** - 通过 HTTP 请求将拼接好的 payload 发往测试环境。 - 如果 payload 执行成功,可以在内存中看到远程加载的类,进一步连接内存马进行控制或交互。  六、意外收获 ------ 在翻阅AviatorScript文档时,发现AviatorScript提供了一个简单的文件 IO 模块实现,可以直接 require('io') 进来使用。从来达到不引入java层面类进行写入文件操作。使用这种方式写入文件在一定情况下可以绕过AviatorScript的一些安全配置。  ```php let io = require('io'); let file = io.file("/tmp/aviator\_test.jsp"); io.spit(file, "Hello world\\r\\nAviator is great!"); ``` 
发表于 2026-02-12 09:00:01
阅读 ( 880 )
分类:
漏洞分析
1 推荐
收藏
0 条评论
不羡仙
ikun
2 篇文章
×
温馨提示
您当前没有「奇安信攻防社区」的账号,注册后可获取更多的使用权限。
×
温馨提示
您当前没有「奇安信攻防社区」的账号,注册后可获取更多的使用权限。
×
举报此文章
垃圾广告信息:
广告、推广、测试等内容
违规内容:
色情、暴力、血腥、敏感信息等内容
不友善内容:
人身攻击、挑衅辱骂、恶意行为
其他原因:
请补充说明
举报原因:
×
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!