Translated ['src/pentesting-web/deserialization/basic-java-deserializati

This commit is contained in:
Translator 2025-07-28 18:13:06 +00:00
parent 2daddbb8e5
commit e7b7c0a463

View File

@ -1,15 +1,27 @@
# Basic Java Deserialization with ObjectInputStream readObject
{{#include ../../banners/hacktricks-training.md}}
在这篇文章中,将解释一个使用 `java.io.Serializable` 的示例。
在这篇文章中,将解释一个使用 `java.io.Serializable` 的示例**以及为什么重写 `readObject()` 在输入流由攻击者控制时可能极其危险**
# Serializable
## Serializable
Java `Serializable` 接口`java.io.Serializable` 是一个标记接口,您的类必须实现它才能被 **序列化****反序列化**。Java 对象序列化(写入)是通过 [ObjectOutputStream](http://tutorials.jenkov.com/java-io/objectoutputstream.html) 完成的,反序列化(读取)是通过 [ObjectInputStream](http://tutorials.jenkov.com/java-io/objectinputstream.html) 完成的。
Java `Serializable` 接口 (`java.io.Serializable`) 是一个标记接口,您的类必须实现它才能被 **序列化****反序列化**。Java 对象序列化(写入)是通过 [`ObjectOutputStream`](http://tutorials.jenkov.com/java-io/objectoutputstream.html) 完成的,反序列化(读取)是通过 [`ObjectInputStream`](http://tutorials.jenkov.com/java-io/objectinputstream.html) 完成的。
让我们看一个 **可序列化的类 Person** 的示例。这个类 **重写了 readObject** 函数,因此当 **这个类的任何对象****反序列化** 时,这个 **函数** 将被 **执行**。\
在这个示例中,类 Person 的 **readObject 函数** 调用了它的宠物的 `eat()` 函数,而 Dog 的 `eat()` 函数(出于某种原因)调用了 **calc.exe**。 **我们将看到如何序列化和反序列化一个 Person 对象以执行这个计算器:**
### 提醒:在反序列化过程中哪些方法会被隐式调用?
**以下示例来自 [https://medium.com/@knownsec404team/java-deserialization-tool-gadgetinspector-first-glimpse-74e99e493649](https://medium.com/@knownsec404team/java-deserialization-tool-gadgetinspector-first-glimpse-74e99e493649)**
1. `readObject()` 类特定的读取逻辑(如果实现且为 *private*)。
2. `readResolve()` 可以用另一个对象替换反序列化的对象。
3. `validateObject()` 通过 `ObjectInputValidation` 回调。
4. `readExternal()` 对于实现 `Externalizable` 的类。
5. 构造函数 **不会** 被执行 因此小工具链完全依赖于之前的回调。
在该链中的任何方法如果最终调用了攻击者控制的数据命令执行、JNDI 查找、反射等),将使反序列化过程变成 RCE 小工具。
让我们看一个 **Person 类** 的示例,该类是 **可序列化的**。这个类 **重写了 readObject** 函数,因此当 **该类的任何对象****反序列化** 时,这个 **函数** 将被 **执行**。\
在这个示例中Person 类的 **readObject** 函数调用了它的宠物的 `eat()` 函数,而 Dog 的 `eat()` 函数(出于某种原因)调用了 **calc.exe**。 **我们将看到如何序列化和反序列化一个 Person 对象以执行这个计算器:**
**以下示例来自 <https://medium.com/@knownsec404team/java-deserialization-tool-gadgetinspector-first-glimpse-74e99e493649>**
```java
import java.io.Serializable;
import java.io.*;
@ -80,8 +92,63 @@ payloadTest("test.ser");
}
}
```
## 结论
### 结论(经典场景)
正如您在这个非常基本的示例中所看到的,这里的“漏洞”出现是因为 **readObject** 函数 **调用了其他易受攻击的函数**
正如您在这个非常基本的例子中所看到的,这里的“漏洞”出现是因为 **readObject()** 方法 **调用了其他攻击者控制的代码**。在现实世界的 gadget 链中成千上万的类包含在外部库中Commons-Collections、Spring、Groovy、Rome、SnakeYAML 等)可以被滥用——攻击者只需要 *一个* 可达的 gadget 就能获得代码执行。
---
## 2023-2025Java 反序列化攻击的新动态
* 2023 CVE-2023-34040`checkDeserExWhen*` 标志启用时Spring-Kafka 反序列化错误记录头允许从攻击者发布的主题构造任意 gadget。已在 3.0.10 / 2.9.11 中修复。¹
* 2023 CVE-2023-36480Aerospike Java 客户端的受信任服务器假设被破坏——恶意服务器回复包含被客户端 反序列化 的序列化有效负载 → RCE。²
* 2023 CVE-2023-25581`pac4j-core` 用户配置文件属性解析接受 `{#sb64}` 前缀的 Base64 blob并在 `RestrictedObjectInputStream` 的情况下反序列化它们。升级 ≥ 4.0.0。
* 2023 CVE-2023-4528JSCAPE MFT Manager Service端口 10880接受 XML 编码的 Java 对象,导致以 root/SYSTEM 身份的 RCE。
* 2024 多个新的 gadget 链被添加到 ysoserial-plus(mod),包括 Hibernate5、TomcatEmbed 和 SnakeYAML 2.x 类,绕过了一些旧的过滤器。
## 现代缓解措施
1. **JEP 290 / 序列化过滤Java 9+**
*添加类的允许列表或拒绝列表:*
```bash
# 仅接受您的 DTO 和 java.base拒绝其他所有内容
-Djdk.serialFilter="com.example.dto.*;java.base/*;!*"
```
编程示例:
```java
var filter = ObjectInputFilter.Config.createFilter("com.example.dto.*;java.base/*;!*" );
ObjectInputFilter.Config.setSerialFilter(filter);
```
2. **JEP 415Java 17+)上下文特定过滤器工厂** 使用 `BinaryOperator<ObjectInputFilter>` 根据执行上下文(例如,每个 RMI 调用、每个消息队列消费者)应用不同的过滤器。
3. **不要通过网络暴露原始 `ObjectInputStream`** 更倾向于没有代码执行语义的 JSON/二进制编码Jackson 在禁用 `DefaultTyping`Protobuf、Avro 等)。
4. **深度防御限制** 设置最大数组长度、深度、引用:
```bash
-Djdk.serialFilter="maxbytes=16384;maxdepth=5;maxrefs=1000"
```
5. **持续的 gadget 扫描** 在您的 CI 中运行工具,如 `gadget-inspector``serialpwn-cli`,如果危险的 gadget 变得可达则失败构建。
## 更新的工具备忘单2024
* `ysoserial-plus.jar` 社区分支,包含 > 130 个 gadget 链:
```bash
java -jar ysoserial-plus.jar CommonsCollections6 'calc' | base64 -w0
```
* `marshalsec` 仍然是 JNDI gadget 生成的参考LDAP/RMI
* `gadget-probe` 针对网络服务的快速黑盒 gadget 发现。
* `SerialSniffer` JVMTI 代理,打印每个被 `ObjectInputStream` 读取的类(有助于制作过滤器)。
* **检测提示** 启用 `-Djdk.serialDebug=true`JDK 22+)以记录过滤器决策和被拒绝的类。
## 安全 `readObject()` 实现的快速检查清单
1. 将方法设为 `private` 并添加 `@Serial` 注解(有助于静态分析)。
2. 切勿在方法中调用用户提供的方法或执行 I/O 仅读取字段。
3. 如果需要验证,请在反序列化 **后** 执行,位于 `readObject()` 之外。
4. 更倾向于实现 `Externalizable` 并显式读取字段,而不是默认序列化。
5. 即使对于内部服务,也要注册一个强化的 `ObjectInputFilter`(抗妥协设计)。
## 参考文献
1. Spring Security Advisory CVE-2023-34040 Java 反序列化在 Spring-Kafka 中2023年8月
2. GitHub Security Lab GHSL-2023-044Aerospike Java 客户端中的不安全反序列化2023年7月
{{#include ../../banners/hacktricks-training.md}}