JAVA 反序列化漏洞入门学习笔记(一)--反序列化简介

JAVA 真的令人头大

参考文章

Java反序列化漏洞从入门到深入

JAVA 序列化与反序列化

简介

同 PHP/Python 类似,java 序列化的目的是将程序中对象状态转换成以数据流形式,反序列化是将数据流恢复为对象。
此举可以有效地实现多平台之间的通信、对象持久化存储。

序列化实例

import java.io.*;

//定义一个可序列化的类,该类必须实现 java.io.Serializable 接口
class Giao implements java.io.Serializable
{
    public String name;
    public String motto;
    public void saygiao()
    {
        System.out.println(this.motto);
    }
    // 自定义 readObject 方法
    private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException{
        //执行默认的readObject()方法
        in.defaultReadObject();
        
        //执行命令
        Runtime.getRuntime().exec("calc.exe");
    }
}


//序列化/反序列化
public class SerializeGiao
{
    public static void main(String [] args) throws IOException, ClassNotFoundException{
        //实例化一个可序列化对象
        Giao testClass = new Giao();
        testClass.name = "说唱带师";
        testClass.motto = "一给我哩 giao giao!";
        
        //序列化
        //将序列化后的对象写入到文件
        FileOutputStream fos = new FileOutputStream("test.ser");
        ObjectOutputStream os = new ObjectOutputStream(fos);
        os.writeObject(testClass);
        os.close();
        fos.close();
        
        //反序列化
        Giao obj = null;
        //从文件读取序列化的结果后进行反序列化
        FileInputStream fis = new FileInputStream("test.ser");
        ObjectInputStream ois = new ObjectInputStream(fis);
        obj = (Giao)ois.readObject();
        ois.close();
        fis.close();
    

        System.out.println(obj.name);
        System.out.println(obj.motto);
    }
}



//由此可见:人生苦短,我用 Python

序列化结果:

反序列化结果:

序列化条件

一个类的对象要想序列化成功,必须满足两个条件:

  • 该类必须实现 java.io.Serializable 或 java.io.Externalizable 接口。
    Externalizable 接口继承自 Serializable 接口,实现Externalizable 接口的类完全由自身来控制序列化的行为,而仅实现 Serializable 接口的类可以采用默认的序列化方式 。
    class Giao implements java.io.Serializable{}

  • 该类的所有属性必须是可序列化的。如果有一个属性不是可序列化的,则该属性必须注明是短暂的。

实现序列化/反序列化的方法

其中实现序列化与反序列化的两个类为:

  • java.io.ObjectOutputStream
    序列化:首先给该类传入一个文件对象(用于写入序列化结果),然后通过调用该类的 writeObject(目标对象) 方法将目标对象写入到文件

  • java.io.ObjectInputStream
    反序列化:首先给该类传入一个文件对象(用于读取文件中的序列化结果),然后通过调用该类的 readObject() 方法将其反序列化为目标对象

看到下面代码的执行结果:

输出了 1ndex,说明反序列化时调用了用户类 Giao 中的 readObject 方法,并且当我注释 in.defaultReadObject(); 代码,实际会输出 null
因此,实际上完成反序列化的操作的具体步骤是用户类 Giao 中的 readObject 方法,也就是继承自 Serializable 接口的 readObject 方法

为什么会出现反序列化漏洞

当被反序列化的数据流用户可控时,那么攻击者即可通过构造恶意输入,让反序列化产生非预期的对象,在此过程中执行构造的任意代码
关键点在于用户自定义类中的 readObject() 方法形成了不安全的类,导致了反序列化安全问题

简单的 demo

漏洞代码:

class RCE implements java.io.Serializable {
    public String cmd;

    private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException {
        in.defaultReadObject();
        Runtime.getRuntime().exec(cmd);
    }

public class ToRCE{
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        RCE testClass = new RCE();
        testClass.cmd = "calc";
        
        FileOutputStream fileoutputstream = new FileOutputStream("RCE.ser");
        ObjectOutputStream outputstream = new ObjectOutputStream(fileoutputstream);
        outputstream.writeObject(testClass);
        outputstream.close();
        
        
        FileInputStream fileinputstream = new FileInputStream("RCE.ser");
        ObjectInputStream inputstream = new ObjectInputStream(fileinputstream);
        RCE obj = (RCE)inputstream.readObject();
        inputstream.close();
    }
}

看完上面的代码,是不是觉得 JAVA 反序列化漏洞跟 PHP 反序列化漏洞有些相似
但是怎么会有人写这么愚蠢的代码呢?JAVA 反序列化的高端操作还得看 构造反序列化链

JAVA 反序列化漏洞入门学习笔记(二):JAVA 反射

posted @ 2021-02-24 17:54  1ndex-  阅读(790)  评论(0编辑  收藏  举报