转载

Jackson-databind 反序列化分析

java bean

public class Person {
    private String name;
    private Integer age;
    
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '/'' +
                ", age=" + age +
                '}';
    }
}

序列化

public static void main(String[] args) throws Exception{
    Person p = new Person();
    p.setAge(20);
    p.setName("kingkk");

    ObjectMapper mapper = new ObjectMapper();
    String json = mapper.writeValueAsString(p);
    System.out.println(json);
    // {"name":"kingkk","age":20}
}

反序列化

ObjectMapper mapper = new ObjectMapper();

String json = "{/"name/":/"kingkk/",/"age/":20}";

System.out.println(mapper.readValue(json, Person.class));
// Person{name='kingkk', age=20}

这也就是最基础的json序列化与反序列化操作

当然,这样简单的demo是没有什么安全问题的,问题出在于一些可选配置上

DefaultTyping

public enum DefaultTyping {
    /**
         * This value means that only properties that have
         * {@link java.lang.Object} as declared type (including
         * generic types without explicit type) will use default
         * typing.
         */
    JAVA_LANG_OBJECT,

    /**
         * Value that means that default typing will be used for
         * properties with declared type of {@link java.lang.Object}
         * or an abstract type (abstract class or interface).
         * Note that this does <b>not</b> include array types.
         *<p>
         * Since 2.4, this does NOT apply to {@link TreeNode} and its subtypes.
         */
    OBJECT_AND_NON_CONCRETE,

    /**
         * Value that means that default typing will be used for
         * all types covered by {@link #OBJECT_AND_NON_CONCRETE}
         * plus all array types for them.
         *<p>
         * Since 2.4, this does NOT apply to {@link TreeNode} and its subtypes.
         */
    NON_CONCRETE_AND_ARRAYS,

    /**
         * Value that means that default typing will be used for
         * all non-final types, with exception of small number of
         * "natural" types (String, Boolean, Integer, Double), which
         * can be correctly inferred from JSON; as well as for
         * all arrays of non-final types.
         *<p>
         * Since 2.4, this does NOT apply to {@link TreeNode} and its subtypes.
         */
    NON_FINAL
}

默认情况下, mapper.enableDefaultTyping() 什么参数都不加时,会默认调用 OBJECT_AND_NON_CONCRETE

public ObjectMapper enableDefaultTyping() {
    return enableDefaultTyping(DefaultTyping.OBJECT_AND_NON_CONCRETE);
}

重点说下 OBJECT_AND_NON_CONCRETE 这个参数

考虑如下这一种情况,序列化的对象的成员变量中,数据类型是一个 抽象类/接口 (或者直接看代码来的易懂点

public class Person {
    private String name;
    private Integer age;
    private Cls cls;
    
    // getter setter toString
}
public class Cls1 {
	private String name="cls1";
    
    // getter setter toString
}

public abstract class Cls {
}

这时候对这个类进行反序列化的时候,可以看到是无法进行反序列化的

Person p = new Person();
p.setAge(20);
p.setName("kingkk");
p.setCls(new Cls1());

ObjectMapper mapper = new ObjectMapper();

String json = mapper.writeValueAsString(p);
System.out.println(json);
// {"name":"kingkk","age":20,"cls":{"name":"cls1"}}

System.out.println(mapper.readValue(json, Person.class));

Jackson-databind 反序列化分析

大意就是说由于 Cls 这个变量是一个抽象类,所以反序列化失败。因为jackson也不知道要反序列化它的哪一个实现。

这时候就需要开启 enableDefaultTyping ,对数据类型进行指定

将上面的代码加一条 mapper.enableDefaultTyping(); ,就可以看到反序列化成功了

Person p = new Person();
p.setAge(20);
p.setName("kingkk");
p.setCls(new Cls1());

ObjectMapper mapper = new ObjectMapper();
mapper.enableDefaultTyping();
String json = mapper.writeValueAsString(p);
System.out.println(json);
// {"name":"kingkk","age":20,"cls":["cls.Cls1",{"name":"cls1"}]}

System.out.println(mapper.readValue(json, Person.class));
// Person{name='kingkk', age=20, cls=Cls1{name='cls1'}}

比较两次序列化后的json字符串,可以看到,多了指定的数据类型。

未开启enableDefaultTyping // {"name":"kingkk","age":20,"cls":{"name":"cls1"}}
开启了enableDefaultTyping // {"name":"kingkk","age":20,"cls":["cls.Cls1",{"name":"cls1"}]}

jackson也就可以根据数据类型去自动反序列化 出对应的类

那假如有一个 Cls2 ,将传入的json字符串变成 {"name":"kingkk","age":20,"cls":["cls.Cls2",{"name":"cls2"}]} 是否也可以反序列化出对应的类呢,显然是可以的。

修改下

String json = "{/"name/":/"kingkk/",/"age/":20,/"cls/":[/"cls.Cls1/",{/"name/":/"cls1/"}]}";
String json2 = "{/"name/":/"kingkk/",/"age/":20,/"cls/":[/"cls.Cls2/",{/"name/":/"cls2/"}]}";

System.out.println(mapper.readValue(json, Person.class));
//Person{name='kingkk', age=20, cls=Cls1{name='cls1'}}

System.out.println(mapper.readValue(json2, Person.class));
//Person{name='kingkk', age=20, cls=Cls2{name='cls2'}}

那假如将数据类型 Cls 改成 Object 呢?是否就可以反序列化出任意类?

修改下 Person 类型

public class Person {
    private String name;
    private Integer age;
    private Object cls;
    
    // getter setter toString
}

假设此时有一个存在危险函数的类

public class Vuln {
    String cmd;

    Vuln(){
        System.out.println("init");
    }

    public String getCmd() {
        System.out.println("get");
        return cmd;
    }

    public void setCmd(String cmd) throws IOException {
        System.out.println("set");
        this.cmd = cmd;
        Runtime.getRuntime().exec(cmd);
    }
}

尝试去反序列化这个 Vuln

ObjectMapper mapper = new ObjectMapper();
mapper.enableDefaultTyping();

String json = "{/"name/":/"kingkk/",/"age/":20,/"cls/":[/"vuln.Vuln/",{/"cmd/":/"calc/"}]}";

System.out.println(mapper.readValue(json, Person.class));

Jackson-databind 反序列化分析

可以看到是可以反序列化到任意类的,而且当 setter 或构造函数中存在危险函数时也会自动触发。

CVE-2017-7525

显然,实际代码中开发也几乎不可能写出那样不靠谱的 Vuln 类,也为了寻找一种更为通用的反序列化链,也就是所说的Gadget

CVE-2017-7525 中就是利用JDK7u21的 com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl 作为Gadget。

具体的触发流程我也没具体跟进了,总的原理和之前 Vuln 类中的原理类似。

触发的调节为

enableDefaultTyping
Object

在这篇文章中也了解到 http://www.leadroyal.cn/?p=630

当java bean的字段上加上了

@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)
@JsonTypeInfo(use = JsonTypeInfo.Id.MANIMAL_CLASS)

两个注解时,即使没有开启 enableDefaultTyping 也可以进行指定类的反序列化。亲测有效。

漏洞修复

至于漏洞的修补比较惊讶的是基于黑名单的修复,仅仅限制了可以反序列化的类,导致之前的Poc失效。

https://github.com/FasterXML/jackson-databind/commit/60d459cedcf079c6106ae7da2ac562bc32dcabe1

Jackson-databind 反序列化分析

也就意味着只要找到新的Gadget就还是可以利用这个洞(咋感觉比之前Weblogic XMLDecoder的修复还要不靠谱!

原文  https://www.kingkk.com/2019/05/Jackson-databind-反序列化分析/
正文到此结束
Loading...