Jackson反序列化调用链完整分析过程
调试代码
JSTest.java
package com.DemoJackson.Study.DemoDefaultTyping.Demo5Serialized;
import com.fasterxml.jackson.databind.ObjectMapper;
public class JSTest {
public static void main(String[] args) throws Exception {
ObjectMapper mapper = new ObjectMapper();
mapper.enableDefaultTyping();
String json = "{\"age\":6,\"name\":\"mi1k7ea\",\"sex\":[\"com.DemoJackson.Study.DemoDefaultTyping.Demo5Serialized.MySex\",{\"sex\":1}]}";
Person p2 = mapper.readValue(json, Person.class);
System.out.println(p2);
}
}
Person.java
package com.DemoJackson.Study.DemoDefaultTyping.Demo5Serialized;
public class Person {
public int age;
public String name;
public Sex sex;
public Person() {
System.out.println("Person构造函数");
}
@Override
public String toString() {
return String.format("Person.age=%d, Person.name=%s, %s", age, name, sex == null ? "null" : sex);
}
}
Sex.java
package com.DemoJackson.Study.DemoDefaultTyping.Demo5Serialized;
public interface Sex {
public void setSex(int sex);
public int getSex();
}
MySex.java
package com.DemoJackson.Study.DemoDefaultTyping.Demo5Serialized;
public class MySex implements Sex {
int sex;
public MySex() {
System.out.println("MySex构造函数");
}
@Override
public int getSex() {
System.out.println("MySex.getSex");
return sex;
}
@Override
public void setSex(int sex) {
System.out.println("MySex.setSex");
this.sex = sex;
}
}
分析过程
以enableDefaultTyping反序列化为demo分析
在反序列化处断点
com/fasterxml/jackson/core/jackson-databind/2.7.9/jackson-databind-2.7.9.jar!/com/fasterxml/jackson/databind/ObjectMapper.class
ObjectMapper#_readMapAndClose
com/fasterxml/jackson/core/jackson-databind/2.7.9/jackson-databind-2.7.9.jar!/com/fasterxml/jackson/databind/deser/BeanDeserializer.class
BeanDeserializer#deserialize
com/fasterxml/jackson/core/jackson-databind/2.7.9/jackson-databind-2.7.9.jar!/com/fasterxml/jackson/databind/deser/BeanDeserializer.class
这个方法比较核心
BeanDeserializer#vanillaDeserialize
先进入到Object bean = this._valueInstantiator.createUsingDefault(ctxt);
com/fasterxml/jackson/core/jackson-databind/2.7.9/jackson-databind-2.7.9.jar!/com/fasterxml/jackson/databind/deser/std/StdValueInstantiator.class
_valueInstantiator#createUsingDefault
进入_defaultCreator.call()
com/fasterxml/jackson/core/jackson-databind/2.7.9/jackson-databind-2.7.9.jar!/com/fasterxml/jackson/databind/introspect/AnnotatedConstructor.class
AnnotatedConstructor#call
进入到了要反序列化类的构造函数里
回到
com/fasterxml/jackson/core/jackson-databind/2.7.9/jackson-databind-2.7.9.jar!/com/fasterxml/jackson/databind/deser/BeanDeserializer.class
BeanDeserializer#vanillaDeserialize
可以看到bean对象是Person类了
do while 循环,反序列化和赋值Person类的每个属性
进入到prop.deserializeAndSet(p, ctxt, bean)
com/fasterxml/jackson/core/jackson-databind/2.7.9/jackson-databind-2.7.9.jar!/com/fasterxml/jackson/databind/deser/impl/FieldProperty.class
FieldProperty#deserializeAndSet
分别是反序列化和赋值
先进入Object value = this.deserialize(p, ctxt)
com/fasterxml/jackson/core/jackson-databind/2.7.9/jackson-databind-2.7.9.jar!/com/fasterxml/jackson/databind/deser/SettableBeanProperty.class
SettableBeanProperty#deserialize
this._valueTypeDeserializer != null ? this._valueDeserializer.deserializeWithType(p, ctxt, this._valueTypeDeserializer) : this._valueDeserializer.deserialize(p, ctxt);
因为this._valueTypeDeserializer为null,所以进入this._valueDeserializer.deserialize(p, ctxt)
com/fasterxml/jackson/core/jackson-databind/2.7.9/jackson-databind-2.7.9.jar!/com/fasterxml/jackson/databind/deser/std/NumberDeserializers.class
NumberDeserializers#deserialize
进入p.getIntValue()
com/fasterxml/jackson/core/jackson-core/2.7.9/jackson-core-2.7.9.jar!/com/fasterxml/jackson/core/base/ParserBase.class
ParserBase#getIntValue
进入this._parseIntValue();
com/fasterxml/jackson/core/jackson-core/2.7.9/jackson-core-2.7.9.jar!/com/fasterxml/jackson/core/base/ParserBase.class
ParserBase#_parseIntValue
在这个方法里获取到了age的值
com/fasterxml/jackson/core/jackson-databind/2.7.9/jackson-databind-2.7.9.jar!/com/fasterxml/jackson/databind/deser/impl/FieldProperty.class
回到FieldProperty#deserializeAndSet
可以看到value的值为6
进入到this._field.set(instance, value);
给实例的属性赋值
com/fasterxml/jackson/core/jackson-databind/2.7.9/jackson-databind-2.7.9.jar!/com/fasterxml/jackson/databind/deser/BeanDeserializer.class
回到BeanDeserializer#vanillaDeserialize
同理给name属性赋值
可以看到age和name属性都已经赋值了,接下来给MySex类反序列化和赋值
进入到
com/fasterxml/jackson/core/jackson-databind/2.7.9/jackson-databind-2.7.9.jar!/com/fasterxml/jackson/databind/deser/SettableBeanProperty.class
SettableBeanProperty#deserialize
因为Sex属性的值是MySex类,所以进入到
this._valueDeserializer.deserializeWithType
com/fasterxml/jackson/core/jackson-databind/2.7.9/jackson-databind-2.7.9.jar!/com/fasterxml/jackson/databind/deser/AbstractDeserializer.class
AbstractDeserializer#deserializeWithType
进入到typeDeserializer.deserializeTypedFromObject(p, ctxt)
com/fasterxml/jackson/core/jackson-databind/2.7.9/jackson-databind-2.7.9.jar!/com/fasterxml/jackson/databind/jsontype/impl/AsArrayTypeDeserializer.class
AsArrayTypeDeserializer#_deserialize
进入到this._locateTypeId((JsonParser)p, ctxt);
_locateTypeId方法获取到MySex类
回到AsArrayTypeDeserializer#_deserialize
进入Object value = deser.deserialize((JsonParser)p, ctxt);
com/fasterxml/jackson/core/jackson-databind/2.7.9/jackson-databind-2.7.9.jar!/com/fasterxml/jackson/databind/deser/BeanDeserializer.class
BeanDeserializer#deserialize
这里很熟悉了,和之前实例化Person一样,现在实例化MySex类
com/fasterxml/jackson/core/jackson-databind/2.7.9/jackson-databind-2.7.9.jar!/com/fasterxml/jackson/databind/deser/BeanDeserializer.class
BeanDeserializer#vanillaDeserialize
对MySex类的属性赋值,也就是对sex属性赋值
com/fasterxml/jackson/core/jackson-databind/2.7.9/jackson-databind-2.7.9.jar!/com/fasterxml/jackson/databind/deser/impl/MethodProperty.class
MethodProperty#deserializeAndSet
进入到this._setter.invoke(instance, value);
最后跳转到MySex类的setSex方法,即就是通过setter方法去赋值的。
至此Jackson的反序列化调用链分析完~
总结
前提条件
在Jackson反序列化中,若调用了enableDefaultTyping()函数或使用@JsonTypeInfo注解指定反序列化得到的类的属性为JsonTypeInfo.Id.CLASS或JsonTypeInfo.Id.MINIMAL_CLASS,则会调用该属性的类的构造函数和setter方法。
满足下面三个条件之一即存在Jackson反序列化漏洞:
- 调用了ObjectMapper.enableDefaultTyping()函数;
- 对要进行反序列化的类的属性使用了值为JsonTypeInfo.Id.CLASS的@JsonTypeInfo注解;
- 对要进行反序列化的类的属性使用了值为JsonTypeInfo.Id.MINIMAL_CLASS的@JsonTypeInfo注解;
漏洞原理
当使用的JacksonPolymorphicDeserialization机制配置有问题时,Jackson反序列化就会调用属性所属类的构造函数和setter方法。那么我们只要传递进去的类的setter方法里放入执行命令的代码即可触发反序列化漏洞。类似于fastjson~
参考链接
https://www.mi1k7ea.com/2019/11/13/Jackson%E7%B3%BB%E5%88%97%E4%B8%80%E2%80%94%E2%80%94%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E6%BC%8F%E6%B4%9E%E5%9F%BA%E6%9C%AC%E5%8E%9F%E7%90%86