Loading

Fastjson反序列化漏洞基础

Fastjson反序列化漏洞基础

FastJson是alibaba的一款开源JSON解析库,可用于将Java对象转换为其JSON表示形式,也可以用于将JSON字符串转换为等效的Java对象。

0x0 简单使用

pom.xml加入FastJson

<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.24</version>
</dependency>

新建一个User1类,注意这个类有两个属性age和name,并且其两个属性都有get/set方法:

package com.yy.FastJson.demo;

public class User1 {
    private String name;

    public User1() {
        System.out.println("User()");
    }

    public String getName() {
        System.out.println("getName()");
        return name;
    }

    public void setName(String name) {
        System.out.println("setName()");
        this.name = name;
    }
}


0x1 Fastjson的序列化

fastjson是用toJSONString方法来进行的序列化,使用方法:

package com.yy.FastJson.demo;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;

public class serialize {
    public static void main(String[] args){
        User student = new User();
        student.setName("yangyang");
        String st = JSON.toJSONString(student, SerializerFeature.WriteClassName);
        System.out.println(st);
    }
}

这里输出结果为:

image-20210729160607190

可以看到在序列化成json字符串时候会调用get方法,而这里的SerializerFeature.WriteClassName是toJSONString()方法中的一个属性值,设置之后在序列化的时候会多写入一个@type,后面指定反序列化的类名

# 设置了SerializerFeature.WriteClassName属性
{"@type":"com.yy.FastJson.demo.User1","name":"yangyang"}

# 未设置SerializerFeature.WriteClassName属性
{"name":"yangyang"}

0x2 Fastjson的反序列化

在fastjson中,有三种方式进行反序列化:

JSON.parse(json)
JSON.parseObject(json)
JSON.parseObject(json, User.class)

新建一个unserialize类来理解这三个方法的区别:

package com.yy.FastJson.demo;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;

public class unserialize {
    public static void main(String[] args){
        String json ="{\"@type\":\"com.yy.FastJson.demo.User1\",\"name\":\"yangyang\"}";

        System.out.println("parse(json)");
        User1 obj01 = (User1)JSON.parse(json);
        System.out.println(obj01);
        System.out.println("--------------------------------------");

        System.out.println("parseObject(json)");
        JSONObject obj02 = JSON.parseObject(json);
        System.out.println(obj02);
        System.out.println("--------------------------------------");

        System.out.println("parseObject(json, User.class)");
        User1 obj03 = JSON.parseObject(json, User1.class);
        System.out.println(obj03);
    }
}

输出结果:

parse(json)
User()
setName()
com.yy.FastJson.demo.User1@6d21714c
--------------------------------------
parseObject(json)
User()
setName()
getName()
{"name":"yangyang"}
--------------------------------------
parseObject(json, User.class)
User()
setName()
com.yy.FastJson.demo.User1@5b80350b

可以看到,只有parseObject(json)调用了getName方法

其原因是因为parseObject(json) 本质上也是调用 parse(json) 进行反序列化的。但是 parseObject(json) 会额外的将Java对象转为 JSONObject对象,即 JSON.toJSON(),所以会多调用一个get方法

引出问题:是不是除了parseObject(json) ,其余两种方式是无法调用get方法的呢

新建一个User2,加入一个age方法,并且类型是Properties

import java.util.Properties;

public class User2 {
    private String name;
    private Properties age;

    public User2() {
        System.out.println("User()");
    }

    public String getName() {
        System.out.println("getName()");
        return name;
    }

    public void setName(String name) {
        System.out.println("setName()");
        this.name = name;
    }

    public Properties getAge() {
        System.out.println("getAge()");
        return age;
    }

    public Properties setAge() {
        System.out.println("setAge()");
        return age;
    }
}

修改unserialize里面得User1成User2

package com.yy.FastJson.demo;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;

public class unserialize {
    public static void main(String[] args){
        String json ="{\"@type\":\"com.yy.FastJson.demo.User2\",\"name\":\"yangyang\",\"age\":{ }}";

        System.out.println("parse(json)");
        User2 obj01 = (User2)JSON.parse(json);
        System.out.println(obj01);
        System.out.println("--------------------------------------");

        System.out.println("parseObject(json)");
        JSONObject obj02 = JSON.parseObject(json);
        System.out.println(obj02);
        System.out.println("--------------------------------------");

        System.out.println("parseObject(json, User.class)");
        User2 obj03 = JSON.parseObject(json, User2.class);
        System.out.println(obj03);
    }
}

运行结果:

parse(json)
User()
setName()
getAge()
com.yy.FastJson.demo.User2@4459eb14
--------------------------------------
parseObject(json)
User()
setName()
getAge()
getAge()
getName()
{"name":"yangyang"}
--------------------------------------
parseObject(json, User.class)
User()
setName()
getAge()
com.yy.FastJson.demo.User2@4f2410ac

可以看到,所有的getAge方法都调用了一次。

这里除了parseObject(json)会直接调用getAge()外,其他两种方式调用getAge方法是有条件的。

条件一、方法名需要长于4

条件二、不是静态方法

条件三、以get字符串开头,且第四个字符需要是大写字母

条件四、方法不能有参数传入

条件五、继承自Collection || Map || AtomicBoolean || AtomicInteger ||AtomicLong

条件六、此getter不能有setter方法(程序会先将目标类中所有的setter加入fieldList列表,因此可以通过读取fieldList列表来判断此类中的getter方法有没有setter)

更详细的解析可以看看这篇文章:

https://www.anquanke.com/post/id/211035#h3-3

为了方便记忆,拿了一张大佬的图:

image-20210729104847866

图片来源:https://www.cnblogs.com/sijidou/p/13121332.html?ivk_sa=1024320u

parseObject(json)会直接调用get方法,其他的则需要满足条件

所以,在反序列化的时候,会把目标类的构造函数、getter方法、setter方法、is方法执行,如果此时这四个方法中有危险操作,则会导致反序列化漏洞。

参考:

https://www.anquanke.com/post/id/211035#h2-5

https://blog.csdn.net/qq_34101364/article/details/111706189

posted @ 2021-07-31 11:01  Atomovo  阅读(262)  评论(2编辑  收藏  举报