FastJson-前置基础

Fastjson

0x01Fastjson简介

Fastjson是Alibaba开发的Java语言编写的高性能JSON库,用于将数据在JSON和Java Object之间相互转换。
提供两个主要接口来分别实现序列化和反序列化操作
JSON.toJSONString将Java对象转换为json对象,序列化的过程。
JSON.parseObject/JSON.parse将json对象重新变回Java对象:反序列化的过程
所以可以简单的把json理解成是一个字符串。

0x02代码demo

1、序列化代码实现

这里通过Demo了解下如何使用Fastjson进行序列化和反序列化,以及其中的一些特性之间的区别等等。
在pom.xml里面导入Fastjson的依赖,这里先倒入1.2.24

1
2
3
4
5
<dependency>    
 <groupId>com.alibaba</groupId>    
 <artifactId>fastjson</artifactId>    
 <version>1.2.24</version>    
</dependency>  

Student

 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
public class Student {    
    private String name;    
    private int age;    
    
    public Student() {    
        System.out.println("构造函数");    
    }    
    
    public String getName() {    
        System.out.println("getName");    
        return name;    
    }    
    
    public void setName(String name) {    
        System.out.println("setName");    
        this.name = name;    
    }    
    
    public void setAge(int age) {    
        System.out.println("setAge");    
        this.age = age;    
    }    
    
    public int getAge() {    
        System.out.println("getAge");    
        return age;    
    }    
}  

然后写序列化的代码,调用JSON.toJsonString()来序列化Student类对象
StudentSerialize.java

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
import com.alibaba.fastjson.JSON;    
import com.alibaba.fastjson.serializer.SerializerFeature;    
    
public class StudentSerialize {    
    public static void main(String[] args) {    
        Student student = new Student();    
        student.setName("huahua");    
        String jsonString = JSON.toJSONString(student,SerializerFeature.WriteClassName);    
        System.out.println(jsonString);    
    }    
}  

这个地方,序列化的逻辑我们可以调试一下
首先会进到JSON这个类,然后进到它的toJSONString()的函数里面,new了一个SerializeWriter对象。我们的序列化在这一步就已经是完成了。
再进到JSON这个类里面的时候多出了个static的变量,写着members of JSON,这里特别注意一个值DEFAULT_TYPE_KEY@type,这个很重要。
里面定义了一些初值,赋值给out变量,这个out变量后续作为JSONSerializer类构造的参数。
继续往下,就是显示部分了,toString()方法,最后的运行结果。
很明显这句语句是关键的
String jsonString=JSON.toJSONString(student,SerializerFeature.WriteClassName);
我们关注它的参数
第一个参数是student,是一个对象。
第二个参数是SerializerFeature.WriteClassName,是JSON.toJSONString()中的一个设置属性值,设置之后在序列化的时候会多写入一个@tyoe,即协商被序列化的类名,type可以指定反序列化的类,并且调用其getter/setter/is方法。
Fastjson接受的JSON可以通过@type字段来指定该JSON应当还原成何种类型的对象,在反序列化的时候方便操作。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
// 设置了SerializerFeature.WriteClassName  
构造函数  
setName  
getAge  
getName  
{"@type":"Student","age":0,"name":"huahua"}  
// 未设置SerializerFeature.WriteClassName  
构造函数  
setName  
getAge  
getName  
{"age":0,"name":"huahua"}  

2、反序列化代码实现

调用JSON.parseObject(),代码如下
StudentUnserialize01.java

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
import com.alibaba.fastjson.JSON;    
import com.alibaba.fastjson.parser.Feature;    
    
public class StudentUnserialize01 {    
    public static void main(String[] args) {    
        String jsonString = "{\"@type\":\"Student\",\"age\":0,\"name\":\"huahua\"}";    
        Student student = JSON.parseObject(jsonString, Student.class, Feature.SupportNonPublicField);    
        System.out.println(student);    
        System.out.println(student.getClass().getName());    
    }    
}  

运行结果如图

0x03 fastjson反序列化漏洞原理

fastjson在反序列化的时候会找我们在@type中规定的类是哪个类,然后在反序列化的时候会自动调用这些settergetter方法的调用,注意!并不是所有的settergetter方法。
直接引用结论,Fastjson会对满足下列要求的setter/getter方法进行调用:
满足条件的setter:
非静态函数
返回类型为void或当前类
参数个数为1个
满足条件的getter
非静态方法
无参数
返回值类型继承自CollectionMapAtomicBooleanAtomiclntegerAtomicLong

漏洞原理

由前面知道,Fastjson是自己实现的一套序列化和反序列化机制,不过时用的Java原生的序列化和反序列化机制。无论是哪个版本,Fastjson反序列化漏洞的原理都是一样的,只不过不同版本是针对不同的黑名单或者利用不同利用链来进行绕过利用
通过Fastjson反序列化漏洞,攻击者可以传入一个恶意构造的JSON内容,程序对其进行反序列化后得到恶意类并执行了恶意类中的恶意函数,进而导致代码执行。

那么如何才能够反序列化恶意类呢?

由前面demo指导,Fastjson使用parseObject()/parse()进行反序列化的时候可以指定类型。如果指定的类型太大,包含太多子类,就有利用空间了。例如,如果指定类型为ObjectJSONObject,则可以反序列化出来任意类。例如代码写Object o = JSON.parseObject(poc,Object.class)就可以反序列化出Object类或其任意子类,而Object又是任意类的父类,所以就可以反序列化出所有类。

接着,如何才能反序列化得到恶意类中的恶意函数呢?

由前面知道,在某些情况下进行反序列化时会将反序列化得到的类的构造函数、getter方法、setter方法执行一遍,如果这三种方法中存在危险操作,则可能导致反序列化漏洞的存在。换句话说,就是攻击者传入要进行反序列化的类中的构造函数、getter方法、setter方法中要存在漏洞才能触发。
我们到DefaultJSONParser.parseObject(Map object, Object fieldName)中看下,JSON中以@type形式传入的类的时候,调用deserializer.deserialize()处理该类,并去调用这个类的settergetter方法:

1
2
3
4
5
6
7
public final Object parseObject(final Map object, Object fieldName) {  
    ...  
    // JSON.DEFAULT_TYPE_KEY即@type  
    if (key == JSON.DEFAULT_TYPE_KEY && !lexer.isEnabled(Feature.DisableSpecialKeyDetect)) {  
        ...  
        ObjectDeserializer deserializer = config.getDeserializer(clazz);  
        return deserializer.deserialze(this, clazz, fieldName);  
0%