Java反序列化-01
前言
在这篇文章之前,我已经学习了有关fastjson的Java反序列化,但是在最近的比赛中考察到了Java原生反序列化的知识点,初步了解之后发现两者相距甚远,所以决定写下这篇这篇文章,记录一下Java反序列化的学习。虽然Java原生的反序列化已经十分少见,但是毕竟是网络安全,可以不用,不能不会,所以还是深入研究一下。
序列化与反序列化的代码实现
有关于序列化和反序列化的知识点这里不做赘述,个人建议从PHP开始了解序列化与反序列化,因为PHP的序列化更加简单些,这里贴出之前所写的文章链接,供师傅们学习:
序列化与反序列化基础及反序列化漏洞(附案例)_反序列化漏洞代码-CSDN博客
还是先创建一个Java项目,创建一个类文件,键入如下代码。
Person.java
1 | package org.example; |
再创建一个类文件,键入如下代码,该代码为序列化代码:
SerializationTest.java
1 | package org.example; |
最后创建一个反序列化文件:
UnserializeTest.java
1 | package org.example; |
运行效果
我们都知道,序列化的目的在于数据的传输。
在序列化的代码中,我们将序列化功能封装进入serialize
方法中,我们通过FileOutputStream
输出流,将序列化对象输出到ser.bin
文件中,在通过oss
的writeObject
方法,对对象进行序列化操作。
在反序列化的代码中,我们将反序列化功能封装进入unserialize
方法中,我们通过FileInputStream
输入流,将序列化后的对象中ser.bin
,文件中读取出来,再通过obj
的readObject
方法,对对象进行反序列化操作。
对于Java中的一些反序列化的特性,这里不做赘述,需要的师傅可以前往第一篇参考文献中了解。
序列化与反序列化的安全问题
原因
在Java的序列化和反序列化中,有两个重要的方法,readObject
和writeObject
。这两个方法可以由开发者重写,一般来说,重写这两个方法存在于下面这种场景:
在一个类中,存在一个数组属性:array,初始化的数组长度为100。在实际的序列化过程中,假设让array参加序列化过程,那么长度为100的数组都会被序列化,而实际使用的可能不足30个,这显然是不合理的,所以这里就需要自定义序列化和反序列化的过程。具体做法就是重写
readObject
和writeObject
方法。
综上所述,当服务端反序列化数据时,对应类中的readObject方法就会自动执行。所以反序列化产生危害的根本在于readObejct
方法
漏洞形式
一个简单的示例
这种形式在实际生产中其实并不常见,我们写一段弹出计算器的代码。
1 | private void readObject(ObjectInputStream in) throws java.io.IOException, ClassNotFoundException{ |
序列化一下,再反序列化一下,就可以直接弹出计算器了。这是最理想的情况,实战中很少会出现这种情况
URLDNS Gadget
URLDNS 是 Java 反序列化漏洞中最经典、最简单的利用链之一,通常用于检测目标是否存在反序列化漏洞(因为它不会执行恶意代码,而是触发一次 DNS 请求)。它主要依赖 HashMap
和 URL
类的特性,结合 hashCode()
方法在反序列化时的自动调用机制。
URLDNS Gadget 的核心原理
利用链组成
1 | HashMap.readObject() |
关键点
HashMap
反序列化时会计算hashCode
当HashMap
被反序列化时,会调用readObject()
,进而调用hash()
方法计算每个键(Key)的哈希值,触发key.hashCode()
。URL
类的hashCode()
会触发 DNS 查询URL.hashCode()
默认调用URLStreamHandler.hashCode()
,而该方法会调用URL.getHostAddress()
,最终执行InetAddress.getByName(host)
,向目标域名发起 DNS 请求。
解析
根据描述,我们有如下示例代码:
1 | package org.example; |
我们跟进HashMap
这个类,找到其中的readObject
这个方法。
我们发现这里调用了hash
这个方法,跟进hash
。
又发现这里调用了传入的对象key
的hashCode
的方法。根据上面的跟踪,我们知道当进行反序列化时,readObject
这个方法会调用HashMap
中的所有对象的hashCode
方法。接下来我们回到一开始,去跟踪URL
这个类中的hashCode
方法。
跟进我们发现,这里调用了handler
这个对象的中的hashCode
方法,hanlder
又属于URLStreamHandler
这个类。继续跟进URLStreamHandler
这个类的hashCode
方法。
我们关注其中的getHostAddress
方法。
最后到这里,触发DNS请求。