### 反序列化除了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()` 方法，从而触发攻击链