java序列化与反序列化

序列化与反序列化

​ 序列化就是将一个对象转化为二进制内容,并且进行持久化存储或者网络传输过程。反序列化从文件或者网络读入二进制序列并且将其转化为Java对象。

​ 一个对象要实现序列化必须实现java.io.Serializable接口。Serializable无任何方法,只是一个**“标记接口”(Marker Interface)**,或者实现 java.io.Externalizable接口的方法。同时java对象中支持在类中定义如下函数:

private void writeObject(java.io.ObjectOutputStream out) throws IOException
private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException;

​ 这两个函数不是java.io.Serializable的接口函数,而是约定的函数,如果一个类实现了这两个函数,那么在序列化和反序列化的时候ObjectInputStream.readObject()和ObjectOutputStream.writeObject()会主动调用这两个函数。这也是反序列化漏洞产生的根本原因

序列化步骤

  1. 创建一个ObjectOutputStream输出流;
  2. 调用ObjectOutputStream对象的writeObject输出可序列化对象

反序列化步骤

  1. 创建一个ObjectInputStream输入流;
  2. 调用ObjectInputStream对象的readObject()得到序列化的对象。

序列化和反序列化的样例代码如下:
Payload实现了Serializable 并且自定义了readObject和writeObject方法

//Payload.java 
public class Payload implements Serializable {

    private String msg;


    public Payload() {
    }

    public Payload(String msg) {
        this.msg = msg;
    }

    public String getMsg() {
        return msg;
    }

    public void setMsg(String msg) {
        this.msg = msg;
    }


    public String say() {
        return "Hello " + msg;
    }
			
     /**
     * 自定义反序列化逻辑,实现rce
     * @param objectInputStream
     * @throws IOException
     */
    private void readObject(ObjectInputStream objectInputStream) throws IOException{
        System.out.println("do serialize....");
        Process result = Runtime.getRuntime().exec("ifconfig");

        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(result.getInputStream()));
        do{
            System.out.println(bufferedReader.readLine());
        }while (bufferedReader.readLine() != null);
    }

    private void writeObject(ObjectOutputStream objectOutputStream) throws IOException{
        System.out.println("do deserialize....");
    }
}
// SerializeDemo.java
public class SerializeDemo {
    String outName = "123.ser";
    byte[] payloadBytes;

    public void serialize() throws Exception {

        Payload payload = new Payload("world");
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream(outName));
        objectOutputStream.writeObject(payload);
    }
    public void serializeByte() throws Exception{
        Payload payload = new Payload("world2");
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);

        objectOutputStream.writeObject(payload);
        payloadBytes = byteArrayOutputStream.toByteArray();

        objectOutputStream.close();
        byteArrayOutputStream.close();
        System.out.println(Arrays.toString(payloadBytes));
    }
    public Object deserialize() throws Exception {
        FileInputStream fileInputStream = new FileInputStream(outName);
        ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream);
        return objectInputStream.readObject();
    }
    public Object deserializeBytes() throws Exception{
        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(payloadBytes);
        ObjectInputStream objectInputStream  = new ObjectInputStream(byteArrayInputStream);
        return objectInputStream.readObject();
    }
    public static void main(String[] args) throws Exception {
        SerializeDemo demo = new SerializeDemo();
//        System.out.println("serialize finished ...");
//        Payload payload = (Payload) demo.deserialize();
//        System.out.println("deserialize finished ...");
//
//        String result = payload.say();
//        System.out.println("result is: " + result);

        demo.serializeByte();
        Payload payload1 = (Payload) demo.deserializeBytes();

        String result1 = payload1.say();
        System.out.println("result is: " + result1);
    }

识别序列化数据

//序列化后的对象,数据一般以 aced开头
AC ED:STREAM_MAGIC,声明使用了序列化协议,从这里可以判断保存的内容是否为序列化数据。
00 05:STREAM_VERSION,序列化协议版本。

或者使用hexdump查看
> xxd 123.ser
00000000: aced 0005 7372 0011 636f 6d2e 7274 7975  ....sr..com.rtyu
00000010: 792e 5061 796c 6f61 64c9 8f6b 4fbb efb7  y.Payload..kO...
00000020: 7d02 0001 4c00 036d 7367 7400 124c 6a61  }...L..msgt..Lja
00000030: 7661 2f6c 616e 672f 5374 7269 6e67 3b78  va/lang/String;x
00000040: 7074 0005 6865 6c6c 6f                   pt..hello

反序列化漏洞

Java的反序列化机制可以导致一个实例能直接从byte[]数组创建,而不经过构造方法,因此,它存在一定的安全隐患。一个精心构造的byte[]数组被反序列化后可以执行特定的Java代码,从而导致严重的安全漏洞。

如下:

   /**
     * 自定义反序列化,实现rce
     * @param objectInputStream
     * @throws IOException
     */
    private void readObject(ObjectInputStream objectInputStream) throws IOException{
        System.out.println("do serialize....");
        Process result = Runtime.getRuntime().exec("ifconfig");

        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(result.getInputStream()));
        do{
            System.out.println(bufferedReader.readLine());
        }while (bufferedReader.readLine() != null);
    }

漏洞挖掘

代码审计

​ java是支持自定义readObject与writeObject方法的,只要某个类中按照特定的要求实现了readObject方法,那么在反序列化的时候就会自动调用它。需要格外关注自定义readObject方法的类调用关系或者实现java.io.Externalizable接口的类调用关系。

web黑盒探测

  1. 通过http请求的Content-Type,具体来说ContentType: application/x-java-serialized-object 是序列化请求的请求头

  2. 检查请求数据的开头是否是 0xaced,有时候序列化请求不存在正确的content-type,此时需要根据数据来判断是否是序列化请求

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值