首页 > 其他分享 >Fastjsonfan反序列化(一)

Fastjsonfan反序列化(一)

时间:2022-11-24 15:12:13浏览次数:44  
标签:name age id JSON Fastjsonfan User 序列化 public

前置知识

Fastjson 是一个 Java 库,可以将 Java 对象转换为 JSON 格式,当然它也可以将 JSON 字符串转换为 Java 对象。
Fastjson 可以操作任何 Java 对象,即使是一些预先存在的没有源码的对象(通过远程调用的方式)

//序列化
String text = JSON.toJSONString(obj); 
//反序列化
VO vo = JSON.parse(); //解析为JSONObject类型或者JSONArray类型
VO vo = JSON.parseObject("{...}"); //JSON文本解析成JSONObject类型
VO vo = JSON.parseObject("{...}", VO.class); //JSON文本解析成VO.class类

对象转字符串(序列化)

实体类User

package org.example;

public class User {
    public int id;
    public String name;
    public int age;

    public User() {
    }
    public User(int id, String name, int age) {
        this.id = id;
        this.name = name;
        this.age = age;
    }
    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
}

定义一个测试类

package org.example;

import org.example.User;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;

public class Test {
    public static void main(String[] args) {
        User user = new User();
        user.setId(1);
        user.setName("zhangsan");
        user.setAge(18);

        //对象转字符串
        String s1 = JSON.toJSONString(user);
        String s2 = JSON.toJSONString(user, SerializerFeature.WriteClassName);
        System.out.println(s1);
        System.out.println(s2);
    }
}

输出结果

{"age":18,"id":1,"name":"zhangsan"}
{"@type":"org.example.User","age":18,"id":1,"name":"zhangsan"}

字符串转对象(反序列化)

package org.example;

import org.example.User;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.SerializerFeature;

public class fastjsonTest {
    public static void main(String[] args) {
        User user = new User();
        user.setId(1);
        user.setName("zhangsan");
        user.setAge(18);

        //对象转字符串
        String s1 = JSON.toJSONString(user);
        String s2 = JSON.toJSONString(user, SerializerFeature.WriteClassName);
        System.out.println(s1);
        System.out.println(s2);

        System.out.println("s1==========================s1");
        //字符串转对象
        Object o1 = JSON.parse(s1);
        JSONObject jo1 = JSON.parseObject(s1);
        User user1 = JSON.parseObject(s1, User.class);
        System.out.println(o1);
        System.out.println(jo1);
        System.out.println(user1);

        System.out.println("s2=========================s2");
        Object o2 = JSON.parse(s2);
        JSONObject jo2 = JSON.parseObject(s2);
        User user2 = JSON.parseObject(s2, User.class);
        System.out.println(o2);
        System.out.println(jo2);
        System.out.println(user2);
    }
}

输出结果

{"age":18,"id":1,"name":"zhangsan"}
{"@type":"org.example.User","age":18,"id":1,"name":"zhangsan"}
s1==========================s1
{"name":"zhangsan","id":1,"age":18}
{"name":"zhangsan","id":1,"age":18}
User{id=1, name='zhangsan', age=18}
s2=========================s2
User{id=1, name='zhangsan', age=18}
{"name":"zhangsan","id":1,"age":18}
User{id=1, name='zhangsan', age=18}

fastjson通过JSON.toJSONString()将对象转为字符串(序列化),当使用SerializerFeature.WriteClassName参数时会将对象的类名写入@type字段中,在重新转回对象时会根据@type来指定类,进而调用该类的setget方法。因为这个特性,我们可以指定@type为任意存在问题的类,造成一些问题

利用

漏洞是利用fastjson autotype在处理json对象的时候,未对@type字段进行完全的安全性验证,攻击者可以传入危险类,并调用危险类连接远程rmi主机,通过其中的恶意类执行代码。攻击者通过这种方式可以实现远程代码执行漏洞的利用,获取服务器的敏感信息泄露,甚至可以利用此漏洞进一步对服务器数据进行修改,增加,删除等操作,对服务器造成巨大的影响。

set,get,is自动调用问题

构造Evil类

package org.example;

public class Evil {
    private String cmd;
    public Evil(){
        System.out.println("Evil()"+ this.hashCode());
    }

    public String getCmd() {
        System.out.println("getCmd()"+this.hashCode());
        return cmd;
    }

    public void setCmd(String cmd) {
        System.out.println("setCmd"+this.hashCode());
        this.cmd = cmd;
    }
}

写一个test测试下

package org.example;

import com.alibaba.fastjson.JSON;

public class Test {
    public static void main(String[] args) {
        String evil = "{\"@type\":\"org.example.Evil\",\"cmd\":\"calc\"}";
        JSON.parse(evil);
        System.out.println("-------------------");
        JSON.parseObject(evil);
        System.out.println("------------------");
        JSON.parseObject(evil, Object.class);
    }
}

跟进parseObject(evil)发现其方法发现也是使用parse解析的,但是多了一个(JSONObject)toJSON(obj)

这个方法调用的get,堆栈如下

getCmd:11, Evil (org.example)
invoke0:-1, NativeMethodAccessorImpl (sun.reflect)
invoke:62, NativeMethodAccessorImpl (sun.reflect)
invoke:43, DelegatingMethodAccessorImpl (sun.reflect)
invoke:498, Method (java.lang.reflect)
get:451, FieldInfo (com.alibaba.fastjson.util)
getPropertyValue:105, FieldSerializer (com.alibaba.fastjson.serializer)
getFieldValuesMap:439, JavaBeanSerializer (com.alibaba.fastjson.serializer)
toJSON:902, JSON (com.alibaba.fastjson)
toJSON:824, JSON (com.alibaba.fastjson)
parseObject:206, JSON (com.alibaba.fastjson)
main:13, Test (org.example)

那么setter在哪调用的?

com.alibaba.fastjson.util.JavaBeanInfo#build

在通过@type拿到类之后,通过反射拿到该类所有的方法存入methods,接下来遍历methods进而获取get、set方法,如上图。总结set方法自动调用的条件为:

  1. 方法名长度大于4
  2. 非静态方法
  3. 返回值为void或当前类
  4. 方法名以set开头
  5. 参数个数为1

当满足条件之后会从方法名截取属性名,截取时会判断_,如果是set_name会截取为name属性,具体逻辑如下:

当截取完但是找不到这个属性

会判断传入的第一个参数类型是否为布尔型,是的话就在截取完的变量前加上is,截取propertyName的第一个字符转大写和第二个字符,并且然后重新尝试获取属性字段。

比如:public boolean setBoy(boolean t) 会寻找isBoy字段。

set的整个判断就是:如果有setCmd()会绑定cmd属性,如果该类没有cmd属性会绑定isCmd属性。

get的判断

总结下就是:

  1. 方法名长度大于等于4
  2. 非静态方法
  3. 以get开头且第4个字母为大写
  4. 无传入参数
  5. 返回值类型继承自Collection Map AtomicBoolean AtomicInteger AtomicLong

当程序绑定了对应的字段之后,如果传入json字符串的键值中存在这个值,就会去调用执行对应的setter、构造方法

小结:

  1. parse(jsonStr) 构造方法+Json字符串指定属性的setter()+特殊的getter()
  2. parseObject(jsonStr) 构造方法+Json字符串指定属性的setter()+所有getter() 包括不存在属性和私有属性的getter()
  3. parseObject(jsonStr,Object.class) 构造方法+Json字符串指定属性的setter()+特殊的getter()

标签:name,age,id,JSON,Fastjsonfan,User,序列化,public
From: https://www.cnblogs.com/gk0d/p/16921888.html

相关文章

  • Java实体类为什么需要序列化和反序列化
    最近在学习做微服务的项目,在参考他人的微服务项目时,发现数据库表所对应的实体类都会实现Serializable接口,以往做的项目中并没有遇到过,也没有实现过这个接口,所以好奇实体类......
  • 序列化工具类
     importio.protostuff.LinkedBuffer;importio.protostuff.ProtostuffIOUtil;importio.protostuff.Schema;importio.protostuff.runtime.RuntimeSchema;importo......
  • Android 进程之间复杂的数据类型传输为啥一定需要序列化
    Android进程之间复杂的数据类型传输为啥一定需要序列化Linux特性Android系统都是基于Linux系统实现的,而这里Linux运行的时候,都是有进程隔离机制的。Linux采用了虚拟内......
  • SpringMVC中LocalDate、LocalDateTime、LocalTime、Date的序列化与反序列化
    这是在使用SpringMVC时经常会遇到的日期类型转换,直接上代码:@ConfigurationpublicclassConverterConfig{privateLoggerlogger=LoggerFactory.getLogger(Converter......
  • 19:使用pickle实现序列化和反序列化_神经元记忆移植
    ###使用pickle序列化Python中,一切皆对象,对象本质上就是一个“存储数据的内存块”。有时候,我们需要将“内存块的数据”保存到硬盘上,或者通过网络传输到其他的计算机上。......
  • Java序列化与反序列化
    序列化保证对象可传递性和完整性将对象转为字节流,可以保存在本地或在网上传输保存对象状态和重建反序列化根据字节流,重建对象为什么需要序列化与反序列化分布式对象......
  • 木马免杀代码篇之python反序列化分离免杀(一)
    前言本篇文章主要用到python来对CobaltStrike生成的Shellcode进行分离免杀处理,因此要求读者要有一定的python基础,下面我会介绍pyhon反序列化免杀所需用到的相关函数和......
  • [C# 中的序列化与反序列化](.NET 源码学习)
    [C#中的序列化与反序列化](.NET源码学习)关键词:序列化(概念与分析)   三种序列化(底层原理源码)   Stream(底层原理源码)   反射(底层原理源码)假如有一天我们要......
  • RedisTemplate进行对象序列化踩坑
    RedisTemplate默认使用JdkSerializationRedisSerializer对对象进行序列化RedisTemplate相关源码如下:private@NullableRedisSerializer<?>defaultSerializer;@Su......
  • java 序列化 浅克隆 深克隆
    序列化Java序列化技术可以使你将一个对象的状态写入一个Byte流里,并且可以从其它地方把该Byte流里的数据读出来,重新构造一个相同的对象。当两......