当数据在网络中传输时,我们需要把我们代码中的数据固定成某种特定的数据格式去传输,目前使用最为广泛的 两种格式是: json和xml. 但是这两种格式默认只支持了基本的数据类型,如数字,字符串等。

当我们想序列一些自定义对象时,它们就无能为力了。 因此各种语言,各个框架都自定义了自己的一套规则自定义对象。

基本使用

还是直接先看代码吧

我们先定义一个类

1
2
3
class Person implements Serializable{
    public String name;
}

可以看到这个Person类实现了java.io.Serializable接口,这也是一个类序列化的必要条件。

序列化

先实例化Person类

1
2
Person p = new Person();
p.name = "wwww";

将person类序列化并保存

1
2
3
4
5
6
String fileName = "./person.ser";
FileOutputStream f = new FileOutputStream(fileName);
ObjectOutputStream out = new ObjectOutputStream(f);
out.writeObject(p);
out.close();
f.close();

这里用到ObjectOutputStreamwriteObject方法将其序列化

用winhex打开person.ser

img.png

可以看到开头两位是AC ED, 这也是java反序列化的魔术字符,有时我们在ctf看到这时,就知道这跟java反序列化逃不了干系了。

反序列化

有出就有进。 反序列化用的是ObjectInputStreamreadObject方法

1
2
3
4
FileInputStream f1 = new FileInputStream(fileName);
ObjectInputStream in = new ObjectInputStream(f1);
Person p1 = (Person) in.readObject();
System.out.println(p1.name);

运行后打印出了我们在上面设置的name: wwww

writeObject和readObject

有时候,我们会想在序列化和反序列化时做一些事情,例如在序列化时存个链接,在反序列时再把这请求这链接拿到结果等等

java Serializable接口贴心的为我们准备了两个方法实现这个功能:

  • writeObject 序列化时自动调用
  • readObject 反序列化时自动调用 直接看示例
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
class Person implements Serializable{
    public String name;

    private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
        in.defaultReadObject();
        String password = (String) in.readObject();
        System.out.println(password);
    }

    private void writeObject(ObjectOutputStream out) throws IOException {
        out.defaultWriteObject();
        out.writeObject("this is password");
    }
}

那么漏洞呢?

漏洞

上面说了java会在反序列化时自动调用类的readObject方法,那么如果开发人员在readObject上写上了恶意代码, 如:

1
2
3
4
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
    in.defaultReadObject();
    Runtime.getruntime.exec("calc.exe")
}

那么在反序列化时就会自动执行命令了。

什么? 不会有这样的开发人员?

确实不大可能会有这么缺心眼的开发者。 因此我们往往需要通过将java内自带的库或者比较通用的库,将它们的readObject串联起来, 从而达成我们想要的目的。

更详细的ObjectInputStream.readObject之后发生了什么? 可以看这篇文章:http://www.lmxspace.com/2019/11/20/Java%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E8%BF%87%E7%A8%8B%E6%B7%B1%E7%A9%B6/

下一篇文章会用一个简短的调用链来了解什么是"gadget".