Fastjson 1.2.22-1.2.24反序列化漏洞分析
2021-01-08 16:20:51 Author: xz.aliyun.com(查看原文) 阅读量:328 收藏

Fastjson是Alibaba开发的Java语言编写的高性能JSON库,用于将数据在JSON和Java Object之间互相转换,提供两个主要接口JSON.toJSONString和JSON.parseObject/JSON.parse来分别实现序列化和反序列化操作。

项目地址:https://github.com/alibaba/fastjson

序列化

Student.java

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 int getAge() {
        System.out.println("getAge");
        return age;
    }

    public void setAge(int age) {
        System.out.println("setAge");
        this.age = age;
    }
}

然后通过Ser.java进行序列化

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

public class Ser {
    public static void main(String[] args){
        Student student = new Student();
        student.setName("ghtwf01");
        student.setAge(80);
        String jsonstring = JSON.toJSONString(student, SerializerFeature.WriteClassName);
        System.out.println(jsonstring);
    }
}

SerializerFeature.WriteClassNametoJSONString设置的一个属性值,设置之后在序列化的时候会多写入一个@type,即写上被序列化的类名,type可以指定反序列化的类,并且调用其getter/setter/is方法。

没加SerializerFeature.WriteClassName

反序列化

上面说了有parseObject和parse两种方法进行反序列化,现在来看看他们之间的区别

public static JSONObject parseObject(String text) {
        Object obj = parse(text);
        return obj instanceof JSONObject ? (JSONObject)obj : (JSONObject)toJSON(obj);
    }

parseObject其实也是使用的parse方法,只是多了一步toJSON方法处理对象。

看下面几种反序列化方法

一二种方法没用成功反序列化,因为没有确定到底属于哪个对象的,所以只能将其转换为一个普通的JSON对象而不能正确转换。所以这里就用到了@type,修改后代码如下

这样便能成功反序列化,可以看到parse成功触发了set方法,parseObject同时触发了set和get方法,因为这种autoType所以导致了fastjson反序列化漏洞

我们知道了Fastjson的autoType,所以也就能想到反序列化漏洞产生的原因是get或set方法中存在恶意操作,以下面demo为例

Student.java

import java.io.IOException;

public class Student {
    private String name;
    private int age;
    private String sex;

    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 int getAge() {
        System.out.println("getAge");
        return age;
    }

    public void setAge(int age) {
        System.out.println("setAge");
        this.age = age;
    }
    public void setSex(String sex) throws IOException {
        System.out.println("setSex");
        Runtime.getRuntime().exec("open -a Calculator");
    }
}

Unser.java

import com.alibaba.fastjson.JSON;

public class Unser {
    public static void main(String[] args){
        String jsonstring ="{\"@type\":\"Student\":\"age\":80,\"name\":\"ghtwf01\",\"sex\":\"man\"}";
        //System.out.println(JSON.parse(jsonstring));
        System.out.println(JSON.parseObject(jsonstring));
    }
}

Fastjson反序列化流程分析

在parseObject处下断点,跟进

public static JSONObject parseObject(String text) {
        Object obj = parse(text);
        return obj instanceof JSONObject ? (JSONObject)obj : (JSONObject)toJSON(obj);
    }

第一行将json字符串转化成对象,跟进parse

public static Object parse(String text) {
        return parse(text, DEFAULT_PARSER_FEATURE);
    }

继续跟进

public static Object parse(String text, int features) {
        if (text == null) {
            return null;
        } else {
            DefaultJSONParser parser = new DefaultJSONParser(text, ParserConfig.getGlobalInstance(), features);
            Object value = parser.parse();
            parser.handleResovleTask(value);
            parser.close();
            return value;
        }
    }

这里会创建一个DefaultJSONParser对象,在这个过程中有如下操作

int ch = lexer.getCurrent();
        if (ch == '{') {
            lexer.next();
            ((JSONLexerBase)lexer).token = 12;
        } else if (ch == '[') {
            lexer.next();
            ((JSONLexerBase)lexer).token = 14;
        } else {
            lexer.nextToken();
        }

判断解析的字符串是{还是[并设置token值,创建完成DefaultJSONParser对象后进入DefaultJSONParser#parse方法

因为之前设置了token值为12,所以进入如下判断

case 12:
            JSONObject object = new JSONObject(lexer.isEnabled(Feature.OrderedField));
            return this.parseObject((Map)object, fieldName);

在第一行会创建一个空的JSONObject,随后会通过 parseObject 方法进行解析,在解析后有如下操作

if (key == JSON.DEFAULT_TYPE_KEY && !lexer.isEnabled(Feature.DisableSpecialKeyDetect)) {
                        ref = lexer.scanSymbol(this.symbolTable, '"');

文章来源: http://xz.aliyun.com/t/8979
如有侵权请联系:admin#unsafe.sh