初探fastjson系列漏洞

参考几位师傅的文章复现分析下fastjson的漏洞

0x01 序列化和反序列化

为了方便修改fastjson版本,利用maven构建项目,

直接用了rai4over师傅的源码,User()的成员及其属性如下:

image-20200907111204669

序列化

  • 非自省的序列化JSON.toJSONString(Object object)
1
2
3
4
5
6
7
8
9
10
11
import com.alibaba.fastjson.JSON;

public class Test {
public static void main(String[] args) {
User a = new User();
System.out.println("===========================");
String jsonstr_a = JSON.toJSONString(a);
System.out.println("===========================");
System.out.println(jsonstr_a);
}
}

image-20200907112008933

只要存在get方法(不管是public还是private)都被调用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
{
"age1": "a1",
"age2": "a2",
"name1": "mount4in1",
"name2": "mount4in2",
"name3": "mount4in3",
"name4": "mount4in4",
"prop1_1": {
"prop1_1": "1_1"
},
"prop1_2": {
"prop1_2": "1_2"
},
"prop1_3": {
"prop1_3": "1_3"
},
"prop1_4": {
"prop1_4": "1_4"
},
"prop2_1": {
"prop2_1": "2_1"
},
"prop2_2": {
"prop2_2": "2_2"
}
}

可以发现除了private属性而且不存在对应的get方法的成员外,其他的成员均被序列化。

  • 自省的序列化toJSONString(Object object, SerializerFeature... features)
1
2
3
4
5
6
7
8
9
10
11
12
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;

public class Test {
public static void main(String[] args) {
User a = new User();
System.out.println("===========================");
String jsonstr_a = JSON.toJSONString(a,SerializerFeature.WriteClassName);
System.out.println("===========================");
System.out.println(jsonstr_a);
}
}

image-20200907112634394

只要存在get方法(不管是public还是private)都被调用。与非自省相同。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
{
"@type": "User",
"age1": "a1",
"age2": "a2",
"name1": "mount4in1",
"name2": "mount4in2",
"name3": "mount4in3",
"name4": "mount4in4",
"prop1_1": {
"@type": "java.util.Properties",
"prop1_1": "1_1"
},
"prop1_2": {
"@type": "java.util.Properties",
"prop1_2": "1_2"
},
"prop1_3": {
"@type": "java.util.Properties",
"prop1_3": "1_3"
},
"prop1_4": {
"@type": "java.util.Properties",
"prop1_4": "1_4"
},
"prop2_1": {
"@type": "java.util.Properties",
"prop2_1": "2_1"
},
"prop2_2": {
"@type": "java.util.Properties",
"prop2_2": "2_2"
}
}

与非自省的区别是增加了@type字段,可以得到对象的类型。

反序列化

  • 反序列化分三种,分别是parseObject(String text, Class<T> clazz)JSONObject parseObject(String text)parse(String text)。反序列化时要把User.java中的构造函数注释掉。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.SerializerFeature;

public class Test {
public static void main(String[] args) {
String jsonstr_a = "{\"age1\":\"a1\",\"age2\":\"a2\",\"age3\":\"a3\",\"age4\":\"a4\",\"name1\":\"mount4in1\",\"name2\":\"mount4in2\",\"name3\":\"mount4in3\",\"name4\":\"mount4in4\",\"prop1_1\":{\"prop1_1\":\"1_1\"},\"prop1_2\":{\"prop1_2\":\"1_2\"},\"prop1_3\":{\"prop1_3\":\"1_3\"},\"prop1_4\":{\"prop1_4\":\"1_4\"},\"prop2_1\":{\"prop2_1\":\"2_1\"},\"prop2_2\":{\"prop2_2\":\"2_2\"},\"prop2_3\":{\"prop2_3\":\"2_3\"},\"prop2_4\":{\"prop2_4\":\"2_4\"}}";
String jsonstr_b = "{\"@type\":\"User\",\"age1\":\"a1\",\"age2\":\"a2\",\"age3\":\"a3\",\"age4\":\"a4\",\"name1\":\"mount4in1\",\"name2\":\"mount4in2\",\"name3\":\"mount4in3\",\"name4\":\"mount4in4\",\"prop1_1\":{\"@type\":\"java.util.Properties\",\"prop1_1\":\"1_1\"},\"prop1_2\":{\"@type\":\"java.util.Properties\",\"prop1_2\":\"1_2\"},\"prop1_3\":{\"@type\":\"java.util.Properties\",\"prop1_3\":\"1_3\"},\"prop1_4\":{\"@type\":\"java.util.Properties\",\"prop1_4\":\"1_4\"},\"prop2_1\":{\"@type\":\"java.util.Properties\",\"prop2_1\":\"2_1\"},\"prop2_2\":{\"@type\":\"java.util.Properties\",\"prop2_2\":\"2_2\"},\"prop2_3\":{\"@type\":\"java.util.Properties\",\"prop2_3\":\"2_3\"},\"prop2_4\":{\"@type\":\"java.util.Properties\",\"prop2_4\":\"2_4\"}}";
System.out.println("============JSON.parseObject()指定类,非自省===============");
User a = JSON.parseObject(jsonstr_a, User.class);
System.out.println(a);

System.out.println("============JSON.parseObject()自省===============");
JSONObject b = JSON.parseObject(jsonstr_b);
System.out.println(b);

System.out.println("============JSON.parse()===============");
Object c = JSON.parse(jsonstr_b);
System.out.println(c);
}
}

image-20200907122925167

  • JSON.parseObject()指定类,非自省

    只要存在set方法(不管是public还是private)都被调用。额外调用了getprop2_2();//private且有get

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    User {
    name1 = 'mount4in1', name2 = 'mount4in2', name3 = 'mount4in3', name4 = 'mount4in4', age1 = 'a1', age2 = 'null', age3 = 'a3', age4 = 'null', prop1_1 = {
    prop1_1 = 1 _1
    }, prop1_2 = {
    prop1_2 = 1 _2
    }, prop1_3 = {
    prop1_3 = 1 _3
    }, prop1_4 = {
    prop1_4 = 1 _4
    }, prop2_1 = {
    prop2_1 = 2 _1
    }, prop2_2 = {
    prop2_2 = 2 _2
    }, prop2_3 = {
    prop2_3 = 2 _3
    }, prop2_4 = null
    }

    public属性或含有set方法的成员都被赋值,private属性而且没有set方法的的成员赋值为null,但是这里prop2_2被赋值了,不知道什么原因。

  • JSON.parseObject()自省

    所有的set和get方法都被调用,getProp2_2调用了两次。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    {
    "prop1_3": {
    "prop1_3": "1_3"
    },
    "prop2_2": {
    "prop2_2": "2_2"
    },
    "prop1_4": {
    "prop1_4": "1_4"
    },
    "name4": "mount4in4",
    "prop1_1": {
    "prop1_1": "1_1"
    },
    "name3": "mount4in3",
    "prop1_2": {
    "prop1_2": "1_2"
    },
    "prop2_1": {
    "prop2_1": "2_1"
    },
    "name2": "mount4in2",
    "name1": "mount4in1",
    "age1": "a1"
    }

    这里public属性的成员都被赋值,private属性的的只有age1和prop2_1被赋值。

  • JSON.parse()

    只要存在set方法(不管是public还是private)都被调用。额外调用了getprop2_2();//private且有get

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    User {
    name1 = 'mount4in1', name2 = 'mount4in2', name3 = 'mount4in3', name4 = 'mount4in4', age1 = 'a1', age2 = 'null', age3 = 'a3', age4 = 'null', prop1_1 = {
    prop1_1 = 1 _1
    }, prop1_2 = {
    prop1_2 = 1 _2
    }, prop1_3 = {
    prop1_3 = 1 _3
    }, prop1_4 = {
    prop1_4 = 1 _4
    }, prop2_1 = {
    prop2_1 = 2 _1
    }, prop2_2 = {
    prop2_2 = 2 _2
    }, prop2_3 = {
    prop2_3 = 2 _3
    }, prop2_4 = null
    }

    public属性或含有set方法的成员都被赋值,private属性而且没有set方法的的成员赋值为null,但是这里prop2_2被赋值了,不知道什么原因。

ps:可以通过传递Feature.SupportNonPublicField来对没有set方法的private属性成员赋值。

1
2
3
4
5
6
7
8
9
10
11
12
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.parser.Feature;

public class Test2 {
public static void main(String[] args) {
String jsonstr_a = "{\"age1\":\"a1\",\"age2\":\"a2\",\"age3\":\"a3\",\"age4\":\"a4\",\"name1\":\"mount4in1\",\"name2\":\"mount4in2\",\"name3\":\"mount4in3\",\"name4\":\"mount4in4\",\"prop1_1\":{\"prop1_1\":\"1_1\"},\"prop1_2\":{\"prop1_2\":\"1_2\"},\"prop1_3\":{\"prop1_3\":\"1_3\"},\"prop1_4\":{\"prop1_4\":\"1_4\"},\"prop2_1\":{\"prop2_1\":\"2_1\"},\"prop2_2\":{\"prop2_2\":\"2_2\"},\"prop2_3\":{\"prop2_3\":\"2_3\"},\"prop2_4\":{\"prop2_4\":\"2_4\"}}";
System.out.println(jsonstr_a);
System.out.println("===========================");
User b = JSON.parseObject(jsonstr_a, User.class, Feature.SupportNonPublicField);
System.out.println(b);
}
}

image-20200907125946234

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
User {
name1 = 'mount4in1', name2 = 'mount4in2', name3 = 'mount4in3', name4 = 'mount4in4', age1 = 'a1', age2 = 'a2', age3 = 'a3', age4 = 'a4', prop1_1 = {
prop1_1 = 1 _1
}, prop1_2 = {
prop1_2 = 1 _2
}, prop1_3 = {
prop1_3 = 1 _3
}, prop1_4 = {
prop1_4 = 1 _4
}, prop2_1 = {
prop2_1 = 2 _1
}, prop2_2 = {
prop2_2 = 2 _2
}, prop2_3 = {
prop2_3 = 2 _3
}, prop2_4 = {
prop2_4 = 2 _4
}
}

0x02