fastjson反序列化 TODO

发布时间 2023-08-15 15:22:35作者: sketch_pl4ne

参考链接

fastjson反序列化入门文章
https://tttang.com/archive/1579/
https://xz.aliyun.com/t/12096
ASM动态加载相关,如何查看内存生成的类的源码
https://juejin.cn/post/6974566732224528392#heading-6
https://blog.csdn.net/wjy160925/article/details/85288569
关闭ASM去调试
https://blog.csdn.net/qq_45854465/article/details/120960671
toJSONString()方法的源码分析(较浅)
https://blog.csdn.net/qq_31615049/article/details/85013129

Fastjson流程分析

先写个例子

package fastjson.pojo;


public class User{
    private String name;
    private int age;
    private String hobby;

    public User() {
    }

    public User(String name, int age, String hobby) {
        this.name = name;
        this.age = age;
        this.hobby = hobby;
    }

    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) {
        this.age = age;
    }

    public String getHobby() {
        return hobby;
    }

    public void setHobby(String hobby) {
        this.hobby = hobby;
    }
    @Override
    public String toString() {
        return "user{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", hobby='" + hobby + '\'' +
                '}';
    }
}
package fastjson;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.parser.ParserConfig;
import com.alibaba.fastjson.serializer.SerializeConfig;
import com.alibaba.fastjson.serializer.SerializerFeature;
import fastjson.pojo.User;


public class EasyStart extends User {
    public static void main(String[] args) {

        //序列化对象,会调用getter方法
        SerializeConfig.getGlobalInstance().setAsmEnable(false);
        User user = new User("Jasper",22,"fuck_some_people");
//        String s1 = JSON.toJSONString(user);
        String s2 = JSON.toJSONString(user, SerializerFeature.WriteClassName);
        System.out.println(s2);
        System.out.println("---------------------------------------");
//        String s2 = "{\"@type\":\"fastjson.pojo.User\",\"age\":22,\"hobby\":\"fuck_some_people\",\"name\":\"Jasper\"}";
        ParserConfig.getGlobalInstance().setAsmEnable(false);
        //情况1,parse里面调setter
        Object o1 = JSON.parse(s2);
        System.out.println(o1);
        System.out.println(o1.getClass().getName());
        System.out.println("---------------------------------------");
        //情况2,parseObject是parse的封装,所以会调setter,另外JSON.toJSON会调getter
        Object o2 = JSON.parseObject(s2);
        System.out.println(o2);
        System.out.println(o2.getClass().getName());
        System.out.println("---------------------------------------");
        //情况3,parseObject在里边调了setter
        Object o3 = JSON.parseObject(s2,Object.class);
        System.out.println(o3);
        System.out.println(o3.getClass().getName());
        System.out.println("---------------------------------------");
    }
}

输出结果:

调用了getName
{"@type":"fastjson.pojo.User","age":22,"hobby":"fuck_some_people","name":"Jasper"}
---------------------------------------
调用了setName
user{name='Jasper', age=22, hobby='fuck_some_people'}
fastjson.pojo.User
---------------------------------------
调用了setName
调用了getName
{"name":"Jasper","age":22,"hobby":"fuck_some_people"}
com.alibaba.fastjson.JSONObject
---------------------------------------
调用了setName
user{name='Jasper', age=22, hobby='fuck_some_people'}
fastjson.pojo.User
---------------------------------------

目前来看,序列化会调getter;反序列化会调setter,有的还会调getter。
这里我看大部分文章都没分析为什么会调用,这里我跟了一下源码,下面是分析结果。

序列化分析

get:450, FieldInfo
getPropertyValueDirect:110, FieldSerializer
write:196, JavaBeanSerializer
write:275, JSONSerializer
toJSONString:559, JSON
toJSONString:548, JSON
main:16, EasyStart

get里反射调用了getter

public Object get(Object javaObject) throws IllegalAccessException, InvocationTargetException {
    if (method != null) {
        Object value = method.
            invoke(javaObject, new Object[0]);
        return value;
    }
    return field.get(javaObject);
}

注意:需要用下面的语句把ASM关掉,会进到动态生成的类里,调试不了。
:::info
SerializeConfig.getGlobalInstance().setAsmEnable(false);
:::

反序列化分析

反序列化函数底层大差不差,最多是多了一些判断,这里只以parse()为例:

setValue:96, FieldDeserializer
deserialze:593, JavaBeanDeserializer
deserialze:188, JavaBeanDeserializer
deserialze:184, JavaBeanDeserializer
parseObject:368, DefaultJSONParser
parse:1327, DefaultJSONParser
parse:1293, DefaultJSONParser
parse:137, JSON
parse:128, JSON
main:22, EasyStart

setValue()里有反射调用setter方法:

public void setValue(Object object, Object value) {
    if (value == null //
        && fieldInfo.fieldClass.isPrimitive()) {
        return;
    }

    try {
        Method method = fieldInfo.method;
        if (method != null) {
            if (fieldInfo.getOnly) {
                if (fieldInfo.fieldClass == AtomicInteger.class) {
                    AtomicInteger atomic = (AtomicInteger) method.invoke(object);
                    if (atomic != null) {
                        atomic.set(((AtomicInteger) value).get());
                    }
                } else if (fieldInfo.fieldClass == AtomicLong.class) {
                    AtomicLong atomic = (AtomicLong) method.invoke(object);
                    if (atomic != null) {
                        atomic.set(((AtomicLong) value).get());
                    }
                } else if (fieldInfo.fieldClass == AtomicBoolean.class) {
                    AtomicBoolean atomic = (AtomicBoolean) method.invoke(object);
                    if (atomic != null) {
                        atomic.set(((AtomicBoolean) value).get());
                    }
                } else if (Map.class.isAssignableFrom(method.getReturnType())) {
                    Map map = (Map) method.invoke(object);
                    if (map != null) {
                        map.putAll((Map) value);
                    }
                } else {
                    Collection collection = (Collection) method.invoke(object);
                    if (collection != null) {
                        collection.addAll((Collection) value);
                    }
                }
            } else {
                method.invoke(object, value);
            }
            return;
        } else {
            final Field field = fieldInfo.field;
            
            if (fieldInfo.getOnly) {
                if (fieldInfo.fieldClass == AtomicInteger.class) {
                    AtomicInteger atomic = (AtomicInteger) field.get(object);
                    if (atomic != null) {
                        atomic.set(((AtomicInteger) value).get());
                    }
                } else if (fieldInfo.fieldClass == AtomicLong.class) {
                    AtomicLong atomic = (AtomicLong) field.get(object);
                    if (atomic != null) {
                        atomic.set(((AtomicLong) value).get());
                    }
                } else if (fieldInfo.fieldClass == AtomicBoolean.class) {
                    AtomicBoolean atomic = (AtomicBoolean) field.get(object);
                    if (atomic != null) {
                        atomic.set(((AtomicBoolean) value).get());
                    }
                } else if (Map.class.isAssignableFrom(fieldInfo.fieldClass)) {
                    Map map = (Map) field.get(object);
                    if (map != null) {
                        map.putAll((Map) value);
                    }
                } else {
                    Collection collection = (Collection) field.get(object);
                    if (collection != null) {
                        collection.addAll((Collection) value);
                    }
                }
            } else {
                if (field != null) {
                    field.set(object, value);
                }
            }
        }
    } catch (Exception e) {
        throw new JSONException("set property error, " + fieldInfo.name, e);
    }
}

注意:需要用下面的语句把ASM关掉,会进到动态生成的类里,调试不了。
:::info
ParserConfig.getGlobalInstance().setAsmEnable(false);
:::

利用点分析

从输出结果还可以看到,如果序列化的字符串里用@type指定了类名User,那么即使parseObject(s2, Object.class)指定序列化成Object.class,还是会反序列化成User
image.png
这意味着,反序列化成什么样的对象,是受传入的序列化字符串控制的,而前面说了Fastjson在反序列化和序列化的时候,都会自动调用getter和setter方法,那么如果我们传入有危险getter/setter方法的类的序列化字符串就能利用了。

1.2.24漏洞调试

还有很多东西没学,暂时卡住了。
image.png