Java序列化与反序列化是让 Java 对象脱离 Java 运行环境的一种手段,可以有效的实现多平台之间的通信、对象持久化存储。
什么是序列化和反序列化
简单来说
序列化
就是把对象转换为字节序列(二进制),然后储存在内存中
对象 ——> 数据
序列化依赖 ObjectOutputStream 类的 writeObject() 方法
反序列化
就是把字节序列从内存中的提取出来,然后反序列化为对象
数据 ——> 对象
反序列化依赖 ObjectInputStream 类的 readObject() 方法
一个类的对象要想序列化成功,必须满足两个条件:
该类必须实现 java.io.Serializable 接口。
该类的所有属性都是可序列化的。
漏洞成因
暴露或间接暴露反序列化 API ,导致用户可以传入精心构造的反序列化对象并执行恶意代码。
漏洞原理
第一种 重写readObject
Java序列化策略支持默认和用户自定义两种
自定义序列化规则的方式就是重写 writeObject
与readObject
重写后,java就会调用用户自定义的规则
当readObject
方法存在执行命令的机会,通过传入恶意代码,程序反序列化时 ObejctInputStream类的readObject方法就会触发漏洞
第二种 利用反射机制
Apache-CommonCollections REC
Apache-CommonsCollections包中,有一个 InvokerTransformer
类实现了Transformer
接口,InvokerTransformer
的 transform
方法可以通过Java反射机制,调用任意Java方法。
AnnotationInvocationHandler 对象的 readObject() 方法在反序列化时会触发 setValue() 函数,进而调用 checkSetValue 函数,然后进一步触发第一步构造的 ChainedTransformer.transform 方法——
ChainedTransformer 的 transform 方法会依次调用数组中 InvokerTransformer 的transform 函数,从而实现了( Java类加载函数调用链 )反射调用 Runtime.getRuntime().exec() ,触发命令的执行。
高于8u66的版本不支持AnnotationInvocationHandler,可以用BadAttributeValueExpException,9u4以下有效果,利用原理相同但方法不同,具体请查阅资料