Skip to content

Latest commit

 

History

History
61 lines (52 loc) · 5.49 KB

File metadata and controls

61 lines (52 loc) · 5.49 KB

Xalan XStringForChars 调用toString链(XalanXString2)

  • 类别:javanative 别名:— 优先级:—
  • 作者:—
  • 依赖spring-aop:org.springframework.aop.target.HotSwappableTargetSourcexalan:xalan:2.6.0

作用

把内层对象包进 HotSwappableTargetSource,与一个 org.apache.xpath.objects.XStringForChars(Xalan 外置库版本,非 JDK 内置)一起放入一个 哈希碰撞的 HashMap。当 HashMap.readObject 反序列化并重建桶时触发 key 的 equals,经 HotSwappableTargetSource#equals → XStringForChars#equals 最终对内层对象调用任意 toString()。是「Java 原生/Hessian 反序列化 → 任意 toString」的经典跳板,且不依赖 JDK 内置 xalan(适配 xalan 被独立打包的环境,如高版本 JDK 移除 com.sun.org.apache.xpath.internal 或类加载受限时)。

链接标签

  • 入口 tagsJavaNativeDeserializeHessianDeserialize —— 承接 Java 原生反序列化或 Hessian 反序列化入口(HashMap.readObject 在两者中都会被触发)。
  • 衔接 nextTagsToString —— 之后需接一个「在 toString() 中触发危险行为」的 gadget(如 BadAttributeValueExpExceptionToString 之外的各类 *ToString sink、JdbcRowSetImpl 变体、Fastjson*ToString 等)。
  • excludes:无。

源码剖析

核心在 getObjectinvoke

public Object getObject(Object object) throws Exception {
    XStringForChars xString = new XStringForChars(new char[0], 0, 0);  // 空字符数组构造,str() 结果为 ""
    Reflections.set(xString, "m_strCache", "");                        // 预置字符串缓存,避免 equals 时重算/NPE
    HotSwappableTargetSource h1 = new HotSwappableTargetSource(object); // h1 包裹「真正要触发 toString 的内层对象」
    HotSwappableTargetSource h2 = new HotSwappableTargetSource(xString);// h2 包裹 XStringForChars
    HashMap map = PayloadHelper.makeMap(h1, h2);                       // 构造两 key hashCode 碰撞的 HashMap
    return map;
}
  • 碰撞式 HashMapPayloadHelper.makeMap(h1, h2) 生成一个把 h1h2 放进同一个桶HashMap(两者序列化后 hashCode 相同)。反序列化时 HashMap.readObject → putVal 发现桶内已有 key,遂调用 h2.equals(h1)(或 h1.equals(h2))比较。
  • HotSwappableTargetSource 桥接:Spring 的 HotSwappableTargetSource#equals 实现是 this.target.equals(other.target),于是转成 xString.equals(object)
  • XString#equals 触发 toStringXStringForChars 继承自 Xalan 的 XString,其 equals(Object obj2) 在对端不是 XString/Number/Boolean 时执行 str().equals(obj2.toString()) —— 这一步对 object(即内层 gadget 产物)调用了 toString(),把控制权交给下游 ToString sink。
  • m_strCache="" 的意义str() 会用字符数组拼字符串并缓存到 m_strCache;空数组本会得到 "",此处直接反射写入 "" 作为缓存,规避构造/序列化过程中对内部字符缓冲的依赖,保证 str() 稳定返回空串、equals 顺利进入 obj2.toString() 分支。
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);    // Fastjson 绕过场景:额外包一层 List
    }
    return resultObject;
}

invoke 遵循「由内向外」构建:先 chain.doCreate 取得内层对象,再 getObject 把它裹进碰撞 Map。若上下文里存在 FASTJSON_HANDLE_BYPASS_KEY(Fastjson 处理绕过),则用 CommonMethod.listWrap 再包一层 List,否则直接返回该 Map。

参数(@Param)

本类无 @Param,无用户可配字段;触发目标(下游 toString 逻辑)完全由链上下一环决定。

适用版本与原理要点

  • 依赖 spring-aop 提供 HotSwappableTargetSource(其 equals 用 target 相等性比较,是碰撞桥的关键)。
  • 依赖 xalan:xalan:2.6.0 提供外置 org.apache.xpath.objects.XStringForChars。这是与同族 XString2 的唯一区别:XString2 用 JDK 内置 com.sun.org.apache.xpath.internal.objects.XStringForChars,而本类用独立 Xalan 库版本——当目标 classpath 引入了独立 xalan(或 JDK 已移除内置 xpath 实现)时改用本变体。
  • 原理链:HashMap#readObject => HotSwappableTargetSource#equals => XStringForChars#equals => 任意对象 toString()
  • 触发不依赖比较器/getter,仅需 toString 语义,因此下游 sink 选择面广。

所属预设链

usedInChains 为空 —— 自由构件,未直接出现在 51 条预设链中。作为「JavaNative/Hessian → ToString」入口段,可与任意 nextTags=ToString 的 sink 自由组合。

关联

  • 同族(同一 toString 跳板家族,仅内部 XString 载体不同):
  • 下游(nextTags=ToString):各类 *ToString sink 及 JdbcRowSetImpl 等在 toString 中触发 JNDI/字节码加载的 gadget。