Skip to content

Latest commit

 

History

History
64 lines (54 loc) · 4.76 KB

File metadata and controls

64 lines (54 loc) · 4.76 KB

需要toString链触发的Jackson反序列化(JacksonToString)

  • 类别:javanative 别名:— 优先级:9
  • 作者:—
  • 依赖com.fasterxml.jackson.core:jackson-databind>2.9

作用

把内层对象包进 com.fasterxml.jackson.databind.node.POJONode,使其在 toString() 被调用时POJONode.toString() → 序列化该 POJO → 逐个调用其所有字段的 getter)触发下游 getter 型 sink。是一个「以 toString 为入口、以 getter 为出口」的桥接 gadget,常用于 Hessian、二次反序列化等需要 toString 触发点的场景。

链接标签

  • 入口 tagsToString —— 本 gadget 需要由一个「会调用其内层对象 toString()」的上一环来激活(如 BadAttributeValueExpException、Hessian 的 toString 触发点、XString 等)。
  • 衔接 nextTagsTemplatesImplWrapperChainTemplatesImplChainLdapAttributeChainSignedObjectChainMapMessageChainDataSourceChainsDataSourceWrapperChain —— 之后可接任意「暴露危险 getter」的 gadget,其中最常见的是 TemplatesImpl(getOutputPropertiesnewTransformer → 字节码 defineClass)。
  • excludes:无。

源码剖析

public Object getObject(Object object) throws Exception {
    POJONode node = new POJONode(object);   // 把内层对象包成 POJONode
    this.pre();                             // 关键:清除 writeReplace 干扰
    return node;
}

@Override
public Object invoke(GadgetContext context, GadgetChain chain) throws Exception {
    return this.getObject(chain.doCreate(context));   // 先取内层对象再包裹
}

invoke 遵循「由内向外」构建约定:chain.doCreate(context) 先产出内层的 getter 载体(如 TemplatesImpl),再用 POJONode 把它包起来。反序列化后一旦有人对该 POJONode 调用 toString(),Jackson 会对内部 POJO 做序列化式遍历,逐个反射调用其 getter,从而触达 sink。

public void pre() {
    ClassPool pool = ClassPool.getDefault();
    try {
        String classname = "com.fasterxml.jackson.databind.node.BaseJsonNode";
        ClassLoader loader = Thread.currentThread().getContextClassLoader();
        CtClass ctClass = pool.get(classname);
        CtMethod writeReplace = ctClass.getDeclaredMethod("writeReplace");
        ctClass.removeMethod(writeReplace);      // 删除 writeReplace
        ctClass.toClass(loader);
        ctClass.toClass(Class.forName(classname).getClassLoader());
    } catch (Exception exception) {
        // empty catch block
    }
}

pre() 借助 Javassist 在运行时把 BaseJsonNodePOJONode 的父类)里的 writeReplace 方法删除jackson-databind 高版本给 BaseJsonNode 加了 writeReplace,序列化 POJONode 时会被替换成一个无害的中间对象,导致 payload 失效;这里通过热修改字节码去掉该方法,保证 POJONode 本体能被原样序列化。注意这是对生成端 JVM 的自身类的修改,用于让 java-chains 能顺利产出可用的 POJONode 字节流。

参数(@Param)

本类无独立 @Param。要触发哪个 getter 由下游 gadget 通过链上下文决定(如 TemplatesImpl 注入 outputProperties)。

适用版本与原理要点

  • 适用 jackson-databind > 2.9(存在 POJONodeBaseJsonNodewriteReplace 的版本)。
  • 原理链路:toString()POJONode.toString() → 遍历序列化 POJO → 反射调用该对象所有字段的 getter。
  • 不稳定性(源码 description 明确指出):getter 的调用顺序不固定,有时 outputProperties 会先于 stylesheetDOM 被取值,只有这种顺序下 TemplatesImpl 才能成功触发。因此该链存在概率性失败,实战中可能需要多次尝试。

所属预设链

  • Hessian Jackson 链([JacksonToString, SignedObject, JavaNativeSerialization, Jackson, TWrap, TemplatesImpl, BytecodeConvert, Exec]
  • JDK17 RCE链[EventListenerListToStringHighJDK, JacksonToString, TWrapHighVersion, TemplatesImpl2, BytecodeConvert, Exec]
  • JDK17 RCE链2[TextAndMnemonicHashMapToStringHighJDK, JacksonToString, TWrapHighVersion, TemplatesImpl2, BytecodeConvert, Exec]

关联

  • 上游(提供 ToString 触发点):EventListenerListToStringHighJDKTextAndMnemonicHashMapToStringHighJDKBadAttributeValueExpExceptionToString
  • 下游(nextTags 指向的 getter 载体):TemplatesImplTWrap / TWrapHighVersion(TemplatesImpl 包装器)、LdapAttributeJdbcRowSetImpl
  • 同族:FastjsonToString1 / FastjsonToString2(同为 toString→getter 桥接,走 fastjson 的 JSONObject.toString)。