0x00前言
提起反序列化不是什么新鲜的名词了,无论是php还是java,最终的目的都是执行恶意代码达到RCE或权限控制的目的,总结下JAVA的反序列化的一些知识点,巩固一下。
0X01序列化与反序列化
序列化:将对象转换为字节流并存储到指定文件
反序列化:读取文件并将字节流还原为对象
ObjectOutputStream类 —-> writeObject() 序列化(OOS)
ObjectInputStream类 —-> readObject() 反序列化(OIS)

序列化实现:

反序列化实现:

0x02序列化实现
java类需要实现serialize接口或者实现Externalizable,否则执行的时候会抛出异常
1 | public interface Serializable { |
子类继承父类时父类需要实现上述接口,若无实现则需要子类提供一个无参的构造函数并创建对象
记住:序列化是序列化对象而不是序列化类
并且静态变量不能被序列化,因为静态变量优先于对象存在,随着类的加载而加载,所以不会被序列化
条件
共同条件 继承Serializable
入口类 source(重写readObject 参数类型宽泛 最好jdk自带)
调用链gadget chain
执行类sink (rce ssrf写文件等等)
0x03反序列化的利用
在java反序列化中,会调用执行readObject()方法中的代码,攻击者可以构造恶意代码序列化后发送给服务端,服务端进行反序列化并执行readlObject()方法
利用点
1、入口类的readObject直接调用危险方法
2、入口类参数中包含可控类,该类有危险方法readObject时调用
3、入口类参数中包含可控类,该类有调用其他有危险方法的类readObject时调用
4、构造函数/静态代码块等类加载时隐式执行
验证是否存在反序列化可以使用常见的反序列化链
URLDNS链:HashMap.readObject() -> HashMap.putVal()->HashMap.hash()->URL.hashcode()->URLStreamHandler().hashCode().getHostAddress->URLStreamHandler().hashCode().getHostAddress.getByName()
0x04序列化数据的特征
一段数据以rO0AB开头,基本可以确定这串就是JAVA序列化base64加密的数据
如果以ac ed开头,那么它就是这一段JAVA序列化的16进制
0x05JAVA反射
实现JAVA反序列化时不能忽略的一个点就是JAVA内置的的反射机制,它的作用就是让JAVA具有动态性
作用
1、修饰已有动态属性
2、动态生成对象
3、动态调用方法
4、操作内部类和私有方法
操作反射的5中类
- java.lang.Class: 代表类
- java.lang.reflect.Constructor: 代表类的构造方法
- java.lang.reflect.Field: 代表类的属性
- java.lang.reflect.Method: 代表类的方法
- java.lang.reflect.Modifier:代表类、方法、属性的描述修饰符。
AccessObject类中有一个setAccessible()方法,作用是使得非public属性或方法可见,而Constructor、Field、Method都继承这个类
Constructor
| 方法 | 含义 |
|---|---|
| getConstructors() | 获得类的所有public构造方法 |
| getDeclaredConstructors() | 获得类的所有构造方法 |
| getConstructor(Class[] parameterTypes) | 获得类的特定public构造方法 |
| getDeclaredConstructor(Class[] params) | 获取类的特定构造方法 |
Field
| 方法 | 含义 |
|---|---|
| getFields() | 获得类的所有public属性 |
| getDeclaredFields() | 获得类的所有属性 |
| getField(String name) | 获得类的特定public属性 |
| getDeclaredField(String name) | 获取类的特定属性 |
Method
| 方法 | 含义 |
|---|---|
| getMethods() | 获得类的所有public成员方法 |
| getDeclaredMethods() | 获得类的所有成员方法 |
| getMethod(String name, Class[] parameterTypes) | 获得类的特定public成员方法 |
| getDeclaredMethod(String name, Class[] parameterTypes) | 获取类的特定成员方法 |
example
该学生类属性

获取对象的类

1 | Class clazz = Class.forName("src.Student");//也可使用forName方法 |
获取对象构造方法

其中传入的参数是获取student类中带有int类型和String类型参数的构造方法
获取对象的属性

获取对象的方法

当在漏洞利用时通常需要修改对象属性达到攻击目的,贴一段代码
1 | public static boolean setField(Object object, String fieldName, Object fieldValue) { |
0x06工具
https://github.com/frohoff/ysoserial
yeoserial库
在渗透测试或者做CTF题目的时候需要注意看源代码的依赖名称和依赖版本从而选择正确的反序列化链
0x07总结
1、实现序列化时需要实现serialize或Externalizable接口否则报错
2、子类继承父类若无实现接口则需要提供空白参数的构造方法
3、静态常量和类的属性不能被序列化,序列化的只能是对象,静态则优先于对象,随着类加载而加载
4、JAVA反序列化往往配合JAVA反射进行构造恶意代码的目的,一般不能遗漏setAccessible
5、Ysoserial工具使用的时候需根据源代码的依赖名称和版本选择正确的链子