Skip to content

Latest commit

 

History

History
81 lines (71 loc) · 5.04 KB

File metadata and controls

81 lines (71 loc) · 5.04 KB

Java原生反序列化Payload生成(JavaNativeSerialization)

  • 类别:javanative 别名:— 优先级:—
  • 作者:c0ny1、Y4tacker、1ue、Whoopsunix
  • 依赖:无(纯 JDK)

作用

把内层对象用 ObjectOutputStream 做一次 Java 原生序列化,产出 byte[] 字节流。它是「二次反序列化」链的关键接缝:将一个完整的 Java 反序列化 gadget 事先序列化成字节数组,交给外层能触发 readObject 的载体(如 SignedObjectRMIConnector),实现在受害端把这段字节流再次反序列化。

链接标签

  • 入口 tagsJavaNativeDeserializePayload —— 承接需要「一段已序列化字节流」作为输入的上一环。
  • 衔接 nextTagsJavaNativeDeserializeCustomBytePayload —— 其内层必须是一条完整的 Java 原生反序列化链JavaNativeDeserialize 入口,如 CommonsBeanutils1、CommonsCollectionsK3),或用户自定义字节流。
  • excludes:无。

源码剖析

本类只覆写 invoke,把内层对象交给基类的 marshal

@Override
public Object invoke(GadgetContext context, GadgetChain chain) throws Exception {
    return super.marshal(chain.doCreate(context));   // 内层对象 -> 序列化字节流
}

真正的序列化逻辑在基类 AbstractJavaNative

public byte[] marshal(Object obj) throws Exception {
    this.object = obj;
    byte[] resultBytes = null;
    if (obj instanceof byte[]) {
        resultBytes = (byte[]) obj;                  // 已是字节流则直接透传
    } else {
        Object objWraped = DirtyDataWrapper.builder(obj)
            .withDirtyCollectionSize(this.dirtyCollectionSize)
            .withDirtyClassSize(this.dirtyClassSize).build();   // 可选脏数据填充
        resultBytes = AbstractJavaNative.serialize(objWraped);
    }
    int overLong = Integer.parseInt(this.overlongMode);
    if (overLong > 0 || this.resetSize > 0) {
        resultBytes = SerializationObfuscator.builder(resultBytes)
            .withType(overLong).withTcResetSize(this.resetSize).build();  // 可选混淆
    }
    return resultBytes;
}

public static byte[] serialize(Object obj) throws Exception {
    ByteArrayOutputStream out = new ByteArrayOutputStream();
    ObjectOutputStream objOut = new ObjectOutputStream(out);
    objOut.writeObject(obj);                         // 标准 JDK 序列化
    return out.toByteArray();
}
  • marshal 若发现内层已是 byte[](如 UserCustomByteXXX 自定义字节流)则原样透传,否则先经 DirtyDataWrapper 做可选的「脏数据」填充(用 ArrayList/HashMap 等集合或垃圾类名膨胀 payload 以绕过部分检测),再走标准 ObjectOutputStream.writeObject
  • 序列化后可选做 SerializationObfuscator 混淆:TC_RESET 插入与 UTF-8 overlong encoding,用于对抗关键字/明文特征检测。
  • 产物是 byte[],因此它在链中不是最外层——外层需再套一个「读到该字节流就会 readObject」的 gadget 完成二次反序列化。

参数(@Param)

本类无独立 @Param;下列参数继承自基类 AbstractJavaNative,在使用 Java 原生 Payload 入口时可配置:

字段 名称 说明 默认 可选值
dirtyCollectionSize 随机集合脏数据大小 用集合类型包裹 object 并填充指定量脏数据,推荐使用 0 0 ~ 50000000
dirtyClassSize 填充垃圾类的数量 用 LinkedList 包裹随机垃圾类名,生成极慢 0 0 ~ 20000
resetSize TC_RESET 脏数据大小 重复插入 TC_RESET,配 gzip 可设很大 0 0 ~ 50000000
overlongMode Utf8OverlongEncoding UTF-8 overlong 编码混淆模式 "0" 0不开启 / 1混合2、3字节 / 2纯2字节 / 3纯3字节

适用版本与原理要点

  • 纯 JDK 实现,无第三方依赖,任何 JVM 均可。
  • 定位是二次反序列化的中间层内层完整 Java 链 → JavaNativeSerialization 序列化成 byte[] → 外层触发器(SignedObject / RMIConnector 等)readObject 时再次反序列化 byte[]
  • 混淆/脏数据能力面向对抗检测;UserCustomByteXXX 自定义字节流不支持脏数据混淆(description 已注明)。

所属预设链

关联

  • 上游(会读并再次反序列化该字节流):SignedObjectRMIConnectorJavaNativeSerialization 的外层触发器。
  • 下游(nextTags=JavaNativeDeserialize,被序列化的完整内层链):CommonsBeanutils1CommonsCollectionsK3FastjsonJackson