Skip to content

Latest commit

 

History

History
58 lines (47 loc) · 5.15 KB

File metadata and controls

58 lines (47 loc) · 5.15 KB

JNDI C3P0 二次反序列化(JavaBeanObjectFactoryHexDS)

  • 类别:jndi 别名:— 优先级:50
  • 作者:Unam4
  • 依赖c3p0

作用

构造一个 JNDI Reference,把工厂设为 c3p0 的 com.mchange.v2.naming.JavaBeanObjectFactory、目标类设为 com.mchange.v2.c3p0.WrapperConnectionPoolDataSource,并把内层 gadget 序列化后的字节以 Hex 编码塞进 userOverridesAsString 属性。受害端在 JNDI lookup 解析该 Reference、由 JavaBeanObjectFactory 还原 WrapperConnectionPoolDataSource 时,会对 userOverridesAsString 里的 HexAsciiSerializedMap.<hex>. 执行一次 Java 原生反序列化——从而把「JNDI 注入」转成「二次反序列化」,让上游的 JNDI 入口能够触发任意 Java 反序列化 payload。

链接标签

  • 入口 tagsReference —— 本 gadget 产出的就是一个 JNDI Reference 对象,供 LDAP/RMI 等 JNDI 服务端返回;上游若有 nextTags 指向 Reference 即可衔接到它。
  • 衔接 nextTagsJavaNativeDeserializePayloadCustomJavaDeserialize —— 之后需接一个能产出「Java 原生序列化字节」的下游(一个完整的 Java 反序列化利用链,如 CB1 / CC 链,或用户自定义序列化数据),由它给出将被 c3p0 二次反序列化的字节流。
  • excludes:无。

源码剖析

@GadgetTags(tags={"Reference"}, nextTags={"JavaNativeDeserializePayload", "CustomJavaDeserialize"})
public class JavaBeanObjectFactoryHexDS implements Gadget {
    public Reference getObject(Object object) throws Exception {
        String hex = Hex.encodeHexString((byte[]) CommonMethod.handleSerialized(object));
        String hexAsciiSerializedMapString = "HexAsciiSerializedMap." + hex + ".";
        Reference ref = new Reference(
                "com.mchange.v2.c3p0.WrapperConnectionPoolDataSource",
                "com.mchange.v2.naming.JavaBeanObjectFactory", null);
        ref.add(new StringRefAddr("userOverridesAsString", hexAsciiSerializedMapString));
        return ref;
    }

    @Override
    public Object invoke(GadgetContext context, GadgetChain chain) throws Exception {
        return this.getObject(chain.doCreate(context));
    }
}

逐段解释:

  • chain.doCreate(context):先由下游产出「内层对象」——这里期望的是一个可被 Java 原生序列化的完整反序列化利用链对象(对应 nextTagsJavaNativeDeserializePayload / CustomJavaDeserialize)。
  • CommonMethod.handleSerialized(object):把内层对象序列化成 byte[];随后 Hex.encodeHexString 把字节转成十六进制字符串。
  • "HexAsciiSerializedMap." + hex + ".":拼成 c3p0 约定的前缀格式。WrapperConnectionPoolDataSource 在读取 userOverridesAsString 时,会识别 HexAsciiSerializedMap. 前缀,取出中间的 hex,C3P0ImplUtils 将其解码为字节后进行 SerializableUtils.fromByteArray(即 ObjectInputStream.readObject),触发二次反序列化。
  • new Reference(className, factory, null):第 1 参是被还原的目标类 WrapperConnectionPoolDataSource,第 2 参是负责实例化并回填 JavaBean 属性的工厂 JavaBeanObjectFactory。受害端 NamingManager.getObjectInstance 调用该 factory,实例化目标类并按 RefAddr 设置属性 userOverridesAsString,从而在 setter 阶段引爆二次反序列化。

触发条件:受害进程存在可控 JNDI lookup(LDAP/RMI 引用返回本 Reference),且 classpath 具备 c3p0(com.mchange.v2.*)。由于走 c3p0 本地 factory,不需要远程 codebase 下载,也不受 com.sun.jndi.ldap.object.trustURLCodebase=false 限制——这正是本路线在高版本 JDK 下仍可用的关键。

参数(@Param)

本类无 @Param。核心输入(待二次反序列化的对象)由链上下游经 chain.doCreate(context) 注入,不作为用户直填参数。

适用版本与原理要点

  • 依赖目标 classpath 存在 c3p0,用到 com.mchange.v2.naming.JavaBeanObjectFactorycom.mchange.v2.c3p0.WrapperConnectionPoolDataSource
  • 本质是把 JNDI 注入「桥接」为二次反序列化:外层是 JNDI Reference,内层是任意 Java 原生反序列化链,绕过仅在入口做 JNDI 加固、却未收敛后续反序列化面的场景。
  • c3p0 本地 factory 路线不触发远程类加载,天然规避 trustURLCodebase 加固。
  • 最终能否 RCE 取决于内层反序列化链在受害端 classpath 上是否可用(如 commons-beanutils / commons-collections)。

所属预设链

自由构件,未直接出现在预设链(usedInChains 为空)。常作为「JNDI → 二次反序列化」的桥接环节,与任意 Java 原生反序列化链自由组合。

关联

  • 下游(nextTags=JavaNativeDeserializePayload / CustomJavaDeserialize):任意 Java 原生反序列化链,如 CommonsBeanutils1CommonsCollectionsK1 等,或 UserCustomSerializeData 类自定义序列化数据。
  • 同族(其他 JNDI Reference / c3p0 二次反序列化路线):LdapClassLoaderMchangeC3p0HexSerializeC3p0_C3p0HexSerializeMchangeC3p0Reference