Skip to content

Latest commit

 

History

History
56 lines (46 loc) · 4.14 KB

File metadata and controls

56 lines (46 loc) · 4.14 KB

Groovy表达式字符串2(Groovy2Convert)

  • 类别:common 别名:— 优先级:未设置(null)
  • 作者:—
  • 依赖groovy(目标运行时具备 groovy.lang.GroovyShell / Groovy 编译器;内嵌逻辑再借道 JDK 的 JS 引擎,见下)

作用

「表达式转换」构件:把内层产出的 byte[] 字节码封装成一段 Groovy 源码字符串。与 GroovyConvert 直接给出可执行表达式不同,本类把 payload 藏在 @groovy.transform.ASTTest 注解里——该注解在 Groovy 编译期(AST 变换阶段) 就会执行 value 闭包,因此只要目标对源码做一次 Groovy 编译(而非运行)即可触发,适用于「只编译不执行」的场景。

链接标签

  • 入口 tagsGroovy_ExprExpression —— 声明产物是 Groovy 表达式 / 通用表达式字符串,可被会对其做 Groovy 编译或求值的上游 sink(如 GroovyShellRef、GroovyClassLoader 类)承接。
  • 衔接 nextTagsBytecodeConvertTag —— 内层须为 BytecodeConvert 类构件,提供 byte[] 字节码。
  • excludes:无。

源码剖析

public static String template =
  "@groovy.transform.ASTTest(value={\n" +
  "assert Class.forName(\"javax.script.ScriptEngineManager\").newInstance()" +
  ".getEngineByName(\"JavaScript\").eval(\"" + JsConvert.jsTemplate + "\")\n" +
  "})\n" +
  "class Person {\n}";

private String getObject(byte[] bytecode) {
    String bytecodeB64 = Base64.encodeBase64String(bytecode);
    return String.format(template, bytecodeB64);
}

逐段解释:

  • @groovy.transform.ASTTest(value={ ... }) 标注在 class Person 上。ASTTest 是 Groovy 提供的编译期测试注解,其 value 闭包在编译该类时于 AST 阶段被求值——这意味着 无需实例化或运行 Person,仅编译 template 就会执行闭包体,命中「只做 Groovy 编译」的注入点。
  • 闭包内 Class.forName("javax.script.ScriptEngineManager").newInstance().getEngineByName("JavaScript").eval(...):再借 JDK 内置 JS 引擎执行一段 JS。
  • 内嵌 JS 直接复用 JsConvertJsConvert.jsTemplate(编译期字符串拼接得到),该 JS 用 sun.misc.Unsafe.defineAnonymousClass 从 Base64 字节码定义匿名类并 newInstance(),完成字节码加载执行。因此本类是「Groovy 编译期注解 → JS 引擎 → Unsafe 定义类」的三级跳。
  • String.format(template, bytecodeB64) 只有一个 %s(来自 jsTemplate 内的 var s='%s'),填入 Base64 字节码。
public Object invoke(GadgetContext context, GadgetChain chain) throws Exception {
    return this.getObject((byte[]) chain.doCreate(context));
}

invoke 极简:驱动内层拿 byte[],转成 Groovy 源码字符串返回。头部 Could not load ... Base64 为缺依赖的正常反编译提示。

参数(@Param)

本类无 @Param。字节码由内层 BytecodeConvert 提供;template 与所用的 JsConvert.jsTemplate 均为写死字段。

适用版本与原理要点

  • description 明确「注意这里使用 @groovy.transform.ASTTest」:这是本类相对 GroovyConvert 的核心差异——命中编译期而非运行期执行,能覆盖某些只把用户输入交给 Groovy 编译器的场景。
  • 因内嵌 JS 复用 JsConvert.jsTemplateUnsafe.defineAnonymousClass),受其版本约束:JS 引擎需可用(JDK 8~14 的 Nashorn),且 Unsafe.defineAnonymousClass 在高版本 JDK(16/17+)被限制/移除。
  • 需一个会对本字符串做 Groovy 编译/求值的上游 sink 才能形成完整 RCE。

所属预设链

无。自由构件,未直接出现在 51 条预设链中(对照 GroovyConvert 出现在「Groovy 脚本执行」链)。

关联