Serializeable Seializeable是java实现序列化机制的工具,序列化数据包含对象类型和属性值。
Serializeable工具的简单说明
1 2 3 4 只需要实现Serializeable接口就可以进行对象的序列化处理 序列化对象可以是基本数据类型、集合类或其他对象 使用transient 、static 关键字修饰的属性不会被序列化 父类不可序列化时,需要父类中存在无参构造函数。
相关接口和类
1 2 3 4 5 6 java.io .Serializable java.io .Externalizable ObjectOutput ObjectInput ObjectOutputStream ObjectInputStream
序列化
1 2 3 4 5 6 7 8 9 OutputStream outputStream= new FileOutputStream("serial" ); ObjectOutputStream objectOutputStream=new ObjectOutputStream((outputStream)); objectOutputStream.writeObject(Object); objectOutputStream.close(); outputStream.close();
反序列化
1 2 3 4 5 6 7 8 9 InputStream inputStream=new FileInputStream("serial" ); ObjectInputStream objectInputStream=new ObjectInputStream((inputStream)); objectInputStream.readObject(); objectInputStream.close(); inputStream.close();
Serializable接口 一般一个类只要继承了Serializable接口,就代表该类和其子类都能进行JDK的序列化
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 package com.tq;import java.io.*;public class MyJavaSerialize { public static class UInfo implements Serializable { private String userName; private int userAge; private String userAddress; public String getUserName () {return userName;}; public int getUserAge () {return userAge;}; public String getUserAddress () {return userAddress;}; public void setUserName (String userName) {this .userName=userName;}; public void setUserAge (int userAge) {this .userAge=userAge;}; public void setUserAddress (String userAddress) {this .userAddress=userAddress;}; } public static void main (String[] args) throws Exception { UInfo userInfo=new UInfo(); userInfo.setUserAddress("a" ); userInfo.setUserAge(22 ); userInfo.setUserName("admin" ); OutputStream outputStream=new FileOutputStream("serial" ); ObjectOutputStream objectOutputStream=new ObjectOutputStream(outputStream); objectOutputStream.writeObject(userInfo); objectOutputStream.close(); outputStream.close(); InputStream inputStream=new FileInputStream("serial" ); ObjectInputStream objectInputStream=new ObjectInputStream(inputStream); UInfo unserialUInfo=(UInfo) objectInputStream.readObject(); objectInputStream.close(); inputStream.close(); System.out.println(unserialUInfo.userName); System.out.println(unserialUInfo.userAge); System.out.println(unserialUInfo.userAddress); } }
输出结果,工程目录下会生成一个serial文件,其内容就是序列化后的数据
Externalizable接口 除了Serializable接口,java还提供了另一个序列化接口Externalizable,该接口继承自Serializable接口,但是有两个抽象函数:writeExternal和readExternal。需要自行实现两个函数来控制序列化流程,否则目标序列化类属性值会是类初始化后的默认值。
在使用Externalizable接口实现序列化时,读取对象会调用目标序列化类的无参构造函数去创建一个新的对象,再把徐磊话数据中的类属性值分别填充到新对象中。所以实现Externalizable接口的类必须提供一个public属性的无参构造函数
serialVersionUID 目标序列化类有一个隐藏属性
1 private static final long serialVerionUID
Java虚拟机判断是否允许序列化数据被序列化时,会取决于两个类的serialVersionUID是否一致。serialVerionUID在不同编译器内可能有不同的值,开发者可以在目标序列化类中提供固定值。在提高serialVerionUID固定值的情况下,只要序列化数据中国的serialVerionUID和目标序列化类中的一致就可以成功反序列化。如果没有指定值,编译器会根据class文件内容通过一定算法生成值,在不同环境下,编译器得到的serialVerionUID值不同,就会导致反序列化失败。改变目标类中代码也可能会影响生成的serialVersionUID,此时会抛出java.io.InvalidClassException并指出serialVersionUID不一致。建议在目标序列化类中显示定义serialVersionUID并赋予明确的值。
显示定义serialVersionUID有两种用途:
1.在某些场合,希望类的不同版本兼容序列化,所以需要确保类的不同版本有相同的serialVersionUID
2.某些时候不希望类的不同版本对序列化有兼容,所以需要类的不同版本有不同的serialVerionUID
反序列化漏洞 java有多种序列化和反序列化工具
1 2 3 JDK自带的Serializable fastjson和jackson是JSON 的知名反序列化工具 xmldecoder和xstream是XML 的知名反序列化工具
java拥有完善的第三方类库和满足各种需求的框架,但因为很多第三方类库引用广泛,如果其中某些组件出现安全问题,那么受影响范围将极为广泛。
漏洞入口 ObjectInputStream对象的readObject函数调用是java反序列化流程的入口,序列化数据的来源包括:Cookie、GET参数、POST参数或者流、HTTP Head或者来自用户可控内容的数据库等。
触发场景:
1 2 3 4 1.HTTP 请求中的参数 2.RMI ,即Java 远程方法调用,在RMI 中传输的数据皆为序列化 3.JMX ,一个为应用程序植入管理功能的框架 4.自定义协议 用来接收与发送原始的java 对象
反序列化相关函数
1 2 3 4 5 6 7 ObjectInputStream . readObjectObjectInputStream . readUnsharedXMLDecoder . readObjectYaml . loadXStream . fromXMLObjectMapper . readValueJSON . parseObject
数据特征 序列化的数据头是不变的,传输过程中可能会对字节流进行编码,解码后查看字节流开头,反序列化数据开头包含两字节的魔术数字,后面是两字节的版本号,如ac ed 00 05
,而经过Base64编码过序列化数据的字节流头部为Ro0AB
,在攻击检测时可针对该特征进行匹配请求post中是否包含反序列化数据,判断是否为反序列化漏洞攻击。
利用形式 JDK原生反序列化工具Serializable大致有两种利用形式
1 2 3 4 5 1 .完整对象前的利用 在JDK对恶意序列化数据进行反序列化的过程中达成攻击效果,这种利用方式大多基于对java开发中频繁调用函数的理解,寻找漏洞触发点。例如commons-collections3.1反序列化漏洞利用中的rce gadget属于以readObject函数调用点为入口,直接在依赖包中寻找到rce的利用方式2 .生成完整对象的利用 如身份令牌反序列化,要等待对象反序列化完成后,利用其中的函数或者属性值完成攻击