- 类别:
bytecode别名:— 优先级:200 - 作者:Ar3h
- 实现:
Gadget(无基类;直接实现com.ar3h.chains.common.Gadget) - 依赖:无第三方 Maven 依赖(运行期用到工具自带的
org.apache.commons.codec.binary.Hex与JavassistHelper)
链尾的自定义字节码源:把用户提供的一段十六进制字符串(hexString)用 Hex.decodeHex 还原成原始 class 字节流,交由链上游的字节码加载器(典型为 TemplatesImpl + BytecodeConvert)装载执行。它相当于 Exec、各类回显/内存马 sink 的「万能替身」——不生成固定功能的恶意类,而是让使用者直接投喂任意自编译字节码,从而把 java-chains 的利用链变成一个纯粹的「字节码投递」通道,增强工具拓展性。
摘自
@GadgetAnnotation.description:从参数中解码获取字节码字节流,适用于自定义字节码场合,增加了工具拓展性。
与之完全同构的姊妹构件:BytecodeFromBase64(Base64 编码)、BytecodeFromLocalFile(本地文件)、BytecodeFromUploadFile(上传文件)——四者仅字节码来源不同,后续处理一致。
- 入口
tags:BytecodeConvertTag、Bytecode、END—— 一个构件同时挂三种入口标签,使其可占据链上三种角色:BytecodeConvertTag:可直接承接上游标注nextTags=BytecodeConvertTag的 gadget(如TemplatesImpl),越过BytecodeConvert加工环,把 Hex 解码出的字节码原样递给加载器。Bytecode:可承接上游nextTags=Bytecode的加工位(即接在 BytecodeConvert 之后),充当真正产出字节码的 sink,与 Exec/Sleep 同位。END:可作为整条链的终结点——自身即产出最终字节码,无需更内层 gadget。
- 衔接
nextTags:(空)—— 叶子节点,其后不再接任何 gadget。 excludes:(无)
全类仅 40 余行,核心是 getObject:
@GadgetTags(tags={"BytecodeConvertTag", "Bytecode", "END"})
@GadgetAnnotation(name="自定义字节码-Hex字符串",
description="从参数中解码获取字节码字节流,适用于自定义字节码场合,增加了工具拓展性",
priority=200, authors={"Ar3h"})
public class BytecodeFromHex implements Gadget {
@Param(name="十六进制字符串", description="会自动根据该字符串进行Hex解码获取字节码")
public String hexString;
public byte[] getObject(GadgetContext context) throws Exception {
byte[] bytecode = Hex.decodeHex((String)this.hexString); // ① Hex 解码为原始 class 字节
JavassistHelper javassistHelper = new JavassistHelper(bytecode); // ② 用 Javassist 解析字节码
context.put(ContextTag.CLASS_NAME_KEY, javassistHelper.getClassName()); // ③ 把类名写入上下文
return javassistHelper.getBytecode(); // ④ 返回(规范化后的)字节码
}
@Override
public Object invoke(GadgetContext context, GadgetChain chain) throws Exception {
chain.doCreate(context); // 履行链契约:驱动内层(此处通常为空链,返回值被丢弃)
return this.getObject(context); // 真正产物由本构件自身生成
}
}逐段解释:
- ①
Hex.decodeHex(this.hexString):hexString是完全由使用者控制的攻击载荷——一段编译好的 Java class 文件的十六进制串。这里直接还原为byte[]原始字节码,没有任何形态校验或白名单,本构件不关心该类做什么。 - ②
new JavassistHelper(bytecode):把原始字节码交给工具内置的JavassistHelper(JavassistCtClass封装),解析出类结构。该工具类未随 CFR 反编译产物导出(属工具自身 util,不在 中),但从 BytecodeConvert 中对它的用法可知:它能读类名、改类名、设父类(AbstractTranslet)、调字节码版本、去符号、重新导出字节码等。此处只用到其中两项只读能力。 - ③
context.put(ContextTag.CLASS_NAME_KEY, javassistHelper.getClassName()):把从字节码里解析出的真实类名写进GadgetContext。这一步是链协作的关键——上游加载器(TemplatesImpl/BytecodeConvert)需要知道即将defineClass的类名,才能正确实例化/触发它。构件之间通过context这一「黑板」传递此类元信息,实现松耦合的自由组合。 - ④
return javassistHelper.getBytecode():返回字节码。经JavassistHelper往返后是「规范化」后的字节,但因本构件未调用任何handleXxx/setXxx,功能上等价于输入字节码。 invoke中的chain.doCreate(context):本构件是链尾字节码源,正常情况下nextTags为空、其后无内层 gadget,doCreate对空链返回null且返回值被直接丢弃;调用它只是遵守Gadget接口「先构建内层」的契约,并允许任何被显式串在其后的内层 gadget 对context做副作用。真正的产物完全由本构件自身的getObject生成,这与「由内向外包装内层对象」的常规 gadget(如 CommonsBeanutils1)形成对比。
| 字段 | 名称 | 说明 | 类型 | 默认 | 可选值 |
|---|---|---|---|---|---|
hexString |
十六进制字符串 | 会自动根据该字符串进行 Hex 解码获取字节码 | String |
无(必填) | 无 |
使用者需自行准备一段编译好的恶意类字节码并转成十六进制串填入。若目标经由
TemplatesImpl加载,该类通常需继承AbstractTranslet(此步一般交给上游 BytecodeConvert 的needAbstractTranslet处理;直接经BytecodeConvertTag位使用时则需字节码自带该父类)。
- 无版本/依赖约束:
Hex.decodeHex属 commons-codec(工具已内置),JavassistHelper为工具自带,构件本身不引入任何目标侧第三方依赖,故dependencies为空。 - 能否落地取决于加载器:本构件只负责「产出字节码」,是否 RCE 取决于上游加载入口。经典组合是
TemplatesImpl → BytecodeConvert → BytecodeFromHex,要求目标 JDK 能通过TemplatesImpl.newTransformer()实例化 translet(JDK < 17 限制、需类继承AbstractTranslet)。 - 可跳过 BytecodeConvert:因携带
BytecodeConvertTag,可直接接在TemplatesImpl之后。此时不会自动改类名/设父类/去符号——字节码须自带加载器所需的一切(如已 extendsAbstractTranslet),适合投递完全自制、已符合约束的字节码。 - 拓展性定位:优先级 200(数值大、排序靠后),定位为高级/自定义场景,而非默认首选;意在给使用者一个不受工具内置 sink 功能限制的「任意字节码」出口。
usedInChains 为空——自由构件,未直接出现在 51 条预设链中。它按 tags/nextTags 与字节码加载器自由组合,典型用法是替换预设链尾部的 Exec 等固定 sink,例如把 [..., TemplatesImpl, BytecodeConvert, Exec] 改为 [..., TemplatesImpl, BytecodeConvert, BytecodeFromHex] 以投递自定义字节码。
- 姊妹构件(同
bytecode/common,仅字节码来源不同):BytecodeFromBase64、BytecodeFromLocalFile、BytecodeFromUploadFile。 - 上游加工位(
nextTags=Bytecode):BytecodeConvert——常见地在其后作为字节码源。 - 上游加载器(
nextTags=BytecodeConvertTag):TemplatesImpl——可直连本构件的BytecodeConvertTag入口。 - 功能同位的固定 sink:Exec、Sleep 及各类
*Echo回显 / 内存马 sink。
- 检测特征:
- 反序列化流量中出现
TemplatesImpl(com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl)序列化指纹,其_bytecodes字段携带完整 class 字节(魔数CAFEBABE/ 十六进制cafebabe)。 - 运行期
ClassLoader.defineClass/TransletClassLoader.defineClass被从TemplatesImpl.getTransletInstance调用栈触发,加载一个随机名、继承AbstractTranslet的类。 - 该构件本身在攻击者侧留下超长十六进制字符串参数——但这仅存在于攻击者的 java-chains 客户端,不出现在受害流量中,故防守方应聚焦于字节码加载指纹而非 hex 串本身。
- 反序列化流量中出现
- 加固建议:
- 反序列化输入实施类白名单(JEP 290
ObjectInputFilter),拒绝TemplatesImpl、AbstractTranslet及各xsltc类。 - 关闭/隔离危险功能:禁用不必要的 XSLT/模板加载;对
defineClass施加 RASP 拦截(拦截运行期由反序列化路径定义的、继承AbstractTranslet的类)。 - 升级到不再允许
TemplatesImpl反序列化实例化的 JDK(JDK 17+ 强模块封装显著抬高门槛)。 - 网络层对入站序列化数据做
CAFEBABE内嵌字节码扫描与告警。
- 反序列化输入实施类白名单(JEP 290
- 反编译源码:
- 机器可读元数据:
index/gadgets.json(className == "BytecodeFromHex") - 关联加工位剖析:BytecodeConvert