Java代码审计之Jackson反序列化调用链完整分析过程

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分析

在反序列化处断点

image-20211029154323993

com/fasterxml/jackson/core/jackson-databind/2.7.9/jackson-databind-2.7.9.jar!/com/fasterxml/jackson/databind/ObjectMapper.class

ObjectMapper#_readMapAndClose

image-20211029154426911

com/fasterxml/jackson/core/jackson-databind/2.7.9/jackson-databind-2.7.9.jar!/com/fasterxml/jackson/databind/deser/BeanDeserializer.class
BeanDeserializer#deserialize

image-20211029154511992

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);

image-20211029154710682

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()

image-20211029154806063

com/fasterxml/jackson/core/jackson-databind/2.7.9/jackson-databind-2.7.9.jar!/com/fasterxml/jackson/databind/introspect/AnnotatedConstructor.class

AnnotatedConstructor#call

image-20211029154916853

进入到了要反序列化类的构造函数里

image-20211029154942430

回到

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)

image-20211029155101807

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)

image-20211029155205817

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)

image-20211029155355533

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()

image-20211029155444627

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();

image-20211029155530069

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的值

image-20211029155703399

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);
给实例的属性赋值

image-20211029155752221

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属性赋值

image-20211029160826025

可以看到age和name属性都已经赋值了,接下来给MySex类反序列化和赋值

image-20211029160924437

进入到

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

image-20211029161104928

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)

image-20211029161250447

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);

image-20211029161411386

_locateTypeId方法获取到MySex类

image-20211029161450281

回到AsArrayTypeDeserializer#_deserialize

进入Object value = deser.deserialize((JsonParser)p, ctxt);

image-20211029162436250

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类

image-20211029162521878

image-20211029162712126

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属性赋值

image-20211029162828435

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);

image-20211029162936457

最后跳转到MySex类的setSex方法,即就是通过setter方法去赋值的。

image-20211029163007378

至此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


   转载规则


《Java代码审计之Jackson反序列化调用链完整分析过程》 ske 采用 知识共享署名 4.0 国际许可协议 进行许可。