反序列化除了readObject 还有什么触发点
1. readResolve()
和 writeReplace()
这两个方法主要用于控制对象的序列化和反序列化过程,它们可以用来触发攻击链
- 原理:
writeReplace()
:这个方法在对象被序列化时调用。它允许开发者用另一个对象来替换即将被序列化的对象。攻击者可以利用这个方法,让一个无害的对象在序列化时被替换成一个恶意的对象readResolve()
:这个方法在对象被反序列化后调用。它允许开发者用另一个对象来替换刚刚反序列化得到的对象。攻击者可以利用这个方法,在反序列化时触发攻击链,例如调用一个可以触发 JNDI 注入的类
- 攻击链举例:
- 攻击者构造一个恶意的对象,这个对象的
readResolve()
方法被重写,用于返回一个可以触发 JNDI 注入的JdbcRowSetImpl
对象 - 当应用程序反序列化这个对象时,
readResolve()
方法会被自动调用,从而将攻击链的控制权转移到JdbcRowSetImpl
上,最终导致 RCE
- 攻击者构造一个恶意的对象,这个对象的
2. finalize()
finalize()
方法是一个“魔法”方法,它在对象被垃圾回收时调用
- 原理:在某些情况下,当一个对象被反序列化后,如果它不再被引用,Java 虚拟机(JVM)可能会将其放入垃圾回收队列。在垃圾回收前,JVM 会调用对象的
finalize()
方法 - 攻击链举例:
- 攻击者构造一个恶意的对象,这个对象的
finalize()
方法被重写,用于执行系统命令 - 当应用程序反序列化并丢弃这个对象时,如果垃圾回收被触发,
finalize()
方法就会被调用,从而执行恶意代码
- 攻击者构造一个恶意的对象,这个对象的
- 局限性:这种方法不常见,因为它不可预测。你无法控制垃圾回收何时发生,甚至无法保证它一定会发生。因此,它不是一个可靠的漏洞利用方式,但其原理是成立的
3. toString()
在某些情况下,某些类在反序列化时会调用其内部对象的 toString()
方法
- 原理:当一个对象被反序列化时,如果它被放入到一个需要调用
toString()
的上下文中(例如,在日志记录中),那么toString()
方法就会被自动调用 - 攻击链举例:
- 攻击者找到一个类,它的
toString()
方法可以间接触发命令执行 - 攻击者构造一个恶意的序列化对象,这个对象包含上面找到的类
- 当应用程序反序列化这个对象,并将其放入一个需要调用
toString()
的上下文中时,就会触发攻击链 - 典型的例子是
BadAttributeValueExpException
这个类,它的readObject()
方法会调用内部对象的toString()
,从而可以触发InvokerTransformer
的攻击链
- 攻击者找到一个类,它的
4. hashCode()
和 equals()
这两个方法通常用于哈希表(HashMap
、HashSet
)等集合类中
- 原理:当一个哈希表被反序列化时,它需要重新构建内部的数据结构。在这个过程中,它会调用其存储的对象的
hashCode()
和equals()
方法 - 攻击链举例:
- 攻击者构造一个恶意的哈希表,并向其中放入一个可以被利用的对象
- 当这个哈希表被反序列化时,它的
hashCode()
方法会被调用 - 攻击者可以利用一些特殊的类(例如
HashSet
),让其在hashCode()
方法中调用其他恶意对象的transform()
方法,从而触发攻击链